Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate XML-RPC MultiCall #3821

Merged
merged 1 commit into from
Apr 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 8 additions & 106 deletions tests/unit/legacy/api/xmlrpc/test_xmlrpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -794,110 +794,12 @@ def test_browse(db_request):
)) == {(expected_release.name, expected_release.version)}


class TestMulticall:

def test_multicall(self, monkeypatch):
dumped = pretend.stub(encode=lambda: None)
dumps = pretend.call_recorder(lambda *a, **kw: dumped)
monkeypatch.setattr(xmlrpc.xmlrpc.client, 'dumps', dumps)

loaded = pretend.stub()
loads = pretend.call_recorder(lambda *a, **kw: loaded)
monkeypatch.setattr(xmlrpc.xmlrpc.client, 'loads', loads)

subreq = pretend.stub()
blank = pretend.call_recorder(lambda *a, **kw: subreq)
monkeypatch.setattr(xmlrpc.Request, 'blank', blank)

body = pretend.stub()
response = pretend.stub(body=body)

request = pretend.stub(
invoke_subrequest=pretend.call_recorder(lambda *a, **kw: response),
add_response_callback=pretend.call_recorder(
lambda *a, **kw: response),
)

callback = pretend.stub()
monkeypatch.setattr(
xmlrpc,
'measure_response_content_length',
pretend.call_recorder(lambda metric_name: callback)
)

args = [
{'methodName': 'search', 'params': [{'name': 'foo'}]},
{'methodName': 'browse', 'params': [{'classifiers': ['bar']}]},
]

responses = xmlrpc.multicall(request, args)

assert responses == [loaded, loaded]
assert blank.calls == [
pretend.call('/RPC2', headers={'Content-Type': 'text/xml'}),
pretend.call('/RPC2', headers={'Content-Type': 'text/xml'}),
]
assert request.invoke_subrequest.calls == [
pretend.call(subreq),
pretend.call(subreq),
]
assert request.add_response_callback.calls == [
pretend.call(callback),
]
assert dumps.calls == [
pretend.call(({'name': 'foo'},), methodname='search'),
pretend.call(({'classifiers': ['bar']},), methodname='browse'),
]
assert loads.calls == [pretend.call(body), pretend.call(body)]

def test_recursive_multicall(self):
request = pretend.stub()
args = [
{'methodName': 'system.multicall', 'params': []},
]
with pytest.raises(xmlrpc.XMLRPCWrappedError) as exc:
xmlrpc.multicall(request, args)

assert exc.value.faultString == (
'ValueError: Cannot use system.multicall inside a multicall'
)

def test_missing_multicall_method(self):
request = pretend.stub()
args = [{}]
with pytest.raises(xmlrpc.XMLRPCWrappedError) as exc:
xmlrpc.multicall(request, args)

assert exc.value.faultString == (
'ValueError: Method name not provided'
)

def test_too_many_multicalls_method(self):
request = pretend.stub()
args = [{'methodName': 'nah'}] * 21

with pytest.raises(xmlrpc.XMLRPCWrappedError) as exc:
xmlrpc.multicall(request, args)

assert exc.value.faultString == (
'ValueError: Multicall limit is 20 calls'
)

def test_measure_response_content_length(self):
metric_name = 'some_metric_name'
callback = xmlrpc.measure_response_content_length(metric_name)

request = pretend.stub(
registry=pretend.stub(
datadog=pretend.stub(
histogram=pretend.call_recorder(lambda *a: None)
)
)
)
response = pretend.stub(content_length=pretend.stub())

callback(request, response)
def test_multicall():
request = pretend.stub()
with pytest.raises(xmlrpc.XMLRPCWrappedError) as exc:
xmlrpc.multicall(request, [])

assert request.registry.datadog.histogram.calls == [
pretend.call(metric_name, response.content_length),
]
assert exc.value.faultString == (
'ValueError: MultiCall requests have been deprecated, use individual '
'requests instead.'
)
45 changes: 4 additions & 41 deletions warehouse/legacy/api/xmlrpc/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

from elasticsearch_dsl import Q
from packaging.utils import canonicalize_name
from pyramid.request import Request
from pyramid.view import view_config
from pyramid_rpc.xmlrpc import (
exception_view as _exception_view, xmlrpc_method as _xmlrpc_method
Expand Down Expand Up @@ -448,47 +447,11 @@ def browse(request, classifiers):
return [(r.name, r.version) for r in releases]


def measure_response_content_length(metric_name):

def _callback(request, response):
request.registry.datadog.histogram(
metric_name, response.content_length
)

return _callback


@xmlrpc_method(method='system.multicall')
def multicall(request, args):
if any(arg.get('methodName') == 'system.multicall' for arg in args):
raise XMLRPCWrappedError(
ValueError('Cannot use system.multicall inside a multicall')
)

if not all(arg.get('methodName') for arg in args):
raise XMLRPCWrappedError(ValueError('Method name not provided'))

if len(args) > _MAX_MULTICALLS:
raise XMLRPCWrappedError(
ValueError(f'Multicall limit is {_MAX_MULTICALLS} calls')
)

responses = []
for arg in args:
name = arg.get('methodName')
subreq = Request.blank('/RPC2', headers={'Content-Type': 'text/xml'})
subreq.method = 'POST'
subreq.body = xmlrpc.client.dumps(
tuple(arg.get('params')),
methodname=name,
).encode()
response = request.invoke_subrequest(subreq)
responses.append(xmlrpc.client.loads(response.body))

request.add_response_callback(
measure_response_content_length(
'warehouse.xmlrpc.system.multicall.content_length'
raise XMLRPCWrappedError(
ValueError(
'MultiCall requests have been deprecated, use individual '
'requests instead.'
)
)

return responses