##// END OF EJS Templates
typing: added typehint
super-admin -
r5141:d2b5dad9 default
parent child Browse files
Show More
@@ -1,242 +1,241 b''
1 # Copyright (C) 2012-2023 RhodeCode GmbH
1 # Copyright (C) 2012-2023 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 import re
18 import re
19 import textwrap
19 import textwrap
20 import dataclasses
20 import dataclasses
21 import logging
21 import logging
22 import typing
23
22
24 from mako.template import Template
23 from mako.template import Template
25
24
26 from rhodecode import events
25 from rhodecode import events
27 from rhodecode.integrations.types.base import CommitParsingDataHandler, render_with_traceback
26 from rhodecode.integrations.types.base import CommitParsingDataHandler, render_with_traceback
28
27
29 log = logging.getLogger(__name__)
28 log = logging.getLogger(__name__)
30
29
31
30
32 @dataclasses.dataclass
31 @dataclasses.dataclass
33 class SlackData:
32 class SlackData:
34 title: str
33 title: str
35 text: str
34 text: str
36 fields: list[dict] | None = None
35 fields: list[dict] | None = None
37 overrides: dict | None = None
36 overrides: dict | None = None
38
37
39
38
40 def html_to_slack_links(message):
39 def html_to_slack_links(message):
41 return re.compile(r'<a .*?href=["\'](.+?)".*?>(.+?)</a>').sub(r'<\1|\2>', message)
40 return re.compile(r'<a .*?href=["\'](.+?)".*?>(.+?)</a>').sub(r'<\1|\2>', message)
42
41
43
42
44 REPO_PUSH_TEMPLATE = Template('''
43 REPO_PUSH_TEMPLATE = Template('''
45 <%
44 <%
46 def branch_text(branch):
45 def branch_text(branch):
47 if branch:
46 if branch:
48 return 'on branch: <{}|{}>'.format(branch_commits['branch']['url'], branch_commits['branch']['name'])
47 return 'on branch: <{}|{}>'.format(branch_commits['branch']['url'], branch_commits['branch']['name'])
49 else:
48 else:
50 ## case for SVN no branch push...
49 ## case for SVN no branch push...
51 return 'to trunk'
50 return 'to trunk'
52 %> \
51 %> \
53
52
54 % for branch, branch_commits in branches_commits.items():
53 % for branch, branch_commits in branches_commits.items():
55 ${len(branch_commits['commits'])} ${'commit' if len(branch_commits['commits']) == 1 else 'commits'} ${branch_text(branch)}
54 ${len(branch_commits['commits'])} ${'commit' if len(branch_commits['commits']) == 1 else 'commits'} ${branch_text(branch)}
56 % for commit in branch_commits['commits']:
55 % for commit in branch_commits['commits']:
57 `<${commit['url']}|${commit['short_id']}>` - ${commit['message_html']|html_to_slack_links}
56 `<${commit['url']}|${commit['short_id']}>` - ${commit['message_html']|html_to_slack_links}
58 % endfor
57 % endfor
59 % endfor
58 % endfor
60 ''')
59 ''')
61
60
62
61
63 class SlackDataHandler(CommitParsingDataHandler):
62 class SlackDataHandler(CommitParsingDataHandler):
64 name = 'slack'
63 name = 'slack'
65
64
66 def __init__(self):
65 def __init__(self):
67 pass
66 pass
68
67
69 def __call__(self, event: events.RhodecodeEvent, data):
68 def __call__(self, event: events.RhodecodeEvent, data) -> SlackData:
70 if not isinstance(event, events.RhodecodeEvent):
69 if not isinstance(event, events.RhodecodeEvent):
71 raise TypeError(f"event {event} is not subtype of events.RhodecodeEvent")
70 raise TypeError(f"event {event} is not subtype of events.RhodecodeEvent")
72
71
73 actor = data["actor"]["username"]
72 actor = data["actor"]["username"]
74 default_title = f'*{actor}* caused a *{event.name}* event'
73 default_title = f'*{actor}* caused a *{event.name}* event'
75 default_text = f'*{actor}* caused a *{event.name}* event'
74 default_text = f'*{actor}* caused a *{event.name}* event'
76
75
77 default_slack_data = SlackData(title=default_title, text=default_text)
76 default_slack_data = SlackData(title=default_title, text=default_text)
78
77
79 if isinstance(event, events.PullRequestCommentEvent):
78 if isinstance(event, events.PullRequestCommentEvent):
80 return self.format_pull_request_comment_event(
79 return self.format_pull_request_comment_event(
81 event, data, default_slack_data
80 event, data, default_slack_data
82 )
81 )
83 elif isinstance(event, events.PullRequestCommentEditEvent):
82 elif isinstance(event, events.PullRequestCommentEditEvent):
84 return self.format_pull_request_comment_event(
83 return self.format_pull_request_comment_event(
85 event, data, default_slack_data
84 event, data, default_slack_data
86 )
85 )
87 elif isinstance(event, events.PullRequestReviewEvent):
86 elif isinstance(event, events.PullRequestReviewEvent):
88 return self.format_pull_request_review_event(event, data, default_slack_data)
87 return self.format_pull_request_review_event(event, data, default_slack_data)
89 elif isinstance(event, events.PullRequestEvent):
88 elif isinstance(event, events.PullRequestEvent):
90 return self.format_pull_request_event(event, data, default_slack_data)
89 return self.format_pull_request_event(event, data, default_slack_data)
91 elif isinstance(event, events.RepoPushEvent):
90 elif isinstance(event, events.RepoPushEvent):
92 return self.format_repo_push_event(event, data, default_slack_data)
91 return self.format_repo_push_event(event, data, default_slack_data)
93 elif isinstance(event, events.RepoCreateEvent):
92 elif isinstance(event, events.RepoCreateEvent):
94 return self.format_repo_create_event(event, data, default_slack_data)
93 return self.format_repo_create_event(event, data, default_slack_data)
95 else:
94 else:
96 raise ValueError(
95 raise ValueError(
97 f'event type `{event.__class__}` has no handler defined')
96 f'event type `{event.__class__}` has no handler defined')
98
97
99 def format_pull_request_comment_event(self, event, data, slack_data):
98 def format_pull_request_comment_event(self, event, data, slack_data):
100 comment_text = data['comment']['text']
99 comment_text = data['comment']['text']
101 if len(comment_text) > 200:
100 if len(comment_text) > 200:
102 comment_text = '<{comment_url}|{comment_text}...>'.format(
101 comment_text = '<{comment_url}|{comment_text}...>'.format(
103 comment_text=comment_text[:200],
102 comment_text=comment_text[:200],
104 comment_url=data['comment']['url'],
103 comment_url=data['comment']['url'],
105 )
104 )
106
105
107 fields = None
106 fields = None
108 overrides = None
107 overrides = None
109 status_text = None
108 status_text = None
110
109
111 if data['comment']['status']:
110 if data['comment']['status']:
112 status_color = {
111 status_color = {
113 'approved': '#0ac878',
112 'approved': '#0ac878',
114 'rejected': '#e85e4d'}.get(data['comment']['status'])
113 'rejected': '#e85e4d'}.get(data['comment']['status'])
115
114
116 if status_color:
115 if status_color:
117 overrides = {"color": status_color}
116 overrides = {"color": status_color}
118
117
119 status_text = data['comment']['status']
118 status_text = data['comment']['status']
120
119
121 if data['comment']['file']:
120 if data['comment']['file']:
122 fields = [
121 fields = [
123 {
122 {
124 "title": "file",
123 "title": "file",
125 "value": data['comment']['file']
124 "value": data['comment']['file']
126 },
125 },
127 {
126 {
128 "title": "line",
127 "title": "line",
129 "value": data['comment']['line']
128 "value": data['comment']['line']
130 }
129 }
131 ]
130 ]
132
131
133 template = Template(textwrap.dedent(r'''
132 template = Template(textwrap.dedent(r'''
134 *${data['actor']['username']}* left ${data['comment']['type']} on pull request <${data['pullrequest']['url']}|#${data['pullrequest']['pull_request_id']}>:
133 *${data['actor']['username']}* left ${data['comment']['type']} on pull request <${data['pullrequest']['url']}|#${data['pullrequest']['pull_request_id']}>:
135 '''))
134 '''))
136 title = render_with_traceback(
135 title = render_with_traceback(
137 template, data=data, comment=event.comment)
136 template, data=data, comment=event.comment)
138
137
139 template = Template(textwrap.dedent(r'''
138 template = Template(textwrap.dedent(r'''
140 *pull request title*: ${pr_title}
139 *pull request title*: ${pr_title}
141 % if status_text:
140 % if status_text:
142 *submitted status*: `${status_text}`
141 *submitted status*: `${status_text}`
143 % endif
142 % endif
144 >>> ${comment_text}
143 >>> ${comment_text}
145 '''))
144 '''))
146 text = render_with_traceback(
145 text = render_with_traceback(
147 template,
146 template,
148 comment_text=comment_text,
147 comment_text=comment_text,
149 pr_title=data['pullrequest']['title'],
148 pr_title=data['pullrequest']['title'],
150 status_text=status_text)
149 status_text=status_text)
151
150
152 slack_data.title = title
151 slack_data.title = title
153 slack_data.text = text
152 slack_data.text = text
154 slack_data.fields = fields
153 slack_data.fields = fields
155 slack_data.overrides = overrides
154 slack_data.overrides = overrides
156
155
157 return slack_data
156 return slack_data
158
157
159 def format_pull_request_review_event(self, event, data, slack_data) -> SlackData:
158 def format_pull_request_review_event(self, event, data, slack_data) -> SlackData:
160 template = Template(textwrap.dedent(r'''
159 template = Template(textwrap.dedent(r'''
161 *${data['actor']['username']}* changed status of pull request <${data['pullrequest']['url']}|#${data['pullrequest']['pull_request_id']} to `${data['pullrequest']['status']}`>:
160 *${data['actor']['username']}* changed status of pull request <${data['pullrequest']['url']}|#${data['pullrequest']['pull_request_id']} to `${data['pullrequest']['status']}`>:
162 '''))
161 '''))
163 title = render_with_traceback(template, data=data)
162 title = render_with_traceback(template, data=data)
164
163
165 template = Template(textwrap.dedent(r'''
164 template = Template(textwrap.dedent(r'''
166 *pull request title*: ${pr_title}
165 *pull request title*: ${pr_title}
167 '''))
166 '''))
168 text = render_with_traceback(
167 text = render_with_traceback(
169 template,
168 template,
170 pr_title=data['pullrequest']['title'])
169 pr_title=data['pullrequest']['title'])
171
170
172 slack_data.title = title
171 slack_data.title = title
173 slack_data.text = text
172 slack_data.text = text
174
173
175 return slack_data
174 return slack_data
176
175
177 def format_pull_request_event(self, event, data, slack_data) -> SlackData:
176 def format_pull_request_event(self, event, data, slack_data) -> SlackData:
178 action = {
177 action = {
179 events.PullRequestCloseEvent: 'closed',
178 events.PullRequestCloseEvent: 'closed',
180 events.PullRequestMergeEvent: 'merged',
179 events.PullRequestMergeEvent: 'merged',
181 events.PullRequestUpdateEvent: 'updated',
180 events.PullRequestUpdateEvent: 'updated',
182 events.PullRequestCreateEvent: 'created',
181 events.PullRequestCreateEvent: 'created',
183 }.get(event.__class__, str(event.__class__))
182 }.get(event.__class__, str(event.__class__))
184
183
185 template = Template(textwrap.dedent(r'''
184 template = Template(textwrap.dedent(r'''
186 *${data['actor']['username']}* `${action}` pull request <${data['pullrequest']['url']}|#${data['pullrequest']['pull_request_id']}>:
185 *${data['actor']['username']}* `${action}` pull request <${data['pullrequest']['url']}|#${data['pullrequest']['pull_request_id']}>:
187 '''))
186 '''))
188 title = render_with_traceback(template, data=data, action=action)
187 title = render_with_traceback(template, data=data, action=action)
189
188
190 template = Template(textwrap.dedent(r'''
189 template = Template(textwrap.dedent(r'''
191 *pull request title*: ${pr_title}
190 *pull request title*: ${pr_title}
192 %if data['pullrequest']['commits']:
191 %if data['pullrequest']['commits']:
193 *commits*: ${len(data['pullrequest']['commits'])}
192 *commits*: ${len(data['pullrequest']['commits'])}
194 %endif
193 %endif
195 '''))
194 '''))
196 text = render_with_traceback(
195 text = render_with_traceback(
197 template,
196 template,
198 pr_title=data['pullrequest']['title'],
197 pr_title=data['pullrequest']['title'],
199 data=data)
198 data=data)
200
199
201 slack_data.title = title
200 slack_data.title = title
202 slack_data.text = text
201 slack_data.text = text
203
202
204 return slack_data
203 return slack_data
205
204
206 def format_repo_push_event(self, event, data, slack_data) -> SlackData:
205 def format_repo_push_event(self, event, data, slack_data) -> SlackData:
207 branches_commits = self.aggregate_branch_data(
206 branches_commits = self.aggregate_branch_data(
208 data['push']['branches'], data['push']['commits'])
207 data['push']['branches'], data['push']['commits'])
209
208
210 template = Template(r'''
209 template = Template(r'''
211 *${data['actor']['username']}* pushed to repo <${data['repo']['url']}|${data['repo']['repo_name']}>:
210 *${data['actor']['username']}* pushed to repo <${data['repo']['url']}|${data['repo']['repo_name']}>:
212 ''')
211 ''')
213 title = render_with_traceback(template, data=data)
212 title = render_with_traceback(template, data=data)
214
213
215 text = render_with_traceback(
214 text = render_with_traceback(
216 REPO_PUSH_TEMPLATE,
215 REPO_PUSH_TEMPLATE,
217 data=data,
216 data=data,
218 branches_commits=branches_commits,
217 branches_commits=branches_commits,
219 html_to_slack_links=html_to_slack_links,
218 html_to_slack_links=html_to_slack_links,
220 )
219 )
221
220
222 slack_data.title = title
221 slack_data.title = title
223 slack_data.text = text
222 slack_data.text = text
224
223
225 return slack_data
224 return slack_data
226
225
227 def format_repo_create_event(self, event, data, slack_data) -> SlackData:
226 def format_repo_create_event(self, event, data, slack_data) -> SlackData:
228 template = Template(r'''
227 template = Template(r'''
229 *${data['actor']['username']}* created new repository ${data['repo']['repo_name']}:
228 *${data['actor']['username']}* created new repository ${data['repo']['repo_name']}:
230 ''')
229 ''')
231 title = render_with_traceback(template, data=data)
230 title = render_with_traceback(template, data=data)
232
231
233 template = Template(textwrap.dedent(r'''
232 template = Template(textwrap.dedent(r'''
234 repo_url: ${data['repo']['url']}
233 repo_url: ${data['repo']['url']}
235 repo_type: ${data['repo']['repo_type']}
234 repo_type: ${data['repo']['repo_type']}
236 '''))
235 '''))
237 text = render_with_traceback(template, data=data)
236 text = render_with_traceback(template, data=data)
238
237
239 slack_data.title = title
238 slack_data.title = title
240 slack_data.text = text
239 slack_data.text = text
241
240
242 return slack_data No newline at end of file
241 return slack_data
General Comments 0
You need to be logged in to leave comments. Login now