Write only one comment per action
* For every 'select' or 'unselect' a single comment is written in every affected project * Previous automatic comments are deleted (as a first approach) * For more info, see https://progress.opensuse.org/issues/2564
This commit is contained in:
parent
690c76d6fb
commit
507e50d633
@ -2,10 +2,8 @@ from collections import defaultdict
|
||||
from xml.etree import cElementTree as ET
|
||||
|
||||
from osc import oscerr
|
||||
from osc.core import get_request
|
||||
from osc.core import http_GET
|
||||
|
||||
from osclib.comments import CommentAPI
|
||||
from osclib.request_finder import RequestFinder
|
||||
# from osclib.freeze_command import FreezeCommand
|
||||
|
||||
@ -19,8 +17,7 @@ class SelectCommand(object):
|
||||
|
||||
def __init__(self, api):
|
||||
self.api = api
|
||||
self.comment = CommentAPI(self.api.apiurl)
|
||||
self.pending_comments = defaultdict(lambda: defaultdict(list))
|
||||
self.affected_projects = set()
|
||||
|
||||
def _package(self, request):
|
||||
"""
|
||||
@ -67,11 +64,6 @@ class SelectCommand(object):
|
||||
# Normal 'select' command
|
||||
print('Adding request "{}" to project "{}"'.format(request, self.target_project))
|
||||
|
||||
# Add a new pending comment.
|
||||
user = get_request(self.api.apiurl, str(request)).get_creator()
|
||||
package = self._package(request)
|
||||
self.pending_comments[user][SELECT].append((package, request))
|
||||
|
||||
return self.api.rq_to_prj(request, self.target_project)
|
||||
elif 'staging' in request_project and (move or supersede):
|
||||
# 'select' command becomes a 'move'
|
||||
@ -91,10 +83,8 @@ class SelectCommand(object):
|
||||
|
||||
print('Moving "{}" from "{}" to "{}"'.format(request, fprj, self.target_project))
|
||||
|
||||
# Add a new pending comment.
|
||||
user = get_request(self.api.apiurl, str(request)).get_creator()
|
||||
package = self._package(request)
|
||||
self.pending_comments[user][MOVE].append((fprj, package, request))
|
||||
# Store the source project, we also need to write a comment there
|
||||
self.affected_projects.add(fprj)
|
||||
|
||||
return self.api.move_between_project(fprj, request, self.target_project)
|
||||
elif 'staging' in request_project and not move:
|
||||
@ -128,26 +118,10 @@ class SelectCommand(object):
|
||||
if not self.select_request(request, request_project, move, from_):
|
||||
return False
|
||||
|
||||
# Publish pending comments grouped by user and operation.
|
||||
for user in self.pending_comments:
|
||||
lines = []
|
||||
|
||||
if SELECT in self.pending_comments[user]:
|
||||
lines.append('Packages tracked now in %s:\n' % self.target_project)
|
||||
for package, request in self.pending_comments[user][SELECT]:
|
||||
lines.append('* %s [%s](%s)' % (package, request, '/request/show/' + str(request)))
|
||||
|
||||
if MOVE in self.pending_comments[user]:
|
||||
if lines:
|
||||
lines.append('\n')
|
||||
|
||||
lines.append('Packages moved to %s:\n' % self.target_project)
|
||||
for from_project, package, request in self.pending_comments[user][MOVE]:
|
||||
lines.append('* %s [%s](%s) from %s' % (package, request, '/request/show/' + str(request), from_project))
|
||||
lines.append('\nCC [at]%s' % user)
|
||||
|
||||
msg = '\n'.join(lines)
|
||||
self.comment.add_comment(project_name=self.target_project, comment=msg)
|
||||
# Notify everybody about the changes
|
||||
self.api.update_status_comments(target_project, 'select')
|
||||
for fprj in self.affected_projects:
|
||||
self.api.update_status_comments(fprj, 'select')
|
||||
|
||||
# now make sure we enable the prj if the prj contains any ringed package
|
||||
self.api.build_switch_staging_project(target_project)
|
||||
|
@ -24,6 +24,7 @@ from osc.core import http_GET
|
||||
from osc.core import http_POST
|
||||
from osc.core import http_PUT
|
||||
|
||||
from osclib.comments import CommentAPI
|
||||
|
||||
class StagingAPI(object):
|
||||
"""
|
||||
@ -387,13 +388,15 @@ class StagingAPI(object):
|
||||
"""
|
||||
|
||||
data = self.get_prj_pseudometa(project)
|
||||
author = get_request(self.apiurl, str(request_id)).get_creator()
|
||||
append = True
|
||||
for request in data['requests']:
|
||||
if request['package'] == package:
|
||||
request['author'] = author
|
||||
request['id'] = request_id
|
||||
append = False
|
||||
if append:
|
||||
data['requests'].append({'id': request_id, 'package': package})
|
||||
data['requests'].append({'id': request_id, 'package': package, 'author': author })
|
||||
self.set_prj_pseudometa(project, data)
|
||||
|
||||
def get_request_id_for_package(self, project, package):
|
||||
@ -1054,3 +1057,45 @@ class StagingAPI(object):
|
||||
except urllib2.HTTPError:
|
||||
return False
|
||||
return True
|
||||
|
||||
def update_status_comments(self, project, command):
|
||||
"""
|
||||
Refresh the status comments, used for notification purposes, based on
|
||||
the current list of requests. To ensure that all involved users
|
||||
(and nobody else) get notified, old status comments are deleted and
|
||||
a new one is created.
|
||||
:param project: project name
|
||||
:param command: name of the command to include in the message
|
||||
"""
|
||||
|
||||
# TODO: we need to discuss the best way to keep track of status
|
||||
# comments. Right now they are marked with '[osc staging' at the
|
||||
# beginning. Maybe a cleaner approach would be to store something
|
||||
# like 'last_status_comment_id' in the pseudometa. But the current
|
||||
# OBS API for adding comments doesn't return the id of the created
|
||||
# comment.
|
||||
|
||||
comment_api = CommentAPI(self.apiurl)
|
||||
|
||||
comments = comment_api.get_comments(project_name=project)
|
||||
for comment in comments.values():
|
||||
# TODO: update the comment removing the user mentions instead of
|
||||
# deleting the whole comment. But there is currently not call in
|
||||
# OBS API to update a comment
|
||||
if comment['comment'].startswith('[osc staging'):
|
||||
comment_api.delete(comment['id'])
|
||||
|
||||
meta = self.get_prj_pseudometa(project)
|
||||
lines = ['[osc staging %s]\n' % command]
|
||||
lines.append('The list of requests tracked in %s has changed:\n' % project)
|
||||
for req in meta['requests']:
|
||||
author = ''
|
||||
if 'author' in req.keys():
|
||||
# Proper metadata
|
||||
author = req['author']
|
||||
else:
|
||||
# Old style metadata
|
||||
author = get_request(self.apiurl, str(req['id'])).get_creator()
|
||||
lines.append('Request req#%s for package %s submitted by [AT]%s' % (req['id'], req['package'], author))
|
||||
msg = '\n'.join(lines)
|
||||
comment_api.add_comment(project_name=project, comment=msg)
|
||||
|
@ -2,7 +2,6 @@ from osc import oscerr
|
||||
from osc.core import get_request
|
||||
from osc.core import http_GET
|
||||
|
||||
from osclib.comments import CommentAPI
|
||||
from osclib.request_finder import RequestFinder
|
||||
|
||||
|
||||
@ -10,7 +9,6 @@ class UnselectCommand(object):
|
||||
|
||||
def __init__(self, api):
|
||||
self.api = api
|
||||
self.comment = CommentAPI(self.api.apiurl)
|
||||
|
||||
def perform(self, packages):
|
||||
"""
|
||||
@ -18,15 +16,16 @@ class UnselectCommand(object):
|
||||
:param packages: packages/requests to delete from staging projects
|
||||
"""
|
||||
|
||||
affected_projects = set()
|
||||
for request, request_project in RequestFinder.find_staged_sr(packages,
|
||||
self.api).items():
|
||||
staging_project = request_project['staging']
|
||||
affected_projects.add(staging_project)
|
||||
msg = 'Unselecting "{}" from "{}"'.format(request, staging_project)
|
||||
print(msg)
|
||||
self.api.rm_from_prj(staging_project, request_id=request)
|
||||
self.api.add_review(request, by_group='factory-staging', msg='Please recheck')
|
||||
|
||||
# Write a comment in the project.
|
||||
# user = get_request(self.api.apiurl, str(request)).get_creator()
|
||||
# self.comment.add_comment(project_name=staging_project,
|
||||
# comment='[at]%s: %s' % (user, msg))
|
||||
# Notify everybody about the changes
|
||||
for prj in affected_projects:
|
||||
self.api.update_status_comments(prj, 'unselect')
|
||||
|
@ -227,7 +227,7 @@ class TestApiCalls(unittest.TestCase):
|
||||
self.assertEqual('new', self.obs.requests[rq]['review'])
|
||||
self.assertEqual('review', self.obs.requests[rq]['request'])
|
||||
self.assertEqual(self.api.get_prj_pseudometa('openSUSE:Factory:Staging:A'),
|
||||
{'requests': [{'id': 123, 'package': 'gcc'}]})
|
||||
{'requests': [{'id': 123, 'package': 'gcc', 'author': 'Admin'}]})
|
||||
|
||||
def test_generate_build_status_details(self):
|
||||
"""Check whether generate_build_status_details works."""
|
||||
|
42
tests/obs.py
42
tests/obs.py
@ -210,6 +210,16 @@ class OBS(object):
|
||||
},
|
||||
}
|
||||
|
||||
self.comments = {
|
||||
'openSUSE:Factory:Staging:A': [
|
||||
{'who': 'Admin', 'when': '2014-06-01 17:56:28 UTC', 'id': '1',
|
||||
'body': 'Just a comment'
|
||||
}
|
||||
],
|
||||
'openSUSE:Factory:Staging:U': [],
|
||||
'openSUSE:Factory:Staging:B': []
|
||||
}
|
||||
|
||||
#
|
||||
# /request/
|
||||
#
|
||||
@ -552,12 +562,40 @@ class OBS(object):
|
||||
# /comments/
|
||||
#
|
||||
|
||||
@GET(re.compile(r'/comments/project/.*'))
|
||||
def get_comment(self, request, uri, headers):
|
||||
"""Get comments for a project."""
|
||||
prj = re.search(r'comments/project/([^/]*)', uri).group(1)
|
||||
comments = self.comments[prj]
|
||||
if not comments:
|
||||
return (200, headers, '<comments project="%s"/>' % prj)
|
||||
else:
|
||||
ret_str = '<comments project="%s">' % prj
|
||||
for c in comments:
|
||||
ret_str += '<comment who="%s" when="%s" id="%s">' % (c['who'], c['when'], c['id'])
|
||||
ret_str += c['body']
|
||||
ret_str += '</comment>'
|
||||
ret_str += '</comments>'
|
||||
return (200, headers, ret_str)
|
||||
|
||||
@POST(re.compile(r'/comments/project/.*'))
|
||||
def comment(self, request, uri, headers):
|
||||
"""Manage comments into projects."""
|
||||
def post_comment(self, request, uri, headers):
|
||||
"""Add comment to a project."""
|
||||
prj = re.search(r'comments/project/([^/]*)', uri).group(1)
|
||||
comment = {'who': 'Admin', 'when': time.strftime('%Y-%m-%d %H:%M:%S UTC', time.gmtime()),
|
||||
'id': str(sum(len(c) for c in self.comments.values()) + 1), 'body': request.body }
|
||||
self.comments[prj].append(comment)
|
||||
response = (200, headers, '<result>Ok</result>')
|
||||
return response
|
||||
|
||||
@DELETE(re.compile(r'/comment/\d+'))
|
||||
def delete_comment(self, request, uri, headers):
|
||||
"""Delete a comments."""
|
||||
comment_id = re.search(r'comment/(\d+)', uri).group(1)
|
||||
for prj in self.comments:
|
||||
self.comments[prj] = [c for c in self.comments[prj] if c['id'] != comment_id]
|
||||
return (200, headers, '<result>Ok</result>')
|
||||
|
||||
#
|
||||
# Static fixtures
|
||||
#
|
||||
|
@ -14,7 +14,7 @@ from obs import OBS
|
||||
from osc import oscerr
|
||||
from osclib.select_command import SelectCommand
|
||||
from oscs import StagingAPI
|
||||
|
||||
from osclib.comments import CommentAPI
|
||||
|
||||
class TestSelect(unittest.TestCase):
|
||||
|
||||
@ -30,6 +30,33 @@ class TestSelect(unittest.TestCase):
|
||||
self.assertEqual(True, SelectCommand(self.api).perform('openSUSE:Factory:Staging:A', ['gcc']))
|
||||
self.assertEqual(self.api.prj_frozen_enough('openSUSE:Factory:Staging:A'), True)
|
||||
|
||||
def test_select_comments(self):
|
||||
c_api = CommentAPI(self.api.apiurl)
|
||||
staging_a = 'openSUSE:Factory:Staging:A'
|
||||
comments = c_api.get_comments(project_name=staging_a)
|
||||
|
||||
# First select
|
||||
self.assertEqual(True, SelectCommand(self.api).perform(staging_a, ['gcc', 'wine']))
|
||||
first_select_comments = c_api.get_comments(project_name=staging_a)
|
||||
last_id = sorted(first_select_comments.keys())[-1]
|
||||
first_select_comment = first_select_comments[last_id]
|
||||
# Only one comment is added
|
||||
self.assertEqual(len(first_select_comments), len(comments) + 1)
|
||||
# With the right content
|
||||
self.assertTrue('Request req#123 for package gcc submitted by [AT]Admin' in first_select_comment['comment'])
|
||||
|
||||
# Second select
|
||||
self.assertEqual(True, SelectCommand(self.api).perform(staging_a, ['puppet']))
|
||||
second_select_comments = c_api.get_comments(project_name=staging_a)
|
||||
last_id = sorted(second_select_comments.keys())[-1]
|
||||
second_select_comment = second_select_comments[last_id]
|
||||
# The number of comments remains, but they are different
|
||||
self.assertEqual(len(second_select_comments), len(first_select_comments))
|
||||
self.assertNotEqual(second_select_comment['comment'], first_select_comment['comment'])
|
||||
# The new comments contents both old and new information
|
||||
self.assertTrue('Request req#123 for package gcc submitted by [AT]Admin' in second_select_comment['comment'])
|
||||
self.assertTrue('Request req#321 for package puppet submitted by [AT]Admin' in second_select_comment['comment'])
|
||||
|
||||
def test_no_matches(self):
|
||||
# search for requests
|
||||
with self.assertRaises(oscerr.WrongArgs) as cm:
|
||||
|
Loading…
x
Reference in New Issue
Block a user