blob: 327200a40cc5121c59077ce5180d91e4d5dbd28f [file] [log] [blame]
# Copyright (C) 2021-2022 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import json
import re
from .base import Base
from webkitbugspy import User
from webkitcorepy import mocks
class Bugzilla(Base, mocks.Requests):
top = None
@classmethod
def time_string(cls, timestamp):
from datetime import datetime, timedelta
return datetime.utcfromtimestamp(timestamp - timedelta(hours=7).seconds).strftime('%Y-%m-%dT%H:%M:%SZ')
@classmethod
def transform_user(cls, user):
return User(
name=user.name,
username=user.email,
emails=user.emails,
)
def __init__(self, hostname='bugs.example.com', users=None, issues=None):
Base.__init__(self, users=users, issues=issues)
mocks.Requests.__init__(self, hostname)
def _user(self, url, username):
user = self.users[username]
if not user:
return mocks.Response(
url=url,
headers={'Content-Type': 'text/json'},
status_code=404,
text=json.dumps(dict(
code=51,
error=True,
message="There is no user named '{}'. Either you mis-typed the name or that user has not yet registered for a Bugzilla account.".format(username),
)),
)
return mocks.Response.fromJson(dict(
users=[dict(
name=user.username,
real_name=user.name,
)],
), url=url)
def _issue(self, url, id):
if id not in self.issues:
return mocks.Response(
url=url,
headers={'Content-Type': 'text/json'},
status_code=404,
text=json.dumps(dict(
code=101,
error=True,
message="Bug #{} does not exist.".format(id),
)),
)
issue = self.issues[id]
return mocks.Response.fromJson(dict(
bugs=[dict(
id=id,
summary=issue['title'],
creation_time=self.time_string(issue['timestamp']),
status='RESOLVED' if issue['opened'] else 'FIXED',
creator=self.users[issue['creator'].name].username,
creator_detail=dict(
email=issue['creator'].email,
name=self.users[issue['creator'].name].username,
real_name=issue['creator'].name,
), assigned_to=self.users[issue['assignee'].name].username,
assigned_to_detail=dict(
email=issue['assignee'].email,
name=self.users[issue['assignee'].name].username,
real_name=issue['assignee'].name,
), cc=[self.users[user.name].username for user in issue.get('watchers', [])],
cc_detail=[
dict(
email=user.email,
name=self.users[user.name].username,
real_name=user.name,
) for user in issue.get('watchers', [])
], see_also=[
'https://{}/show_bug.cgi?id={}'.format(self.hosts[0], n) for n in issue.get('references', [])
],
)],
), url=url)
def _see_also(self, url, id):
if id not in self.issues:
return mocks.Response(
url=url,
headers={'Content-Type': 'text/json'},
status_code=404,
text=json.dumps(dict(
code=101,
error=True,
message="Bug #{} does not exist.".format(id),
)),
)
issue = self.issues[id]
return mocks.Response.fromJson(dict(
bugs=[dict(
id=id,
see_also=[
'https://{}/show_bug.cgi?id={}'.format(self.hosts[0], n) for n in issue.get('references', [])
],
)],
), url=url)
def _comments(self, url, id):
if id not in self.issues:
return mocks.Response(
url=url,
headers={'Content-Type': 'text/json'},
status_code=404,
text=json.dumps(dict(
code=101,
error=True,
message="Bug #{} does not exist.".format(id),
)),
)
issue = self.issues[id]
return mocks.Response.fromJson(dict(
bugs={str(id): dict(comments=[
dict(
bug_id=id,
creator=self.users[issue['creator'].name].username,
creation_time=self.time_string(issue['timestamp']),
time=self.time_string(issue['timestamp']),
text=issue['description'],
),
] + [
dict(
bug_id=id,
creator=self.users[comment.user.name].username,
creation_time=self.time_string(comment.timestamp),
time=self.time_string(comment.timestamp),
text=comment.content,
) for comment in issue['comments']
])},
), url=url)
def request(self, method, url, data=None, params=None, auth=None, json=None, **kwargs):
if not url.startswith('http://') and not url.startswith('https://'):
return mocks.Response.create404(url)
stripped_url = url.split('://')[-1]
match = re.match(r'{}/rest/user\?names=(?P<username>\S+)$'.format(self.hosts[0]), stripped_url)
if match and method == 'GET':
return self._user(url, match.group('username'))
match = re.match(r'{}/rest/bug/(?P<id>\d+)$'.format(self.hosts[0]), stripped_url)
if match and method == 'GET':
return self._issue(url, int(match.group('id')))
match = re.match(r'{}/rest/bug/(?P<id>\d+)\?include_fields=see_also$'.format(self.hosts[0]), stripped_url)
if match and method == 'GET':
return self._see_also(url, int(match.group('id')))
match = re.match(r'{}/rest/bug/(?P<id>\d+)/comment$'.format(self.hosts[0]), stripped_url)
if match and method == 'GET':
return self._comments(url, int(match.group('id')))
return mocks.Response.create404(url)