Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(697)

Side by Side Diff: appengine/monorail/services/api_pb2_v1_helpers.py

Issue 1868553004: Open Source Monorail (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Rebase Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « appengine/monorail/services/__init__.py ('k') | appengine/monorail/services/api_svc_v1.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright 2016 The Chromium Authors. All rights reserved.
2 # Use of this source code is govered by a BSD-style
3 # license that can be found in the LICENSE file or at
4 # https://developers.google.com/open-source/licenses/bsd
5
6 """Convert Monorail PB objects to API PB objects"""
7
8 import datetime
9 import logging
10
11 from framework import framework_constants
12 from framework import framework_helpers
13 from framework import permissions
14 from proto import api_pb2_v1
15 from proto import project_pb2
16 from proto import tracker_pb2
17 from services import issue_svc
18 from services import project_svc
19 from services import user_svc
20 from tracker import tracker_bizobj
21 from tracker import tracker_helpers
22
23
24 def convert_project(project, config, role):
25 """Convert Monorail Project PB to API ProjectWrapper PB."""
26
27 return api_pb2_v1.ProjectWrapper(
28 kind='monorail#project',
29 name=project.project_name,
30 externalId=project.project_name,
31 htmlLink='/p/%s/' % project.project_name,
32 summary=project.summary,
33 description=project.description,
34 role=role,
35 issuesConfig=convert_project_config(config))
36
37
38 def convert_project_config(config):
39 """Convert Monorail ProjectIssueConfig PB to API ProjectIssueConfig PB."""
40
41 return api_pb2_v1.ProjectIssueConfig(
42 kind='monorail#projectIssueConfig',
43 restrictToKnown=config.restrict_to_known,
44 defaultColumns=config.default_col_spec.split(),
45 defaultSorting=config.default_sort_spec.split(),
46 statuses=[convert_status(s) for s in config.well_known_statuses],
47 labels=[convert_label(l) for l in config.well_known_labels],
48 prompts=[convert_template(t) for t in config.templates],
49 defaultPromptForMembers=config.default_template_for_developers,
50 defaultPromptForNonMembers=config.default_template_for_users)
51
52
53 def convert_status(status):
54 """Convert Monorail StatusDef PB to API Status PB."""
55
56 return api_pb2_v1.Status(
57 status=status.status,
58 meansOpen=status.means_open,
59 description=status.status_docstring)
60
61
62 def convert_label(label):
63 """Convert Monorail LabelDef PB to API Label PB."""
64
65 return api_pb2_v1.Label(
66 label=label.label,
67 description=label.label_docstring)
68
69
70 def convert_template(template):
71 """Convert Monorail TemplateDef PB to API Prompt PB."""
72
73 return api_pb2_v1.Prompt(
74 name=template.name,
75 title=template.summary,
76 description=template.content,
77 titleMustBeEdited=template.summary_must_be_edited,
78 status=template.status,
79 labels=template.labels,
80 membersOnly=template.members_only,
81 defaultToMember=template.owner_defaults_to_member,
82 componentRequired=template.component_required)
83
84
85 def convert_person(user_id, cnxn, services, trap_exception=False):
86 """Convert user id to API AtomPerson PB."""
87
88 if not user_id:
89 return None
90 user_email = None
91 try:
92 user_email = services.user.LookupUserEmail(cnxn, user_id)
93 except user_svc.NoSuchUserException as ex:
94 if trap_exception:
95 logging.warning(str(ex))
96 return None
97 else:
98 raise ex
99 return api_pb2_v1.AtomPerson(
100 kind='monorail#issuePerson',
101 name=user_email,
102 htmlLink='https://%s/u/%d' % (framework_helpers.GetHostPort(), user_id))
103
104
105 def convert_issue_ids(issue_ids, mar, services):
106 """Convert global issue ids to API IssueRef PB."""
107
108 # missed issue ids are filtered out.
109 issues = services.issue.GetIssues(mar.cnxn, issue_ids)
110 result = []
111 for issue in issues:
112 issue_ref = api_pb2_v1.IssueRef(
113 issueId=issue.local_id,
114 projectId=issue.project_name,
115 kind='monorail#issueRef')
116 result.append(issue_ref)
117 return result
118
119
120 def convert_issueref_pbs(issueref_pbs, mar, services):
121 """Convert API IssueRef PBs to global issue ids."""
122
123 if issueref_pbs:
124 result = []
125 for ir in issueref_pbs:
126 project_id = mar.project_id
127 if ir.projectId:
128 project = services.project.GetProjectByName(
129 mar.cnxn, ir.projectId)
130 if project:
131 project_id = project.project_id
132 try:
133 issue = services.issue.GetIssueByLocalID(
134 mar.cnxn, project_id, ir.issueId)
135 result.append(issue.issue_id)
136 except issue_svc.NoSuchIssueException:
137 logging.warning(
138 'Issue (%s:%d) does not exist.' % (ir.projectId, ir.issueId))
139 return result
140 else:
141 return None
142
143
144 def convert_issue(cls, issue, mar, services):
145 """Convert Monorail Issue PB to API IssuesGetInsertResponse."""
146
147 config = services.config.GetProjectConfig(mar.cnxn, issue.project_id)
148 granted_perms = tracker_bizobj.GetGrantedPerms(
149 issue, mar.auth.effective_ids, config)
150 issue_project = services.project.GetProject(mar.cnxn, issue.project_id)
151 component_list = []
152 for cd in config.component_defs:
153 cid = cd.component_id
154 if cid in issue.component_ids:
155 component_list.append(cd.path)
156 cc_list = [convert_person(p, mar.cnxn, services) for p in issue.cc_ids]
157 cc_list = [p for p in cc_list if p is not None]
158 field_values_list = []
159 field_id_dict = {
160 fd.field_id: fd.field_name for fd in config.field_defs}
161 for fv in issue.field_values:
162 field_name = field_id_dict.get(fv.field_id)
163 if not field_name:
164 logging.warning('Custom field %d of project %s does not exist',
165 fv.field_id, issue_project.project_name)
166 continue
167 val = None
168 if fv.user_id:
169 try:
170 val = services.user.LookupUserEmail(mar.cnxn, fv.user_id)
171 except user_svc.NoSuchUserException:
172 val = ''
173 elif fv.str_value:
174 val = fv.str_value
175 elif fv.int_value:
176 val = str(fv.int_value)
177 new_fv = api_pb2_v1.FieldValue(
178 fieldName=field_name,
179 fieldValue=val,
180 derived=fv.derived)
181 field_values_list.append(new_fv)
182 resp = cls(
183 kind='monorail#issue',
184 id=issue.local_id,
185 title=issue.summary,
186 summary=issue.summary,
187 projectId=issue_project.project_name,
188 stars=issue.star_count,
189 starred=services.issue_star.IsItemStarredBy(
190 mar.cnxn, issue.issue_id, mar.auth.user_id),
191 status=issue.status,
192 state=(api_pb2_v1.IssueState.open if
193 tracker_helpers.MeansOpenInProject(
194 tracker_bizobj.GetStatus(issue), config)
195 else api_pb2_v1.IssueState.closed),
196 labels=issue.labels,
197 components=component_list,
198 author=convert_person(issue.reporter_id, mar.cnxn, services),
199 owner=convert_person(issue.owner_id, mar.cnxn, services),
200 cc=cc_list,
201 updated=datetime.datetime.fromtimestamp(issue.modified_timestamp),
202 published=datetime.datetime.fromtimestamp(issue.opened_timestamp),
203 blockedOn=convert_issue_ids(issue.blocked_on_iids, mar, services),
204 blocking=convert_issue_ids(issue.blocking_iids, mar, services),
205 canComment=permissions.CanCommentIssue(
206 mar.auth.effective_ids, mar.perms, issue_project, issue,
207 granted_perms=granted_perms),
208 canEdit=permissions.CanEditIssue(
209 mar.auth.effective_ids, mar.perms, issue_project, issue,
210 granted_perms=granted_perms),
211 fieldValues=field_values_list)
212 if issue.closed_timestamp > 0:
213 resp.closed = datetime.datetime.fromtimestamp(issue.closed_timestamp)
214 if issue.merged_into:
215 resp.mergedInto=convert_issue_ids([issue.merged_into], mar, services)[0]
216 return resp
217
218
219 def convert_comment(issue, comment, mar, services, granted_perms):
220 """Convert Monorail IssueComment PB to API IssueCommentWrapper."""
221
222 can_delete = permissions.CanDelete(
223 mar.auth.user_id, mar.auth.effective_ids, mar.perms,
224 comment.deleted_by, comment.user_id, mar.project,
225 permissions.GetRestrictions(issue), granted_perms=granted_perms)
226
227 return api_pb2_v1.IssueCommentWrapper(
228 attachments=[convert_attachment(a) for a in comment.attachments],
229 author=convert_person(comment.user_id, mar.cnxn, services,
230 trap_exception=True),
231 canDelete=can_delete,
232 content=comment.content,
233 deletedBy=convert_person(comment.deleted_by, mar.cnxn, services,
234 trap_exception=True),
235 id=comment.sequence,
236 published=datetime.datetime.fromtimestamp(comment.timestamp),
237 updates=convert_amendments(issue, comment.amendments, mar, services),
238 kind='monorail#issueComment')
239
240
241 def convert_attachment(attachment):
242 """Convert Monorail Attachment PB to API Attachment."""
243
244 return api_pb2_v1.Attachment(
245 attachmentId=attachment.attachment_id,
246 fileName=attachment.filename,
247 fileSize=attachment.filesize,
248 mimetype=attachment.mimetype,
249 isDeleted=attachment.deleted)
250
251
252 def convert_amendments(issue, amendments, mar, services):
253 """Convert a list of Monorail Amendment PBs to API Update."""
254
255 result = api_pb2_v1.Update(kind='monorail#issueCommentUpdate')
256 for amendment in amendments:
257 if amendment.field == tracker_pb2.FieldID.SUMMARY:
258 result.summary = amendment.newvalue
259 elif amendment.field == tracker_pb2.FieldID.STATUS:
260 result.status = amendment.newvalue
261 elif amendment.field == tracker_pb2.FieldID.OWNER:
262 if len(amendment.added_user_ids) == 0:
263 result.owner = framework_constants.NO_USER_NAME
264 else:
265 user_email = services.user.LookupUserEmail(
266 mar.cnxn, amendment.added_user_ids[0])
267 result.owner = user_email
268 elif amendment.field == tracker_pb2.FieldID.LABELS:
269 result.labels = amendment.newvalue.split()
270 elif amendment.field == tracker_pb2.FieldID.CC:
271 for user_id in amendment.added_user_ids:
272 user_email = services.user.LookupUserEmail(mar.cnxn, user_id)
273 result.cc.append(user_email)
274 for user_id in amendment.removed_user_ids:
275 user_email = services.user.LookupUserEmail(mar.cnxn, user_id)
276 result.cc.append('-%s' % user_email)
277 elif amendment.field == tracker_pb2.FieldID.BLOCKEDON:
278 result.blockedOn = _append_project(
279 amendment.newvalue, issue.project_name)
280 elif amendment.field == tracker_pb2.FieldID.BLOCKING:
281 result.blocking = _append_project(
282 amendment.newvalue, issue.project_name)
283 elif amendment.field == tracker_pb2.FieldID.MERGEDINTO:
284 result.mergedInto = amendment.newvalue
285 elif amendment.field == tracker_pb2.FieldID.COMPONENTS:
286 result.components = amendment.newvalue.split()
287 elif amendment.field == tracker_pb2.FieldID.CUSTOM:
288 fv = api_pb2_v1.FieldValue()
289 fv.fieldName = amendment.custom_field_name
290 fv.fieldValue = amendment.newvalue
291 result.fieldValues.append(fv)
292
293 return result
294
295
296 def _append_project(issue_ids, project_name):
297 """Append project name to convert <id> to <project>:<id> format."""
298
299 result = []
300 id_list = issue_ids.split()
301 for id_str in id_list:
302 if ':' in id_str:
303 result.append(id_str)
304 # '-' means this issue is being removed
305 elif id_str.startswith('-'):
306 result.append('-%s:%s' % (project_name, id_str[1:]))
307 else:
308 result.append('%s:%s' % (project_name, id_str))
309 return result
310
311
312 def split_remove_add(item_list):
313 """Split one list of items into two: items to add and items to remove."""
314
315 list_to_add = []
316 list_to_remove = []
317
318 for item in item_list:
319 if item.startswith('-'):
320 list_to_remove.append(item[1:])
321 else:
322 list_to_add.append(item)
323
324 return list_to_add, list_to_remove
325
326
327 # TODO(sheyang): batch the SQL queries to fetch projects/issues.
328 def issue_global_ids(project_local_id_pairs, project_id, mar, services):
329 """Find global issues ids given <project_name>:<issue_local_id> pairs."""
330
331 result = []
332 for pair in project_local_id_pairs:
333 issue_project_id = None
334 local_id = None
335 if ':' in pair:
336 pair_ary = pair.split(':')
337 project_name = pair_ary[0]
338 local_id = int(pair_ary[1])
339 project = services.project.GetProjectByName(mar.cnxn, project_name)
340 if not project:
341 raise project_svc.NoSuchProjectException(
342 'Project %s does not exist' % project_name)
343 issue_project_id = project.project_id
344 else:
345 issue_project_id = project_id
346 local_id = int(pair)
347 result.append(
348 services.issue.LookupIssueID(mar.cnxn, issue_project_id, local_id))
349
350 return result
351
352
353 def convert_group_settings(group_name, setting):
354 """Convert UserGroupSettings to UserGroupSettingsWrapper."""
355 return api_pb2_v1.UserGroupSettingsWrapper(
356 groupName=group_name,
357 who_can_view_members=setting.who_can_view_members,
358 ext_group_type=setting.ext_group_type,
359 last_sync_time=setting.last_sync_time)
360
361
362 def convert_component_def(cd, mar, services):
363 """Convert ComponentDef PB to Component PB."""
364 project_name = services.project.LookupProjectNames(
365 mar.cnxn, [cd.project_id])[cd.project_id]
366 user_ids = set()
367 user_ids.update(
368 cd.admin_ids + cd.cc_ids + [cd.creator_id] + [cd.modifier_id])
369 user_names_dict = services.user.LookupUserEmails(mar.cnxn, list(user_ids))
370 component = api_pb2_v1.Component(
371 componentId=cd.component_id,
372 projectName=project_name,
373 componentPath=cd.path,
374 description=cd.docstring,
375 admin=sorted([user_names_dict[uid] for uid in cd.admin_ids]),
376 cc=sorted([user_names_dict[uid] for uid in cd.cc_ids]),
377 deprecated=cd.deprecated)
378 if cd.created:
379 component.created = datetime.datetime.fromtimestamp(cd.created)
380 component.creator = user_names_dict[cd.creator_id]
381 if cd.modified:
382 component.modified = datetime.datetime.fromtimestamp(cd.modified)
383 component.modifier = user_names_dict[cd.modifier_id]
384 return component
385
386
387 def convert_component_ids(config, component_names):
388 """Convert a list of component names to ids."""
389 component_names_lower = [name.lower() for name in component_names]
390 result = []
391 for cd in config.component_defs:
392 cpath = cd.path
393 if cpath.lower() in component_names_lower:
394 result.append(cd.component_id)
395 return result
396
397
398 def convert_field_values(field_values, mar, services):
399 """Convert user passed in field value list to FieldValue PB, or labels."""
400 fv_list_add = []
401 fv_list_remove = []
402 fv_list_clear = []
403 label_list_add = []
404 label_list_remove = []
405 field_name_dict = {
406 fd.field_name: fd for fd in mar.config.field_defs}
407
408 for fv in field_values:
409 field_def = field_name_dict.get(fv.fieldName)
410 if not field_def:
411 logging.warning('Custom field %s of does not exist', fv.fieldName)
412 continue
413
414 if fv.operator == api_pb2_v1.FieldValueOperator.clear:
415 fv_list_clear.append(field_def.field_id)
416 continue
417
418 # Enum fields are stored as labels
419 if field_def.field_type == tracker_pb2.FieldTypes.ENUM_TYPE:
420 raw_val = '%s-%s' % (fv.fieldName, fv.fieldValue)
421 if fv.operator == api_pb2_v1.FieldValueOperator.remove:
422 label_list_remove.append(raw_val)
423 elif fv.operator == api_pb2_v1.FieldValueOperator.add:
424 label_list_add.append(raw_val)
425 else:
426 logging.warning('Unsupported field value operater %s', fv.operator)
427 else:
428 new_fv = tracker_pb2.FieldValue(
429 field_id=field_def.field_id)
430 if field_def.field_type == tracker_pb2.FieldTypes.USER_TYPE:
431 try:
432 new_fv.user_id = services.user.LookupUserID(mar.cnxn, fv.fieldValue)
433 except user_svc.NoSuchUserException:
434 new_fv.user_id = 0
435 elif field_def.field_type == tracker_pb2.FieldTypes.STR_TYPE:
436 new_fv.str_value = fv.fieldValue
437 elif field_def.field_type == tracker_pb2.FieldTypes.INT_TYPE:
438 new_fv.int_value = int(fv.fieldValue)
439 else:
440 logging.warning(
441 'Unsupported field value type %s', field_def.field_type)
442
443 if fv.operator == api_pb2_v1.FieldValueOperator.remove:
444 fv_list_remove.append(new_fv)
445 elif fv.operator == api_pb2_v1.FieldValueOperator.add:
446 fv_list_add.append(new_fv)
447 else:
448 logging.warning('Unsupported field value operater %s', fv.operator)
449
450 return (fv_list_add, fv_list_remove, fv_list_clear,
451 label_list_add, label_list_remove)
OLDNEW
« no previous file with comments | « appengine/monorail/services/__init__.py ('k') | appengine/monorail/services/api_svc_v1.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698