Skip to content

Commit 845df6a

Browse files
committed
RCB-596: Sonar issues. Add coverage for AddressSimilarity.
1 parent 73c1524 commit 845df6a

File tree

2 files changed

+103
-86
lines changed

2 files changed

+103
-86
lines changed

rosette/api.py

Lines changed: 49 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,16 @@
2828
import requests
2929
import platform
3030

31+
_APPLICATION_JSON = 'application/json'
32+
_BINDING_LANGUAGE = 'python'
3133
_BINDING_VERSION = '1.14.4'
34+
_CONCURRENCY_HEADER = 'x-rosetteapi-concurrency'
35+
_CUSTOM_HEADER_PREFIX = 'X-RosetteAPI-'
36+
_CUSTOM_HEADER_PATTERN = re.compile('^' + _CUSTOM_HEADER_PREFIX)
3237
_GZIP_BYTEARRAY = bytearray([0x1F, 0x8b, 0x08])
3338

3439
_ISPY3 = sys.version_info[0] == 3
3540

36-
3741
if _ISPY3:
3842
_GZIP_SIGNATURE = _GZIP_BYTEARRAY
3943
else:
@@ -49,7 +53,6 @@ def __init__(self, js, code):
4953
self.status_code = code
5054

5155
def json(self):
52-
""" return json"""
5356
return self._json
5457

5558

@@ -112,7 +115,7 @@ def serialize(self, options):
112115
values = {}
113116
for (key, val) in self.__params.items():
114117
if val is None:
115-
pass
118+
continue
116119
else:
117120
values[key] = val
118121

@@ -242,7 +245,7 @@ def validate(self):
242245
if self[option] is None:
243246
raise RosetteException(
244247
"missingParameter",
245-
"Required Name Translation parameter, " + option + ", not supplied",
248+
"Required Name Translation parameter is missing: " + option,
246249
repr(option))
247250

248251

@@ -268,7 +271,7 @@ def validate(self):
268271
if self[option] is None:
269272
raise RosetteException(
270273
"missingParameter",
271-
"Required Address Similarity parameter, " + option + ", not supplied",
274+
"Required Address Similarity parameter is missing: " + option,
272275
repr(option))
273276

274277

@@ -301,7 +304,7 @@ def validate(self):
301304
if self[option] is None:
302305
raise RosetteException(
303306
"missingParameter",
304-
"Required Name Similarity parameter, " + option + ", not supplied",
307+
"Required Name Similarity parameter is missing: " + option,
305308
repr(option))
306309

307310

@@ -321,7 +324,7 @@ def validate(self):
321324
if self["names"] is None: # required
322325
raise RosetteException(
323326
"missingParameter",
324-
"Required Name De-Duplication parameter, names, not supplied",
327+
"Required Name De-Duplication parameter is missing: names",
325328
repr("names"))
326329

327330

@@ -372,31 +375,37 @@ def __finish_result(self, response, ename):
372375
raise RosetteException(code, complaint_url +
373376
" : failed to communicate with Rosette", msg)
374377

375-
def info(self):
376-
"""Issues an "info" request to the L{EndpointCaller}'s specific endpoint.
377-
@return: A dictionary telling server version and other
378-
identifying data."""
379-
url = self.service_url + self.api.endpoints["INFO"]
380-
headers = {'Accept': 'application/json', 'X-RosetteAPI-Binding': 'python',
381-
'X-RosetteAPI-Binding-Version': _BINDING_VERSION}
378+
def __set_headers(self):
379+
headers = {'Accept': _APPLICATION_JSON,
380+
_CUSTOM_HEADER_PREFIX + 'Binding': _BINDING_LANGUAGE,
381+
_CUSTOM_HEADER_PREFIX + 'Binding-Version': _BINDING_VERSION}
382382

383383
custom_headers = self.api.get_custom_headers()
384-
pattern = re.compile('^X-RosetteAPI-')
385384
if custom_headers is not None:
386385
for key in custom_headers.keys():
387-
if pattern.match(key) is not None:
386+
if _CUSTOM_HEADER_PATTERN.match(key) is not None:
388387
headers[key] = custom_headers[key]
389388
else:
390389
raise RosetteException("badHeader",
391-
"Custom header name must begin with \"X-RosetteAPI-\"",
390+
"Custom header name must begin with \"" + _CUSTOM_HEADER_PREFIX + "\"",
392391
key)
393392
self.api.clear_custom_headers()
394393

395394
if self.debug:
396-
headers['X-RosetteAPI-Devel'] = 'true'
397-
self.logger.info('info: ' + url)
395+
headers[_CUSTOM_HEADER_PREFIX + 'Devel'] = 'true'
396+
398397
if self.user_key is not None:
399-
headers["X-RosetteAPI-Key"] = self.user_key
398+
headers[_CUSTOM_HEADER_PREFIX + "Key"] = self.user_key
399+
400+
return headers
401+
402+
def info(self):
403+
"""Issues an "info" request to the L{EndpointCaller}'s specific endpoint.
404+
@return: A dictionary telling server version and other
405+
identifying data."""
406+
url = self.service_url + self.api.endpoints["INFO"]
407+
headers = self.__set_headers()
408+
self.logger.info('info: ' + url)
400409
response = self.api.get_http(url, headers=headers)
401410
return self.__finish_result(response, "info")
402411

@@ -407,26 +416,8 @@ def ping(self):
407416
signalled."""
408417

409418
url = self.service_url + self.api.endpoints['PING']
410-
headers = {'Accept': 'application/json', 'X-RosetteAPI-Binding': 'python',
411-
'X-RosetteAPI-Binding-Version': _BINDING_VERSION}
412-
413-
custom_headers = self.api.get_custom_headers()
414-
pattern = re.compile('^X-RosetteAPI-')
415-
if custom_headers is not None:
416-
for key in custom_headers.keys():
417-
if pattern.match(key) is not None:
418-
headers[key] = custom_headers[key]
419-
else:
420-
raise RosetteException("badHeader",
421-
"Custom header name must begin with \"X-RosetteAPI-\"",
422-
key)
423-
self.api.clear_custom_headers()
424-
425-
if self.debug:
426-
headers['X-RosetteAPI-Devel'] = 'true'
419+
headers = self.__set_headers()
427420
self.logger.info('Ping: ' + url)
428-
if self.user_key is not None:
429-
headers["X-RosetteAPI-Key"] = self.user_key
430421
response = self.api.get_http(url, headers=headers)
431422
return self.__finish_result(response, "ping")
432423

@@ -454,9 +445,9 @@ def call(self, parameters):
454445

455446
if not isinstance(parameters, _DocumentParamSetBase):
456447
if self.suburl != self.api.endpoints['NAME_SIMILARITY'] \
457-
and self.suburl != self.api.self.api.endpoints['NAME_TRANSLATION'] \
458-
and self.suburl != self.api.self.api.endpoints['NAME_DEDUPLICATION'] \
459-
and self.suburl != self.api.self.api.endpoints['ADDRESS_SIMILARITY']:
448+
and self.suburl != self.api.self.api.endpoints['NAME_TRANSLATION'] \
449+
and self.suburl != self.api.self.api.endpoints['NAME_DEDUPLICATION'] \
450+
and self.suburl != self.api.self.api.endpoints['ADDRESS_SIMILARITY']:
460451
text = parameters
461452
parameters = DocumentParameters()
462453
parameters['content'] = text
@@ -471,22 +462,7 @@ def call(self, parameters):
471462
params_to_serialize = parameters.serialize(self.api.options)
472463
headers = {}
473464
if self.user_key is not None:
474-
custom_headers = self.api.get_custom_headers()
475-
pattern = re.compile('^X-RosetteAPI-')
476-
if custom_headers is not None:
477-
for key in custom_headers.keys():
478-
if pattern.match(key) is not None:
479-
headers[key] = custom_headers[key]
480-
else:
481-
raise RosetteException("badHeader",
482-
"Custom header name must "
483-
"begin with \"X-RosetteAPI-\"",
484-
key)
485-
self.api.clear_custom_headers()
486-
487-
headers["X-RosetteAPI-Key"] = self.user_key
488-
headers["X-RosetteAPI-Binding"] = "python"
489-
headers["X-RosetteAPI-Binding-Version"] = _BINDING_VERSION
465+
headers = self.__set_headers()
490466

491467
if self.use_multipart:
492468
payload = None
@@ -496,7 +472,7 @@ def call(self, parameters):
496472
params = dict(
497473
(key,
498474
value) for key,
499-
value in params_to_serialize.items() if key == 'language')
475+
value in params_to_serialize.items() if key == 'language')
500476
files = {
501477
'content': (
502478
os.path.basename(
@@ -506,7 +482,7 @@ def call(self, parameters):
506482
'request': (
507483
'request_options',
508484
json.dumps(params),
509-
'application/json')}
485+
_APPLICATION_JSON)}
510486
request = requests.Request(
511487
'POST', url, files=files, headers=headers, params=payload)
512488
prepared_request = self.api.session.prepare_request(request)
@@ -519,11 +495,11 @@ def call(self, parameters):
519495
_my_loads(rdata, response_headers), status)
520496
else:
521497
if self.debug:
522-
headers['X-RosetteAPI-Devel'] = True
498+
headers[_CUSTOM_HEADER_PREFIX + 'Devel'] = True
523499
self.logger.info('operate: ' + url)
524-
headers['Accept'] = "application/json"
500+
headers['Accept'] = _APPLICATION_JSON
525501
headers['Accept-Encoding'] = "gzip"
526-
headers['Content-Type'] = "application/json"
502+
headers['Content-Type'] = _APPLICATION_JSON
527503
response = self.api.post_http(url, params_to_serialize, headers)
528504
return self.__finish_result(response, "operate")
529505

@@ -625,6 +601,10 @@ def set_pool_size(self, new_pool_size):
625601
else:
626602
self.session.mount('http://', adapter)
627603

604+
def __adjust_concurrency(self, dict_headers):
605+
if _CONCURRENCY_HEADER in dict_headers and dict_headers[_CONCURRENCY_HEADER] != self.max_pool_size:
606+
self.set_pool_size(dict_headers[_CONCURRENCY_HEADER])
607+
628608
def _make_request(self, operation, url, data, headers):
629609
"""
630610
@param operation: POST or GET
@@ -654,9 +634,8 @@ def _make_request(self, operation, url, data, headers):
654634
status = response.status_code
655635
rdata = response.content
656636
dict_headers = dict(response.headers)
637+
self.__adjust_concurrency(dict_headers)
657638
response_headers = {"responseHeaders": dict_headers}
658-
if 'x-rosetteapi-concurrency' in dict_headers and dict_headers['x-rosetteapi-concurrency'] != self.max_pool_size:
659-
self.set_pool_size(dict_headers['x-rosetteapi-concurrency'])
660639

661640
if status == 200:
662641
return rdata, status, response_headers
@@ -672,9 +651,11 @@ def _make_request(self, operation, url, data, headers):
672651
if not message:
673652
message = rdata
674653
raise RosetteException(code, message, url)
675-
676-
except:
677-
raise
654+
except json.JSONDecodeError as exception:
655+
raise RosetteException(
656+
exception,
657+
"Problem decoding JSON",
658+
rdata)
678659
except requests.exceptions.RequestException as exception:
679660
raise RosetteException(
680661
exception,

tests/test_rosette_api.py

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@
2323
import platform
2424
import httpretty
2525
import pytest
26-
from rosette.api import(API,
27-
DocumentParameters,
28-
NameTranslationParameters,
29-
NameSimilarityParameters,
30-
NameDeduplicationParameters,
31-
RosetteException)
26+
from rosette.api import (API,
27+
DocumentParameters,
28+
NameTranslationParameters,
29+
NameSimilarityParameters,
30+
NameDeduplicationParameters,
31+
RosetteException, AddressSimilarityParameters)
3232

3333
_ISPY3 = sys.version_info[0] == 3
3434

@@ -118,7 +118,7 @@ def test_custom_header_props(api):
118118
assert value == api.get_custom_headers()[key]
119119

120120
api.clear_custom_headers()
121-
assert len(api.get_custom_headers()) is 0
121+
assert len(api.get_custom_headers()) == 0
122122

123123
# Test for invalid header name
124124

@@ -460,10 +460,10 @@ def test_name_deduplicatation_parameters(api, json_response):
460460
params = NameDeduplicationParameters()
461461

462462
with pytest.raises(RosetteException) as e_rosette:
463-
result = api.name_deduplication(params)
463+
api.name_deduplication(params)
464464

465465
assert e_rosette.value.status == 'missingParameter'
466-
assert e_rosette.value.message == 'Required Name De-Duplication parameter, names, not supplied'
466+
assert e_rosette.value.message == 'Required Name De-Duplication parameter is missing: names'
467467

468468
params["names"] = ["John Smith", "Johnathon Smith", "Fred Jones"]
469469

@@ -572,6 +572,43 @@ def test_for_no_content_or_contentUri(api, json_response, doc_params):
572572
httpretty.disable()
573573
httpretty.reset()
574574

575+
576+
def test_for_address_similarity_required_parameters(api, json_response):
577+
"""Test address similarity parameters"""
578+
httpretty.enable()
579+
httpretty.register_uri(httpretty.POST, "https://api.rosette.com/rest/v1/info",
580+
body=json_response, status=200, content_type="application/json")
581+
httpretty.register_uri(httpretty.POST, "https://api.rosette.com/rest/v1/address-similarity",
582+
body=json_response, status=200, content_type="application/json")
583+
584+
params = AddressSimilarityParameters()
585+
586+
with pytest.raises(RosetteException) as e_rosette:
587+
api.address_similarity(params)
588+
589+
assert e_rosette.value.status == 'missingParameter'
590+
assert e_rosette.value.message == 'Required Address Similarity parameter is missing: address1'
591+
592+
params["address1"] = {"houseNumber": "1600",
593+
"road": "Pennsylvania Ave NW",
594+
"city": "Washington",
595+
"state": "DC",
596+
"postCode": "20500"}
597+
598+
with pytest.raises(RosetteException) as e_rosette:
599+
api.address_similarity(params)
600+
601+
assert e_rosette.value.status == 'missingParameter'
602+
assert e_rosette.value.message == 'Required Address Similarity parameter is missing: address2'
603+
604+
params["address2"] = {"text": "160 Pennsilvana Avenue, Washington, D.C., 20500"}
605+
606+
result = api.address_similarity(params)
607+
assert result["name"] == "Rosette"
608+
httpretty.disable()
609+
httpretty.reset()
610+
611+
575612
# Test for required Name Similarity parameters
576613

577614

@@ -588,20 +625,20 @@ def test_for_name_similarity_required_parameters(api, json_response):
588625
params = NameSimilarityParameters()
589626

590627
with pytest.raises(RosetteException) as e_rosette:
591-
result = api.name_similarity(params)
628+
api.name_similarity(params)
592629

593630
assert e_rosette.value.status == 'missingParameter'
594-
assert e_rosette.value.message == 'Required Name Similarity parameter, name1, not supplied'
631+
assert e_rosette.value.message == 'Required Name Similarity parameter is missing: name1'
595632

596633
params["name1"] = {
597634
"text": matched_name_data1,
598635
"language": "eng",
599636
"entityType": "PERSON"}
600637
with pytest.raises(RosetteException) as e_rosette:
601-
result = api.name_similarity(params)
638+
api.name_similarity(params)
602639

603640
assert e_rosette.value.status == 'missingParameter'
604-
assert e_rosette.value.message == 'Required Name Similarity parameter, name2, not supplied'
641+
assert e_rosette.value.message == 'Required Name Similarity parameter is missing: name2'
605642

606643
params["name2"] = {"text": matched_name_data2, "entityType": "PERSON"}
607644

@@ -626,19 +663,18 @@ def test_for_name_translation_required_parameters(api, json_response):
626663
params["targetScript"] = "Latn"
627664

628665
with pytest.raises(RosetteException) as e_rosette:
629-
result = api.name_translation(params)
666+
api.name_translation(params)
630667

631668
assert e_rosette.value.status == 'missingParameter'
632-
assert e_rosette.value.message == 'Required Name Translation parameter, name, not supplied'
669+
assert e_rosette.value.message == 'Required Name Translation parameter is missing: name'
633670

634671
params["name"] = "some data to translate"
635672

636673
with pytest.raises(RosetteException) as e_rosette:
637-
result = api.name_translation(params)
674+
api.name_translation(params)
638675

639676
assert e_rosette.value.status == 'missingParameter'
640-
assert e_rosette.value.message == ('Required Name Translation parameter, '
641-
'targetLanguage, not supplied')
677+
assert e_rosette.value.message == 'Required Name Translation parameter is missing: targetLanguage'
642678

643679
params["targetLanguage"] = "eng"
644680

0 commit comments

Comments
 (0)