diff --git a/restclients/dao.py b/restclients/dao.py index d7099ea0..966b37d9 100644 --- a/restclients/dao.py +++ b/restclients/dao.py @@ -148,6 +148,9 @@ def getURL(self, url, headers): def putURL(self, url, headers, body): return self._putURL('irws', url, headers, body) + def postURL(self, url, headers, body): + return self._postURL('irws', url, headers, body) + def _getDAO(self): return self._getModule('RESTCLIENTS_IRWS_DAO_CLASS', IRWSFile) diff --git a/restclients/dao_implementation/irws.py b/restclients/dao_implementation/irws.py index c74762ac..755629bd 100644 --- a/restclients/dao_implementation/irws.py +++ b/restclients/dao_implementation/irws.py @@ -6,6 +6,7 @@ from restclients.mock_http import MockHTTP import re import logging +import json from restclients.dao_implementation.live import get_con_pool, get_live_url from restclients.dao_implementation.mock import get_mockdata_url @@ -23,19 +24,42 @@ class File(object): RESTCLIENTS_IRWS_DAO_CLASS = 'restclients.dao_implementation.irws.File' """ + # cache of results to fake puts and posts in memory + _cache = {} + def getURL(self, url, headers): - return get_mockdata_url("irws", "file", url, headers) + if url in File._cache: + response = MockHTTP() + response.status = 200 + response.data = File._cache[url] + response.headers = {'Content-Type': 'application/json'} + else: + response = get_mockdata_url("irws", "file", url, headers) + File._cache[url] = response.data + return response def putURL(self, url, headers, body): response = MockHTTP() logger.debug('made it to putURL') - response.status = 200 - response.headers = {"X-Data-Source": "GWS file mock data", - "Content-Type": 'application/json'} - response.data = body + response.headers = {"Content-Type": 'application/json'} + if url in File._cache: + cache = json.loads(File._cache[url]) + request = json.loads(body) + type = request.keys()[0] + for attr in request[type][0].keys(): + cache[type][0][attr] = request[type][0][attr] + File._cache[url] = json.dumps(cache) + response.data = File._cache[url] + response.status = 200 + else: + response.data = body + response.status = 404 return response + def postURL(self, url, headers, body): + return self.putURL(url, headers, body) + class AlwaysJAverage(object): """ @@ -91,6 +115,12 @@ def putURL(self, url, headers, body): url, headers=headers, body=body, service_name='irws') + def postURL(self, url, headers, body): + return get_live_url(self.pool, 'POST', + settings.RESTCLIENTS_IRWS_HOST, + url, headers=headers, body=body, + service_name='irws') + _pool = None @property def pool(self): diff --git a/restclients/exceptions.py b/restclients/exceptions.py index eb31e0da..cc55a7c8 100644 --- a/restclients/exceptions.py +++ b/restclients/exceptions.py @@ -63,6 +63,14 @@ class InvalidIRWSName(Exception): """Exception for invalid IRWS name.""" pass +class InvalidIRWSPerson(Exception): + """Exception for invalid IRWS name.""" + pass + +class IRWSPersonNotFound(Exception): + """Exception for netids that don't exist""" + pass + class DataFailureException(Exception): """ This exception means there was an error fetching content diff --git a/restclients/irws.py b/restclients/irws.py index de304395..1146d173 100644 --- a/restclients/irws.py +++ b/restclients/irws.py @@ -11,7 +11,7 @@ from restclients.exceptions import InvalidRegID, InvalidNetID, InvalidEmployeeID from restclients.exceptions import InvalidIdCardPhotoSize from restclients.exceptions import DataFailureException -from restclients.exceptions import InvalidIRWSName +from restclients.exceptions import InvalidIRWSName, InvalidIRWSPerson, IRWSPersonNotFound from restclients.models.irws import Name, HeppsPerson, PersonIdentity from StringIO import StringIO from urllib import urlencode @@ -92,7 +92,7 @@ def get_hepps_person_by_uri(self, uri): If the record id isn't found, nothing will be returned. If there is an error communicating with the IRWS, a DataFailureException will be thrown. """ - url = "/%s/v1/%s" % (self._service_name, uri) + url = "/%s/v1%s" % (self._service_name, uri) response = IRWS_DAO().getURL(url, {"Accept": "application/json"}) if response.status != 200: @@ -100,6 +100,38 @@ def get_hepps_person_by_uri(self, uri): return self._hepps_person_from_json(response.data) + def get_hepps_person_by_netid(self, netid): + """ + Returns a restclients.irws.HeppsPerson object for a given netid. Two round + trips - one to get the identity, and a second to look up a person based on + the 'hepps' uri in the payload + """ + identity = self.get_identity_by_netid(netid) + if 'hepps' not in identity.identifiers.keys(): + raise IRWSPersonNotFound('netid ' + netid + ' not a hepps person') + return self.get_hepps_person_by_uri(identity.identifiers['hepps']) + + def post_hepps_person_by_netid(self, netid, data): + """ + Post to the irws person hepps resource. + We look up the person by netid to get the uri to post to. + """ + if not self.valid_uwnetid(netid): + raise InvalidNetID(netid) + hepps_person = self.valid_hepps_person_from_json(data) + identity = self.get_identity_by_netid(netid) + if 'hepps' not in identity.identifiers.keys(): + raise IRWSPersonNotFound('netid ' + netid + ' not a hepps person') + post_url = '/{}/v1{}'.format(self._service_name, + identity.identifiers['hepps']) + response = IRWS_DAO().postURL(post_url, + {'Accept': 'application/json'}, + json.dumps(hepps_person)) + if response.status != 200: + raise DataFailureException(post_url, response.status, response.data) + + return response.status + def valid_uwnetid(self, netid): uwnetid = str(netid) return (self._re_personal_netid.match(uwnetid) != None @@ -137,6 +169,26 @@ def valid_irws_name_from_json(self, data): return pd + def valid_hepps_person_from_json(self, data): + """ + Validate input of supported fields and return an + object that can be posted to irws + """ + post_person = {} + try: + data_person = json.loads(data) + post_person['wp_publish'] = data_person.pop('wp_publish') + if len(data_person.keys()) != 0: + logger.info('ignoring the following keys for post: {}'.format( + ', '.join(data_person.keys()))) + except: + raise InvalidIRWSPerson('invalid json') + + if post_person['wp_publish'] not in ('Y', 'N', 'E'): + raise InvalidIRWSPerson('wp_publish can only be Y, N, or E') + + return {'person': [post_person]} + def valid_name_part(self, name): return (len(name) <= 64 and self._re_name_part.match(name) != None) @@ -158,7 +210,7 @@ def _hepps_person_from_json(self, data): person.category_name = person_data['category_name'] if 'wp_publish' in person_data: person.wp_publish = person_data['wp_publish'] - else: person.wp_publish = 'Y' + else: person.wp_publish = 'N' # default to no return person def _name_from_json(self, data): diff --git a/restclients/resources/irws/file/registry-dev/v1/person/hepps/000210870 b/restclients/resources/irws/file/registry-dev/v1/person/hepps/000210870 new file mode 100644 index 00000000..a8b7bf9a --- /dev/null +++ b/restclients/resources/irws/file/registry-dev/v1/person/hepps/000210870 @@ -0,0 +1,28 @@ +{ + "person": [ + { + "validid": "000210870", + "regid": "28098ACBAC71425D9B2912757E4EF3AE", + "lname": "Harrison", + "fname": "Dolley", + "pac": "E", + "category_code": "4", + "category_name": "Staff", + "college": "99", + "department": ".TEST ", + "unit": "000", + "hepps_type": "E", + "hepps_status": "A", + "budget": "000001", + "source_code": "1", + "source_name": "UW Faculty/Staff", + "status_code": "1", + "status_name": "Current", + "in_feed": "2", + "logname": "registry2:getvalidations:NewID/69.91.223.171:anonymous@netid11.cac.washington.edu", + "created": "2015-05-14 16:01:58.671418", + "updated": "2015-05-14 16:52:52.452015" + } + ], + "totalcount": 1 +} diff --git a/restclients/resources/irws/file/registry-dev/v1/person_uwnetid_idtest55 b/restclients/resources/irws/file/registry-dev/v1/person_uwnetid_idtest55 new file mode 100644 index 00000000..4a5ddc2b --- /dev/null +++ b/restclients/resources/irws/file/registry-dev/v1/person_uwnetid_idtest55 @@ -0,0 +1,15 @@ +{ + "person": [ + { + "identity": { + "regid": "F533DB1751A34FF1878CA1C88A3D179C", + "lname": "Adams", + "fname": "Dwight", + "identifiers": { + "sdb": "/person/sdb/990100554" + } + } + } + ], + "totalcount": 1 +} diff --git a/restclients/resources/irws/file/registry-dev/v1/person_uwnetid_idtest56 b/restclients/resources/irws/file/registry-dev/v1/person_uwnetid_idtest56 new file mode 100644 index 00000000..a8a6a32a --- /dev/null +++ b/restclients/resources/irws/file/registry-dev/v1/person_uwnetid_idtest56 @@ -0,0 +1,15 @@ +{ + "person": [ + { + "identity": { + "regid": "28098ACBAC71425D9B2912757E4EF3AE", + "lname": "Harrison", + "fname": "Dolley", + "identifiers": { + "hepps": "/person/hepps/000210870" + } + } + } + ], + "totalcount": 1 +} diff --git a/restclients/test/irws.py b/restclients/test/irws.py index adcd0535..0462b97a 100644 --- a/restclients/test/irws.py +++ b/restclients/test/irws.py @@ -3,7 +3,9 @@ from django.test import TestCase, override_settings from django.conf import settings from restclients.irws import IRWS -from restclients.exceptions import InvalidIRWSName +from restclients.exceptions import InvalidIRWSName, DataFailureException, InvalidNetID +from restclients.exceptions import IRWSPersonNotFound, InvalidIRWSPerson +from restclients.dao_implementation.irws import File logger = logging.getLogger(__name__) @@ -11,12 +13,35 @@ @override_settings(RESTCLIENTS_IRWS_DAO_CLASS='restclients.dao_implementation.irws.File', RESTCLIENTS_IRWS_SERVICE_NAME='registry-dev') class IRWSTest(TestCase): + def setUp(self): + File._cache = {} def test_get_name_by_netid(self): irws = IRWS() name = irws.get_name_by_netid('javerage') self.assertEquals(name.display_cname, 'JAMES AVERAGE STUDENT') + def test_put_name_by_netid(self): + irws = IRWS() + # prime the cache first + irws.get_name_by_netid('javerage') + + response = irws.put_name_by_netid( + 'javerage', + self._json_name_from_tuple(('J', '', 'Student'))) + self.assertEquals(200, response) + name = irws.get_name_by_netid('javerage') + self.assertEquals('J', name.display_fname) + self.assertEquals('', name.display_mname) + self.assertEquals('Student', name.display_lname) + + def test_put_name_by_netid_no_user(self): + irws = IRWS() + self.assertRaises(DataFailureException, + irws.put_name_by_netid, + 'nonuser', + self._json_name_from_tuple(('J', '', 'Student'))) + def test_valid_name_part_good(self): irws = IRWS() self.assertTrue(irws.valid_name_part('james')) @@ -119,6 +144,80 @@ def test_valid_irws_name_too_long(self): irws.valid_irws_name_from_json, bad_name) + def test_get_identity_by_netid(self): + irws = IRWS() + identity = irws.get_identity_by_netid('javerage') + self.assertNotEqual(0, len(identity.identifiers.keys())) + + def test_get_identity_by_netid_bad_netid(self): + irws = IRWS() + self.assertRaises(InvalidNetID, + irws.get_identity_by_netid, + 'lkfajdslkjf#@$$@') + + def test_get_identity_by_netid_nonexistent(self): + irws = IRWS() + self.assertRaises(DataFailureException, + irws.get_identity_by_netid, + 'nonuser') + + def test_get_hepps_person_by_netid(self): + irws = IRWS() + # idtest56 is set up to be a hepps person + hepps_person = irws.get_hepps_person_by_netid('idtest56') + self.assertEqual('N', hepps_person.wp_publish) + self.assertEqual('28098ACBAC71425D9B2912757E4EF3AE', hepps_person.regid) + + def test_get_hepps_person_by_netid_not_hepps(self): + irws = IRWS() + # idtest55 is set up to NOT be a hepps person + self.assertRaises(IRWSPersonNotFound, + irws.get_hepps_person_by_netid, + 'idtest55') + + def test_post_hepps_person_by_netid(self): + irws = IRWS() + netid = 'idtest56' + # prime the cache + irws.get_hepps_person_by_netid(netid) + response = irws.post_hepps_person_by_netid( + netid, + '{"wp_publish": "E"}') + self.assertEqual(200, response) + identity = irws.get_hepps_person_by_netid(netid) + self.assertEqual('E', identity.wp_publish) + + def test_post_hepps_person_by_netid_not_hepps(self): + irws = IRWS() + self.assertRaises(IRWSPersonNotFound, + irws.post_hepps_person_by_netid, + 'idtest55', + '{"wp_publish": "E"}') + + def test_post_hepps_person_by_netid_data_failure(self): + self.assertRaises(DataFailureException, + IRWS().post_hepps_person_by_netid, + 'idtest56', + '{"wp_publish": "E"}') + + def test_valid_hepps_person_from_json(self): + person = IRWS().valid_hepps_person_from_json('{"wp_publish": "Y"}') + expected = json.dumps({'person': [{'wp_publish': 'Y'}]}) + self.assertEqual(expected, json.dumps(person)) + # test all the possible values + person = IRWS().valid_hepps_person_from_json('{"wp_publish": "N"}') + person = IRWS().valid_hepps_person_from_json('{"wp_publish": "E"}') + + def test_valid_hepps_person_from_json_bad_payload(self): + self.assertRaises(InvalidIRWSPerson, + IRWS().valid_hepps_person_from_json, + '{"wp_publish": "J"}') + + def test_valid_hepps_person_from_json_bad_json(self): + self.assertRaises(InvalidIRWSPerson, + IRWS().valid_hepps_person_from_json, + '{"wp_publish":') + def _json_name_from_tuple(self, x): return json.dumps({'display_fname': x[0], 'display_mname': x[1],