From 4273848563a9f91c93b27630cf0bf52631d53281 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 28 Apr 2016 12:08:13 -0400 Subject: [PATCH 001/118] Modify client controller post to accept new objects and updates from Salesforce. --- app/controllers/client.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/app/controllers/client.py b/app/controllers/client.py index e9ab583..b643e38 100644 --- a/app/controllers/client.py +++ b/app/controllers/client.py @@ -1,5 +1,7 @@ """Controllers for managing clients.""" from werkzeug.exceptions import BadRequest + +from app.lib.database import db from app.controllers.base import RestController from app.models.client import Client from app.forms.client import ClientForm @@ -13,3 +15,30 @@ class ClientController(RestController): def get_form(self, filter_data): """Return the client form.""" return ClientForm + + def post(self, data, filter_data): + """ Post a new model. + If the post is from salesforce use, insert if salesforce id + is new and update if it already exist. If post is not from + salesforce treat as regular Restful POST request + """ + if filter_data.get('form') == 'salesforce': + # Create/modify the client. + try: + model = db.session.query(Client)\ + .filter( + Client.sales_force_id == data['sales_force_id'])\ + .first() + except KeyError: + raise BadRequest( + 'When post a client from salesforce, please ' + + 'include its salesforce id.') + if model: + data.update({'id': model.id}) + model = self.put(model.id, data, filter_data) + else: + model = super(ClientController, self).post(data, filter_data) + else: + model = super(ClientController, self).post(data, filter_data) + + return model -- GitLab From 305a6d071a42cbbbe5775891584bec9d185379a9 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 28 Apr 2016 12:27:57 -0400 Subject: [PATCH 002/118] Fix reference to Model in query. --- app/controllers/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/client.py b/app/controllers/client.py index b643e38..fcad514 100644 --- a/app/controllers/client.py +++ b/app/controllers/client.py @@ -25,9 +25,9 @@ class ClientController(RestController): if filter_data.get('form') == 'salesforce': # Create/modify the client. try: - model = db.session.query(Client)\ + model = db.session.query(self.Model)\ .filter( - Client.sales_force_id == data['sales_force_id'])\ + self.Model.sales_force_id == data['sales_force_id'])\ .first() except KeyError: raise BadRequest( -- GitLab From 697f60a8548762bf1749c60512a6adce3cfeb0b4 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 28 Apr 2016 12:33:28 -0400 Subject: [PATCH 003/118] Add super class for objects that can update using post when hit from salesforce. --- app/controllers/base.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/app/controllers/base.py b/app/controllers/base.py index bbea4d7..4a2382c 100644 --- a/app/controllers/base.py +++ b/app/controllers/base.py @@ -138,3 +138,32 @@ class RestController(object): db.session.delete(model) commit() return {} + +class SalesforceObjectController(RestController): + + def post(self, data, filter_data): + """ Post a new model. + If the post is from salesforce, insert if salesforce id + is new and update if it already exist. If post is not from + salesforce treat as regular Restful POST request + """ + if filter_data.get('form') == 'salesforce': + # Create/modify the client. + try: + model = db.session.query(self.Model)\ + .filter( + Model.sales_force_id == data['sales_force_id'])\ + .first() + except KeyError: + raise BadRequest( + 'When posting an object from salesforce, please ' + + 'include its salesforce id.') + if model: + data.update({'id': model.id}) + model = self.put(model.id, data, filter_data) + else: + model = super(type(self), self).post(data, filter_data) + else: + model = super(type(self), self).post(data, filter_data) + + return model -- GitLab From 48196e9dc1ac95b5690f67c6ab9f74397845c2bd Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 28 Apr 2016 12:35:57 -0400 Subject: [PATCH 004/118] Change comment --- app/controllers/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/base.py b/app/controllers/base.py index 4a2382c..443ad78 100644 --- a/app/controllers/base.py +++ b/app/controllers/base.py @@ -148,7 +148,7 @@ class SalesforceObjectController(RestController): salesforce treat as regular Restful POST request """ if filter_data.get('form') == 'salesforce': - # Create/modify the client. + # Create/modify the object. try: model = db.session.query(self.Model)\ .filter( -- GitLab From bebc965211b5739374ca3edd75fec870f86693e9 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 28 Apr 2016 14:35:12 -0400 Subject: [PATCH 005/118] Add post method contact controller. --- app/controllers/contact.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/app/controllers/contact.py b/app/controllers/contact.py index 5045240..610ab48 100644 --- a/app/controllers/contact.py +++ b/app/controllers/contact.py @@ -24,6 +24,33 @@ class ContactController(RestController): """Return the contact form.""" return ContactForm + def post(self, data, filter_data): + """ Post a new model. + If the post is from salesforce use, insert if salesforce id + is new and update if it already exist. If post is not from + salesforce treat as regular Restful POST request + """ + if filter_data.get('form') == 'salesforce': + # Create/modify the client. + try: + model = db.session.query(self.Model)\ + .filter( + self.Model.sales_force_id == data['sales_force_id'])\ + .first() + except KeyError: + raise BadRequest( + 'When post a client from salesforce, please ' + + 'include its salesforce id.') + if model: + data.update({'id': model.id}) + model = self.put(model.id, data, filter_data) + else: + model = super(ContactController, self).post(data, filter_data) + else: + model = super(ContactController, self).post(data, filter_data) + + return model + class ContactMethodController(RestController): """The contact method controller.""" -- GitLab From 5eae5a85429a1eb6bed2dc30f02847e7a9cb291b Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 28 Apr 2016 14:35:27 -0400 Subject: [PATCH 006/118] Allow salesforce to update from salesforcein post method. --- app/controllers/project.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/controllers/project.py b/app/controllers/project.py index e79477c..5488490 100644 --- a/app/controllers/project.py +++ b/app/controllers/project.py @@ -108,7 +108,20 @@ class ProjectController(RestController): place = PlaceController().post(place_data, {}) data.update({'place_id': place.id}) - model = super(ProjectController, self).post(data, filter_data) + try: + model = db.session.query(self.Model)\ + .filter( + self.Model.sales_force_id == data['sales_force_id'])\ + .first() + except KeyError: + raise BadRequest( + 'When post a client from salesforce, please ' + + 'include its salesforce id.') + if model: + data.update({'id': model.id}) + model = self.put(model.id, data, filter_data) + else: + model = super(ProjectController, self).post(data, filter_data) if filter_data.get('form') == 'salesforce': try: -- GitLab From d3eb871e97e68f40b1dc3745189f2527dea3867c Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 28 Apr 2016 15:08:38 -0400 Subject: [PATCH 007/118] Add post method place controller. --- app/controllers/place.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/app/controllers/place.py b/app/controllers/place.py index b870f7c..0f69865 100644 --- a/app/controllers/place.py +++ b/app/controllers/place.py @@ -15,6 +15,33 @@ class PlaceController(RestController): """Return the Place form.""" return PlaceForm + def post(self, data, filter_data): + """ Post a new model. + If the post is from salesforce use, insert if salesforce id + is new and update if it already exist. If post is not from + salesforce treat as regular Restful POST request + """ + if filter_data.get('form') == 'salesforce': + # Create/modify the client. + try: + model = db.session.query(self.Model)\ + .filter( + self.Model.sales_force_id == data['sales_force_id'])\ + .first() + except KeyError: + raise BadRequest( + 'When post a client from salesforce, please ' + + 'include its salesforce id.') + if model: + data.update({'id': model.id}) + model = self.put(model.id, data, filter_data) + else: + model = super(PlaceController, self).post(data, filter_data) + else: + model = super(PlaceController, self).post(data, filter_data) + + return model + class AddressController(RestController): """The Address controller.""" -- GitLab From a31f0cbd94b8ec085bcc203b2317829259d121e4 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 28 Apr 2016 15:40:01 -0400 Subject: [PATCH 008/118] Add post method note controller. --- app/controllers/note.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/app/controllers/note.py b/app/controllers/note.py index 829ed51..7b2ff4a 100644 --- a/app/controllers/note.py +++ b/app/controllers/note.py @@ -13,3 +13,30 @@ class NoteController(RestController): def get_form(self, filter_data): """Return the note form.""" return NoteForm + + def post(self, data, filter_data): + """ Post a new model. + If the post is from salesforce use, insert if salesforce id + is new and update if it already exist. If post is not from + salesforce treat as regular Restful POST request + """ + if filter_data.get('form') == 'salesforce': + # Create/modify the client. + try: + model = db.session.query(self.Model)\ + .filter( + self.Model.sales_force_id == data['sales_force_id'])\ + .first() + except KeyError: + raise BadRequest( + 'When post a client from salesforce, please ' + + 'include its salesforce id.') + if model: + data.update({'id': model.id}) + model = self.put(model.id, data, filter_data) + else: + model = super(NoteController, self).post(data, filter_data) + else: + model = super(NoteController, self).post(data, filter_data) + + return model -- GitLab From aa478926d8647ff2248f4f65d7c043554f185253 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 28 Apr 2016 18:06:54 -0400 Subject: [PATCH 009/118] Fix place to process embedded address. --- app/controllers/place.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/controllers/place.py b/app/controllers/place.py index 0f69865..8adc9f9 100644 --- a/app/controllers/place.py +++ b/app/controllers/place.py @@ -22,6 +22,12 @@ class PlaceController(RestController): salesforce treat as regular Restful POST request """ if filter_data.get('form') == 'salesforce': + try: + address_data = data['address'] + except KeyError: + raise BadRequest( + 'Salesforce place requires an embedded address please.') + # Create/modify the client. try: model = db.session.query(self.Model)\ @@ -35,7 +41,13 @@ class PlaceController(RestController): if model: data.update({'id': model.id}) model = self.put(model.id, data, filter_data) + address = model.address + address_data.update({'id': address.id}) + address = AddressController().put(address.id, address_data, {}) + data.update({'address_id': address.id}) else: + address = AddressController().post(address_data, {}) + data.update({'address_id': address.id}) model = super(PlaceController, self).post(data, filter_data) else: model = super(PlaceController, self).post(data, filter_data) -- GitLab From a7cff7315954315dfc74ca353d9ebd94f9bca7f3 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Wed, 4 May 2016 12:20:05 -0400 Subject: [PATCH 010/118] Add salesforce create and update tests. --- app/tests/test_note.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/app/tests/test_note.py b/app/tests/test_note.py index e15d689..6f0d504 100644 --- a/app/tests/test_note.py +++ b/app/tests/test_note.py @@ -159,3 +159,44 @@ class TestNote(RestTestCase): """Tests /note/ DELETE.""" model = self.env.note self._test_delete(model.id) + +class TestSalesForceNoteAuth(UnprotectedRestTestCase): + """Tests authentication /note/?form=salesforce POST endpoint.""" + url = '/note/' + Model = Note + +class TestSalesForceNote(UnprotectedRestTestCase): + """Tests the /note/?form=salesforce POST endpoint.""" + url = '/note/' + Model = Note + + @property + def global_headers(self): + """Add app headers to authenticate a salesforce client.""" + return { + services.config['headers']['app_key']: \ + self.app.config['SALESFORCE_KEY'], + 'referer': self.app.config['SALESFORCE_REFERRER']} + + def test_create(self): + """Tests /project/?form=salesforce POST with a note.""" + data = { + 'sales_force_id': 'test', + 'project_id': self.env.project.id, + 'posted': arrow.get().isoformat(), + 'title': 'Test', + 'content': 'This is a test note.', + 'poster_name': 'test'} + self._test_post(data, {'form': 'salesforce'}) + + def test_update(self): + """Tests /project/?form=salesforce POST with a note.""" + model = self.env.note_with_name + data = { + 'sales_force_id': model.sales_force_id, + 'project_id': model.project_id, + 'posted': model.posted, + 'title': model.title, + 'content': 'This is an update of test note.', + 'poster_name': model.poster_name} + self._test_post(data, {'form': 'salesforce'}) -- GitLab From cd2e9d1534edf61fe8ec4515f5dbc4ed2a82734a Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Wed, 4 May 2016 16:27:21 -0400 Subject: [PATCH 011/118] Refactor project contact controller and contact controller to accept salesforce post. --- app/controllers/contact.py | 82 +++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/app/controllers/contact.py b/app/controllers/contact.py index 610ab48..2f9f9b3 100644 --- a/app/controllers/contact.py +++ b/app/controllers/contact.py @@ -1,7 +1,9 @@ """Controllers for managing contacts.""" +from sqlalchemy import and_ from werkzeug.exceptions import BadRequest from app.controllers.base import RestController from app.models.contact import Contact, ContactMethod, ProjectContact +from app.models.project import Project from app.forms.contact import ContactForm, ContactMethodForm, ProjectContactForm @@ -27,11 +29,18 @@ class ContactController(RestController): def post(self, data, filter_data): """ Post a new model. If the post is from salesforce use, insert if salesforce id - is new and update if it already exist. If post is not from + is new and update if it already exist. If post is not from salesforce treat as regular Restful POST request """ if filter_data.get('form') == 'salesforce': - # Create/modify the client. + # Create/modify the contact with embedded contact methods. + try: + contact_methods_data = data['contact_methods'] + except KeyError: + raise BadRequest( + 'When posting a contact from salesforce embed contact ' + + 'methods') + try: model = db.session.query(self.Model)\ .filter( @@ -46,6 +55,22 @@ class ContactController(RestController): model = self.put(model.id, data, filter_data) else: model = super(ContactController, self).post(data, filter_data) + + for contact_method_data in contact_methods_data: + contact_method = db.session.query(ContactMethod)\ + .filter(and_( + ContactMethod.contact_id == model.id, + ContactMethod.method == \ + contact_method_data['method'], + ContactMethod.value == \ + contact_method_data['value']))\ + .first() + if not contact_method: + contact_method_data.update({'contact_id': model.id}) + contact_method = ContactMethodController().post( + contact_method_data, + {}) + else: model = super(ContactController, self).post(data, filter_data) @@ -80,3 +105,56 @@ class ProjectContactController(RestController): def get_form(self, filter_data): """Return the project contact form.""" return ProjectContactForm + + def post(self, data, filter_data): + """ Post a new model. + If the post is from salesforce use, insert if salesforce id + is new and update if it already exist. If post is not from + salesforce treat as regular Restful POST request + """ + if filter_data.get('form') == 'salesforce': + # Create/modify the project contact with embeded contact and + # contact methods. + + try: + contact_data = data['contact'] + contact_methods_data = data['contact_methods'] + project_data = data['project'] + except KeyError: + raise BadRequest( + 'When posting a project contact from salesforce embed ' + + 'contacts and contact methods') + + contact_data.update({'contact_methods': contact_methods_data}) + contact = ContactController.post( + contact_data, + {'form': filter_data.get('form')}) + + try: + project = db.session.query(Project)\ + .filter(Project.sales_force_id == project_data['sales_force_id'])\ + .first() + except KeyError: + raise BadRequest( + 'When posting a project contact from salesforce embed ' + + 'project salesforce id.') + + if not project: + raise BadRequest('Salesforce opportunity with the following id '+ + project_data['sales_force_id']+' not in sync with project service.') + + model = db.session.query(self.Model)\ + .filter(_and( + self.Model.contact_id == contact.id , + self.Model.project_id == project.id,))\ + .first() + + if model: + data.update({'id': model.id}) + model = self.put(model.id, data, filter_data) + else: + model = super(ProjectContactController, self).post(data, filter_data) + else: + model = super(ProjectContactController, self).post(data, filter_data) + + return model -- GitLab From 530261260ed4e41985b583ca1da8551b87586bc5 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Wed, 4 May 2016 16:41:48 -0400 Subject: [PATCH 012/118] Update project contact object to add project and contact id. --- app/controllers/contact.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/controllers/contact.py b/app/controllers/contact.py index 2f9f9b3..149a465 100644 --- a/app/controllers/contact.py +++ b/app/controllers/contact.py @@ -130,6 +130,8 @@ class ProjectContactController(RestController): contact_data, {'form': filter_data.get('form')}) + data.update({'contact_id': contact.id}) + try: project = db.session.query(Project)\ .filter(Project.sales_force_id == project_data['sales_force_id'])\ @@ -140,13 +142,16 @@ class ProjectContactController(RestController): 'project salesforce id.') if not project: - raise BadRequest('Salesforce opportunity with the following id '+ - project_data['sales_force_id']+' not in sync with project service.') + raise BadRequest( + 'Opportunity out of sync. When posting a project contact from salesforce project ' + + 'has to be tracked by the project service.') + + data.update({'project_id': project.id}) model = db.session.query(self.Model)\ .filter(_and( self.Model.contact_id == contact.id , - self.Model.project_id == project.id,))\ + self.Model.project_id == project.id))\ .first() if model: -- GitLab From f9ce29f3287a4b38fb0f14bf58e6f31d7a1577c4 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Wed, 4 May 2016 17:36:56 -0400 Subject: [PATCH 013/118] Add tests methods for Contact post from salesforce. --- app/tests/test_contact.py | 92 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/app/tests/test_contact.py b/app/tests/test_contact.py index 76af056..463cd06 100644 --- a/app/tests/test_contact.py +++ b/app/tests/test_contact.py @@ -358,3 +358,95 @@ class TestProjectContact(RestTestCase): """Tests /contact/project/ DELETE.""" model = self.env.project_contact self._test_delete(model.id) + +class TestSalesForceContactAuth(UnprotectedRestTestCase): + """Tests authentication /contact/?form=salesforce POST endpoint.""" + url = '/contact/' + Model = Contact + +class TestSalesForceContact(UnprotectedRestTestCase): + """Tests the /note/?form=salesforce POST endpoint.""" + url = '/contact/' + Model = Contact + + @property + def global_headers(self): + """Add app headers to authenticate a salesforce client.""" + return { + services.config['headers']['app_key']: \ + self.app.config['SALESFORCE_KEY'], + 'referer': self.app.config['SALESFORCE_REFERRER']} + + def test_create(self): + """Tests /contact/?form=salesforce POST with new contact.""" + data = { + 'sales_force_id': 'test', + 'name': 'test_name', + 'contact_methods': + [ + ContactMethod(method='sms', value='8885555555').get_dictionary(), + ContactMethod(method='fax', value='8886666666').get_dictionary() + ] + } + self._test_post(data, {'form': 'salesforce'}) + + def test_update(self): + """Tests /contact/?form=salesforce POST with existing object.""" + model = self.env.contact + data = { + 'sales_force_id': model.sales_force_id, + 'name': model.project_id, + 'posted': model.posted, + 'title': model.title, + 'content': 'This is an update of test note.', + 'poster_name': model.poster_name} + self._test_post(data, {'form': 'salesforce'}) + +class TestSalesForceProjectContactAuth(UnprotectedRestTestCase): + """Tests authentication /contact/?form=salesforce POST endpoint.""" + url = '/contact/project/' + Model = ProjectContact + +class TestSalesForceProjectContact(UnprotectedRestTestCase): + """Tests the /note/?form=salesforce POST endpoint.""" + url = '/contact/project/' + Model = ProjectContact + + @property + def global_headers(self): + """Add app headers to authenticate a salesforce client.""" + return { + services.config['headers']['app_key']: \ + self.app.config['SALESFORCE_KEY'], + 'referer': self.app.config['SALESFORCE_REFERRER']} + + def test_create(self): + """Tests /contact/project/?form=salesforce POST with new contact.""" + project = self.env.project + data = { + 'contact': Contact(name='test').get_dictionary(), + 'project': {"sales_force_id": project.sales_force_id}, + 'contact_methods': + [ + ContactMethod(method='sms', value='8885555555').get_dictionary(), + ContactMethod(method='fax', value='8886666666').get_dictionary() + ] + } + self._test_post(data, {'form': 'salesforce'}) + + def test_update(self): + """Tests /contact/project/?form=salesforce POST with existing object.""" + model = self.env.project_contact + contact = db.session.query(Contact)\ + .filter(Contact.id == model.contact_id)\ + .first() + + project = db.session.query(Project)\ + .filter(Project.id == model.project_id)\ + .first() + + data = { + 'contact': model.sales_force_id, + 'project': model.project_id, + 'contact_methods': model.posted} + self._test_post(data, {'form': 'salesforce'}) -- GitLab From 7900b37d348188cb52ab891fd9220c70b1a38f54 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Wed, 4 May 2016 22:59:53 -0400 Subject: [PATCH 014/118] Refactor project post to only post embedded objects on creation. --- app/controllers/project.py | 184 +++++++++++-------------------------- 1 file changed, 56 insertions(+), 128 deletions(-) diff --git a/app/controllers/project.py b/app/controllers/project.py index 5488490..e42a703 100644 --- a/app/controllers/project.py +++ b/app/controllers/project.py @@ -57,147 +57,75 @@ class ProjectController(RestController): # before posting the project when performing a bulk upload. if filter_data.get('form') == 'salesforce': try: - client_data = data['client'] - place_data = data['place'] - address_data = data['address'] - except KeyError: - raise BadRequest( - 'SalesForce projects require an embedded client, place, ' + - 'and address.') - - # Create/modify the client. - try: - client = db.session.query(Client)\ + model = db.session.query(self.Model)\ .filter( - Client.sales_force_id == client_data['sales_force_id'])\ + self.Model.sales_force_id == data['sales_force_id'])\ .first() except KeyError: raise BadRequest( - 'When embedding a client in a salesforce project, please ' + + 'When posting a project from salesforce, please ' + 'include its salesforce id.') - if client: - client_data.update({'id': client.id}) - client = ClientController().put(client.id, client_data, {}) - else: - client = ClientController().post(client_data, {}) - data.update({'client_id': client.id}) - # Create/modify the place and address. - try: - place = db.session.query(Place)\ - .filter( - Place.sales_force_id == place_data['sales_force_id'])\ - .first() - except KeyError: - raise BadRequest( - 'When embedding a place in a salesforce project, please ' + - 'include its salesforce id.') - - address = place.address if place else None - if address: - address_data.update({'id': address.id}) - address = AddressController().put(address.id, address_data, {}) + if model: + data.update({'id': model.id}) + model = self.put(model.id, data, filter_data) else: - address = AddressController().post(address_data, {}) - place_data.update({'address_id': address.id}) - - if place: - place_data.update({'id': place.id}) - place = PlaceController().put(place.id, place_data, {}) - else: - place = PlaceController().post(place_data, {}) - data.update({'place_id': place.id}) - - try: - model = db.session.query(self.Model)\ - .filter( - self.Model.sales_force_id == data['sales_force_id'])\ - .first() - except KeyError: - raise BadRequest( - 'When post a client from salesforce, please ' + - 'include its salesforce id.') - if model: - data.update({'id': model.id}) - model = self.put(model.id, data, filter_data) - else: - model = super(ProjectController, self).post(data, filter_data) - - if filter_data.get('form') == 'salesforce': - try: try: - contacts_data = data['contacts'] - contact_methods_data = data['contact_methods'] - notes_data = data['notes'] + client_data = data['client'] + place_data = data['place'] + address_data = data['address'] except KeyError: raise BadRequest( - 'SalesForce projects require embedded contacts.') + 'SalesForce projects require an embedded client, place, ' + + 'and address.') - for contact_data in contacts_data: - # Create the contact if it does not exist. - contact = db.session.query(Contact)\ - .filter(Contact.sales_force_id == \ - contact_data['sales_force_id'])\ - .first() - if contact: - contact_data.update({'id':contact.id}) - contact = ContactController().put( - contact.id, contact_data, {}) - else: - contact = ContactController().post(contact_data, {}) - - # Associate the contact with the project. - project_contact = db.session.query(ProjectContact)\ - .filter(and_( - ProjectContact.project_id == model.id, - ProjectContact.contact_id == contact.id))\ - .first() - if not project_contact: - ProjectContactController().post( - {'project_id': model.id, 'contact_id': contact.id}, - {}) - - for contact_method_data in contact_methods_data: - # Grab the contact by salesforce id. - contact = db.session.query(Contact)\ - .join(ProjectContact, - ProjectContact.contact_id == Contact.id)\ - .join(Project, Project.id == ProjectContact.project_id)\ - .filter(and_( - Project.id == model.id, - Contact.sales_force_id == \ - contact_method_data['contact_sales_force_id']))\ + # Create/modify the client. + try: + client = db.session.query(Client)\ + .filter( + Client.sales_force_id == client_data['sales_force_id'])\ .first() - if not contact: - raise BadRequest( - 'Contact method references a contact that is not ' + - 'associated with the project.') - - contact_method = db.session.query(ContactMethod)\ - .filter(and_( - ContactMethod.contact_id == contact.id, - ContactMethod.method == \ - contact_method_data['method'], - ContactMethod.value == \ - contact_method_data['value']))\ + except KeyError: + raise BadRequest( + 'When embedding a client in a salesforce project, please ' + + 'include its salesforce id.') + if client: + client_data.update({'id': client.id}) + client = ClientController().put(client.id, client_data, {}) + else: + client = ClientController().post(client_data, {}) + data.update({'client_id': client.id}) + + # Create/modify the place and address. + try: + place = db.session.query(Place)\ + .filter( + Place.sales_force_id == place_data['sales_force_id'])\ .first() - if not contact_method: - contact_method_data.update({'contact_id': contact.id}) - contact_method = ContactMethodController().post( - contact_method_data, - {}) - - for note_data in notes_data: - # Create the note. We know it doesn't exist previously - # because the project didn't exist previously. - note_data.update({'project_id': model.id}) - note = NoteController().post(note_data, {}) - - except BadRequest as e: - # Do not keep the project if contacts failed to upload. - db.session.delete(model) - self.commit() - raise e + except KeyError: + raise BadRequest( + 'When embedding a place in a salesforce project, please ' + + 'include its salesforce id.') + + address = place.address if place else None + if address: + address_data.update({'id': address.id}) + address = AddressController().put(address.id, address_data, {}) + else: + address = AddressController().post(address_data, {}) + place_data.update({'address_id': address.id}) + + if place: + place_data.update({'id': place.id}) + place = PlaceController().put(place.id, place_data, {}) + else: + place = PlaceController().post(place_data, {}) + data.update({'place_id': place.id}) + + model = super(ProjectController, self).post(data, filter_data) + + else: + model = super(ProjectController, self).post(data, filter_data) self.log_state_change(model) return model -- GitLab From c53028cda6cf7872b43b396f2a0ca99e097cb38a Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 5 May 2016 00:20:57 -0400 Subject: [PATCH 015/118] Fix copying errors --- app/controllers/note.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/note.py b/app/controllers/note.py index 7b2ff4a..b7518f2 100644 --- a/app/controllers/note.py +++ b/app/controllers/note.py @@ -16,12 +16,12 @@ class NoteController(RestController): def post(self, data, filter_data): """ Post a new model. - If the post is from salesforce use, insert if salesforce id + When the post is from salesforce use insert if salesforce id is new and update if it already exist. If post is not from salesforce treat as regular Restful POST request """ if filter_data.get('form') == 'salesforce': - # Create/modify the client. + # Create/modify the note. try: model = db.session.query(self.Model)\ .filter( @@ -29,7 +29,7 @@ class NoteController(RestController): .first() except KeyError: raise BadRequest( - 'When post a client from salesforce, please ' + + 'When post a note from salesforce, please ' + 'include its salesforce id.') if model: data.update({'id': model.id}) -- GitLab From 6efd1092ecbb1007c35228718f89bd03f5fb4402 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 5 May 2016 00:22:35 -0400 Subject: [PATCH 016/118] Fix copying errors on test class --- app/tests/test_note.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/tests/test_note.py b/app/tests/test_note.py index 6f0d504..02cea97 100644 --- a/app/tests/test_note.py +++ b/app/tests/test_note.py @@ -179,7 +179,7 @@ class TestSalesForceNote(UnprotectedRestTestCase): 'referer': self.app.config['SALESFORCE_REFERRER']} def test_create(self): - """Tests /project/?form=salesforce POST with a note.""" + """Tests /note/?form=salesforce POST with a note.""" data = { 'sales_force_id': 'test', 'project_id': self.env.project.id, @@ -190,7 +190,7 @@ class TestSalesForceNote(UnprotectedRestTestCase): self._test_post(data, {'form': 'salesforce'}) def test_update(self): - """Tests /project/?form=salesforce POST with a note.""" + """Tests /note/?form=salesforce POST with a note.""" model = self.env.note_with_name data = { 'sales_force_id': model.sales_force_id, -- GitLab From cd6bd67e27313d19fb30351df7d95daa0e63d6c2 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 5 May 2016 00:23:57 -0400 Subject: [PATCH 017/118] Fix copying errors on place controller. --- app/controllers/place.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/place.py b/app/controllers/place.py index 8adc9f9..6244093 100644 --- a/app/controllers/place.py +++ b/app/controllers/place.py @@ -28,7 +28,7 @@ class PlaceController(RestController): raise BadRequest( 'Salesforce place requires an embedded address please.') - # Create/modify the client. + # Create/modify the place. try: model = db.session.query(self.Model)\ .filter( @@ -36,7 +36,7 @@ class PlaceController(RestController): .first() except KeyError: raise BadRequest( - 'When post a client from salesforce, please ' + + 'When post a place from salesforce, please ' + 'include its salesforce id.') if model: data.update({'id': model.id}) -- GitLab From 83cce6181c8e3c8d414f5458fd32efd3115f4ef0 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 5 May 2016 00:26:30 -0400 Subject: [PATCH 018/118] Add tests for place post from salesforce. --- app/tests/test_place.py | 44 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/app/tests/test_place.py b/app/tests/test_place.py index a2c12d4..6a6c4d8 100644 --- a/app/tests/test_place.py +++ b/app/tests/test_place.py @@ -239,3 +239,47 @@ class TestAddress(RestTestCase): model = self.env.address response = self.delete(self.url + str(model.id)) self.assertEqual(response.status_code, 405) + +class TestSalesForcePlace(UnprotectedRestTestCase): + """Tests the /place/?form=salesforce POST endpoint.""" + url = '/place/' + Model = Place + + @property + def global_headers(self): + """Add app headers to authenticate a salesforce client.""" + return { + services.config['headers']['app_key']: \ + self.app.config['SALESFORCE_KEY'], + 'referer': self.app.config['SALESFORCE_REFERRER']} + + def test_create(self): + """Tests /place/?form=salesforce POST with an address.""" + data = { + 'sales_force_id': 'test', + 'name': 'test place', + 'address': Address( + street_address='15 MetroTech Center', + city='Brooklyn', + county='Kings', + state='NY', + country='United States of America', + postal_code='11210').get_dictionary() + } + self._test_post(data, {'form': 'salesforce'}) + + def test_update(self): + """Tests /place/?form=salesforce POST with an address.""" + model = self.env.place + data = { + 'sales_force_id': model.sales_force_id, + 'name': model.name, + 'address': Address( + street_address='2 MetroTech Center', + city='Brooklyn', + county='Kings', + state='NY', + country='United States of America', + postal_code='11210').get_dictionary() + } + self._test_post(data, {'form': 'salesforce'}) -- GitLab From 7257aae3c875ab484abafd59ccb3fa185799b284 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 5 May 2016 10:45:43 -0400 Subject: [PATCH 019/118] Add salesforce tests for place --- app/tests/test_place.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/tests/test_place.py b/app/tests/test_place.py index 6a6c4d8..e536324 100644 --- a/app/tests/test_place.py +++ b/app/tests/test_place.py @@ -240,6 +240,11 @@ class TestAddress(RestTestCase): response = self.delete(self.url + str(model.id)) self.assertEqual(response.status_code, 405) +class TestSalesForcePlaceAuth(UnprotectedRestTestCase): + """Tests authentication /place/?form=salesforce POST endpoint.""" + url = '/place/' + Model = Place + class TestSalesForcePlace(UnprotectedRestTestCase): """Tests the /place/?form=salesforce POST endpoint.""" url = '/place/' -- GitLab From e8a4c831b34fca44e677c0e327c5955be6ba41c0 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 5 May 2016 10:45:43 -0400 Subject: [PATCH 020/118] Add salesforce tests for place --- app/tests/test_place.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/app/tests/test_place.py b/app/tests/test_place.py index 6a6c4d8..e402542 100644 --- a/app/tests/test_place.py +++ b/app/tests/test_place.py @@ -240,6 +240,11 @@ class TestAddress(RestTestCase): response = self.delete(self.url + str(model.id)) self.assertEqual(response.status_code, 405) +class TestSalesForcePlaceAuth(UnprotectedRestTestCase): + """Tests authentication /place/?form=salesforce POST endpoint.""" + url = '/place/' + Model = Place + class TestSalesForcePlace(UnprotectedRestTestCase): """Tests the /place/?form=salesforce POST endpoint.""" url = '/place/' @@ -268,6 +273,18 @@ class TestSalesForcePlace(UnprotectedRestTestCase): } self._test_post(data, {'form': 'salesforce'}) + def test_post_sf_new_place_no_address(self): + """ Tests /place/?form=salesforce without an address""" + data = { + 'sales_force_id': 'test', + 'name': 'test place', + } + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + def test_update(self): """Tests /place/?form=salesforce POST with an address.""" model = self.env.place @@ -283,3 +300,17 @@ class TestSalesForcePlace(UnprotectedRestTestCase): postal_code='11210').get_dictionary() } self._test_post(data, {'form': 'salesforce'}) + + + def test_post_sf_update_place_no_address(self): + """ Tests /place/?form=salesforce without an address""" + model = self.env.place + data = { + 'sales_force_id': model.sales_force_id, + 'name': 'test change place' + } + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) -- GitLab From fe917249086ef076a1d863a878e0ce77dd754aae Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 5 May 2016 11:58:29 -0400 Subject: [PATCH 021/118] Add place controller. --- app/controllers/place.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/app/controllers/place.py b/app/controllers/place.py index 6244093..d64ab4b 100644 --- a/app/controllers/place.py +++ b/app/controllers/place.py @@ -19,7 +19,10 @@ class PlaceController(RestController): """ Post a new model. If the post is from salesforce use, insert if salesforce id is new and update if it already exist. If post is not from - salesforce treat as regular Restful POST request + salesforce treat as regular Restful POST request. + + Place always has an embedded address when coming from salesforce + so we save the address object and then the location object. """ if filter_data.get('form') == 'salesforce': try: @@ -38,16 +41,19 @@ class PlaceController(RestController): raise BadRequest( 'When post a place from salesforce, please ' + 'include its salesforce id.') - if model: - data.update({'id': model.id}) - model = self.put(model.id, data, filter_data) - address = model.address + + address = place.address if place else None + if address: address_data.update({'id': address.id}) address = AddressController().put(address.id, address_data, {}) - data.update({'address_id': address.id}) else: address = AddressController().post(address_data, {}) - data.update({'address_id': address.id}) + place_data.update({'address_id': address.id}) + + if model: + data.update({'id': model.id}) + model = self.put(model.id, data, filter_data) + else: model = super(PlaceController, self).post(data, filter_data) else: model = super(PlaceController, self).post(data, filter_data) -- GitLab From 133635cdea9e4114049689ff4a3769c400bc2540 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 5 May 2016 11:59:39 -0400 Subject: [PATCH 022/118] Modify comment on project controller. --- app/controllers/project.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/controllers/project.py b/app/controllers/project.py index e42a703..8b7b101 100644 --- a/app/controllers/project.py +++ b/app/controllers/project.py @@ -45,13 +45,6 @@ class ProjectController(RestController): This logs a model state change after saving a new model. It includes support for salesforce, which dumps all submodels (e.g client, contacts, ...) in addition to the model itself. - - Behavior with the embedded models is a little odd. Rather than - validating everything up front, we validate as we go. Except for - the project itself, an object will successfully save if all - subsequent saves succeeded. For example, this means that a client - may be created even if the request 400s due to a problem with the - project itself. """ # A project has to have a client and a place, so we create these models # before posting the project when performing a bulk upload. @@ -123,7 +116,6 @@ class ProjectController(RestController): data.update({'place_id': place.id}) model = super(ProjectController, self).post(data, filter_data) - else: model = super(ProjectController, self).post(data, filter_data) -- GitLab From 137c567eb8e754a7fc853b0138697483c7b426b7 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 5 May 2016 13:12:27 -0400 Subject: [PATCH 023/118] Fix tests for project contact create and update post from salesforce --- app/tests/test_contact.py | 122 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 117 insertions(+), 5 deletions(-) diff --git a/app/tests/test_contact.py b/app/tests/test_contact.py index 463cd06..4e539ce 100644 --- a/app/tests/test_contact.py +++ b/app/tests/test_contact.py @@ -424,7 +424,7 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): """Tests /contact/project/?form=salesforce POST with new contact.""" project = self.env.project data = { - 'contact': Contact(name='test').get_dictionary(), + 'contact': Contact(name='test',sales_force_id="testId").get_dictionary(), 'project': {"sales_force_id": project.sales_force_id}, 'contact_methods': [ @@ -434,8 +434,39 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): } self._test_post(data, {'form': 'salesforce'}) + def test_post_sf_new_project_contact_no_contact_methods(self): + """Tests /contact/project?form=salesforce POST new project contact without contact methods""" + project = self.env.project + data = { + 'contact': Contact(name='test').get_dictionary(), + 'project': {"sales_force_id": project.sales_force_id} + } + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_post_sf_new_project_contact_no_contact(self): + """Tests /contact/project?form=salesforce POST new project contact without contact methods""" + project = self.env.project + data = { + 'project': {"sales_force_id": project.sales_force_id}, + 'contact_methods': + [ + ContactMethod(method='sms', value='8885555555').get_dictionary(), + ContactMethod(method='fax', value='8886666666').get_dictionary() + ] + } + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_update(self): - """Tests /contact/project/?form=salesforce POST with existing object.""" + """Tests /contact/project/?form=salesforce POST with existing project contact.""" model = self.env.project_contact contact = db.session.query(Contact)\ .filter(Contact.id == model.contact_id)\ @@ -445,8 +476,89 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): .filter(Project.id == model.project_id)\ .first() + project_data = {'sales_force_id' : project.sales_force_id} + + contact_methods = db.session.query(ContactMethod)\ + .filter(ContactMethod.contact_id == contact.id)\ + .all() + + contact_method_data = [] + for contact_method in contact_methods: + contact_method_object = contact_method.get_dictionary() + contact_method_object.pop("id") + contact_method_data.append(contact_method_object) + + data = { - 'contact': model.sales_force_id, - 'project': model.project_id, - 'contact_methods': model.posted} + 'contact': contact.get_dictionary(), + 'project': project_data, + 'contact_methods': contact_method_data} self._test_post(data, {'form': 'salesforce'}) + + def test_post_sf_update_project_contact_no_contact_methods(self): + """Tests /contact/project?form=salesforce POST updating project contact without contact methods""" + model = self.env.project_contact + contact = db.session.query(Contact)\ + .filter(Contact.id == model.contact_id)\ + .first() + + project = db.session.query(Project)\ + .filter(Project.id == model.project_id)\ + .first() + project_data = {'sales_force_id' : project.sales_force_id} + + data = { + 'contact': contact.get_dictionary(), + 'project': project_data} + + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_post_sf_update_project_contact_no_contact(self): + """Tests /contact/project?form=salesforce POST updating project contact without contact methods""" + model = self.env.project_contact + project = db.session.query(Project)\ + .filter(Project.id == model.project_id)\ + .first() + data = { + 'project': {"sales_force_id": project.sales_force_id}, + 'contact_methods': + [ + ContactMethod(method='sms', value='8885555555').get_dictionary(), + ContactMethod(method='fax', value='8886666666').get_dictionary() + ] + } + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_post_sf_update_project_contact_no_project(self): + """Tests /contact/project/?form=salesforce POST with existing project contact.""" + model = self.env.project_contact + contact = db.session.query(Contact)\ + .filter(Contact.id == model.contact_id)\ + .first() + + contact_methods = db.session.query(ContactMethod)\ + .filter(ContactMethod.contact_id == contact.id)\ + .all() + + contact_method_data = [] + for contact_method in contact_methods: + contact_method_object = contact_method.get_dictionary() + contact_method_object.pop("id") + contact_method_data.append(contact_method_object) + + data = { + 'contact': contact.get_dictionary(), + 'contact_methods': contact_method_data} + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) -- GitLab From 8ae67b9715ca8dc2623498e2b164981aec4dd585 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 5 May 2016 13:33:53 -0400 Subject: [PATCH 024/118] Refactor project tests for changes to the project controller. --- app/tests/test_project.py | 272 +------------------------------------- 1 file changed, 7 insertions(+), 265 deletions(-) diff --git a/app/tests/test_project.py b/app/tests/test_project.py index e4462cb..844abf9 100644 --- a/app/tests/test_project.py +++ b/app/tests/test_project.py @@ -229,10 +229,7 @@ class TestSalesForceProject(UnprotectedRestTestCase): 'state': 'pending', 'client': client_data, 'place': place.get_dictionary(), - 'address': place.address.get_dictionary(), - 'contacts': [], - 'contact_methods': [], - 'notes': []} + 'address': place.address.get_dictionary()} response_data = self._test_post(data, {'form': 'salesforce'}) # Check that a client was posted with the new data. self.assertTrue( @@ -257,10 +254,7 @@ class TestSalesForceProject(UnprotectedRestTestCase): 'state': 'pending', 'client': client_data, 'place': place.get_dictionary(), - 'address': place.address.get_dictionary(), - 'contacts': [], - 'contact_methods': [], - 'notes': []} + 'address': place.address.get_dictionary()} response_data = self._test_post(data, {'form': 'salesforce'}) # Check that the client was modified as expected. self.assertTrue( @@ -282,10 +276,7 @@ class TestSalesForceProject(UnprotectedRestTestCase): 'name': 'test', 'state': 'pending', 'place': place.get_dictionary(), - 'address': place.address.get_dictionary(), - 'contacts': [], - 'contact_methods': [], - 'notes': []} + 'address': place.address.get_dictionary()} response = self.post( self.url, data=data, @@ -313,10 +304,7 @@ class TestSalesForceProject(UnprotectedRestTestCase): 'state': 'pending', 'client': self.env.client.get_dictionary(), 'place': place_data, - 'address': address_data, - 'contacts': [], - 'contact_methods': [], - 'notes': []} + 'address': address_data} response_data = self._test_post(data, {'form': 'salesforce'}) filters = [Project.id == response_data['id']] @@ -359,10 +347,7 @@ class TestSalesForceProject(UnprotectedRestTestCase): 'state': 'pending', 'client': self.env.client.get_dictionary(), 'place': place_data, - 'address': address_data, - 'contacts': [], - 'contact_methods': [], - 'notes': []} + 'address': address_data} response_data = self._test_post(data, {'form': 'salesforce'}) filters = [ @@ -398,10 +383,7 @@ class TestSalesForceProject(UnprotectedRestTestCase): 'name': 'test', 'state': 'pending', 'client': self.env.client.get_dictionary(), - 'address': address_data, - 'contacts': [], - 'contact_methods': [], - 'notes': []} + 'address': address_data} response = self.post( self.url, data=data, @@ -417,10 +399,7 @@ class TestSalesForceProject(UnprotectedRestTestCase): 'name': 'test', 'state': 'pending', 'client': self.env.client.get_dictionary(), - 'place': place_data, - 'contacts': [], - 'contact_methods': [], - 'notes': []} + 'place': place_data} response = self.post( self.url, data=data, @@ -430,240 +409,3 @@ class TestSalesForceProject(UnprotectedRestTestCase): # There are additional ways that posting with a place and address could # fail, but those are already tested in the /address/ and /place/ # endpoints, so there's no need to test them here. - - def test_post_sf_new_contacts(self): - """Tests /project/?form=salesforce POST with a new contact.""" - place = self.env.place - contact_data = {'sales_force_id': 'test', 'name': 'test'} - data = { - 'sales_force_id': 'test', - 'name': 'test', - 'state': 'pending', - 'client': self.env.client.get_dictionary(), - 'place': place.get_dictionary(), - 'address': place.address.get_dictionary(), - 'contacts': [contact_data], - 'contact_methods': [], - 'notes': []} - response_data = self._test_post(data, {'form': 'salesforce'}) - self.assertTrue( - db.session.query(Contact)\ - .join(ProjectContact, ProjectContact.contact_id == Contact.id)\ - .join(Project, ProjectContact.project_id == Project.id)\ - .filter(and_( - Project.id == response_data['id'], - Contact.sales_force_id == contact_data['sales_force_id'], - Contact.name == contact_data['name']))\ - .first()) - - def test_post_sf_change_contacts(self): - """Tests /project/?form=salesforce POST with a modified contact.""" - place = self.env.place - contact = self.env.contact - contact_data = contact.get_dictionary() - contact_data.pop('id') - contact_data.update({'name': 'foo'}) - data = { - 'sales_force_id': 'test', - 'name': 'test', - 'state': 'pending', - 'client': self.env.client.get_dictionary(), - 'place': place.get_dictionary(), - 'address': place.address.get_dictionary(), - 'contacts': [contact_data], - 'contact_methods': [], - 'notes': []} - response_data = self._test_post(data, {'form': 'salesforce'}) - self.assertTrue( - db.session.query(Contact)\ - .join(ProjectContact, ProjectContact.contact_id == Contact.id)\ - .join(Project, ProjectContact.project_id == Project.id)\ - .filter(and_( - Project.id == response_data['id'], - Contact.id == contact.id, - Contact.sales_force_id == contact_data['sales_force_id'], - Contact.name == contact_data['name']))\ - .first()) - - def test_post_sf_no_contacts(self): - """Tests /project/?form=salesforce POST with no contacts. It should - 400. - """ - place = self.env.place - data = { - 'sales_force_id': 'test', - 'name': 'test', - 'state': 'pending', - 'client': self.env.client.get_dictionary(), - 'place': place.get_dictionary(), - 'address': place.address.get_dictionary(), - 'contact_methods': [], - 'notes': []} - response = self.post( - self.url, - data=data, - query_string={'form': 'salesforce'}) - self.assertEqual(response.status_code, 400) - self.assertFalse(db.session.query(Project).first()) - - # There are additional ways that posting with a contact could fail, but - # those are already tested in the /contact/ and /contact/project/ - # endpoints, so there's no need to test them here. - - def test_post_sf_contact_methods(self): - """Tests /project/?form=salesforce POST with a contact method.""" - place = self.env.place - contact_data = { - 'sales_force_id': 'test', - 'name': 'test'} - contact_method_data = { - 'contact_sales_force_id': contact_data['sales_force_id'], - 'method': 'email', - 'value': 'test@blocpower.org'} - data = { - 'sales_force_id': 'test', - 'name': 'test', - 'state': 'pending', - 'client': self.env.client.get_dictionary(), - 'place': place.get_dictionary(), - 'address': place.address.get_dictionary(), - 'contacts': [contact_data], - 'contact_methods': [contact_method_data], - 'notes': []} - response_data = self._test_post(data, {'form': 'salesforce'}) - self.assertTrue( - db.session.query(ContactMethod)\ - .join(Contact, ContactMethod.contact_id == Contact.id)\ - .join(ProjectContact, ProjectContact.contact_id == Contact.id)\ - .join(Project, ProjectContact.project_id == Project.id)\ - .filter(and_( - Project.id == response_data['id'], - ContactMethod.method == contact_method_data['method'], - ContactMethod.value == contact_method_data['value']))\ - .first()) - - def test_post_sf_contact_methods_bad_sf_id(self): - """Tests /project/?form=salesforce POST with a contact method with a - bad contact_sales_force_id. It should 400. - """ - place = self.env.place - contact_method_data = { - 'contact_sales_force_id': 'test', - 'method': 'email', - 'value': 'test@blocpower.org'} - data = { - 'sales_force_id': 'test', - 'name': 'test', - 'state': 'pending', - 'client': self.env.client.get_dictionary(), - 'place': place.get_dictionary(), - 'address': place.address.get_dictionary(), - 'contacts': [], - 'contact_methods': [contact_method_data], - 'notes': []} - response = self.post( - self.url, - data=data, - query_string={'form': 'salesforce'}) - self.assertEqual(response.status_code, 400) - - def test_post_sf_contact_methods_unassociated_sf_id(self): - """Tests /project/?form=salesforce POST with a contact method with a - contact_sales_force_id that is not associated with the project. It - should 400. - """ - place = self.env.place - contact = self.env.contact - contact_method_data = { - 'contact_sales_force_id': contact.sales_force_id, - 'method': 'email', - 'value': 'test@blocpower.org'} - data = { - 'sales_force_id': 'test', - 'name': 'test', - 'state': 'pending', - 'client': self.env.client.get_dictionary(), - 'place': place.get_dictionary(), - 'address': place.address.get_dictionary(), - 'contacts': [], - 'contact_methods': [contact_method_data], - 'notes': []} - response = self.post( - self.url, - data=data, - query_string={'form': 'salesforce'}) - self.assertEqual(response.status_code, 400) - - def test_post_sf_no_contact_methods(self): - """Tests /project/?form=salesforce POST with no contact methods. It - should 400. - """ - place = self.env.place - data = { - 'sales_force_id': 'test', - 'name': 'test', - 'state': 'pending', - 'client': self.env.client.get_dictionary(), - 'place': place.get_dictionary(), - 'address': place.address.get_dictionary(), - 'contacts': [], - 'notes': []} - response = self.post( - self.url, - data=data, - query_string={'form': 'salesforce'}) - self.assertEqual(response.status_code, 400) - - # There are additional ways that posting with a contact method could fail, - # but those are already tested in the /contact/method/ endpoint, so - # there's no need to test them here. - - def test_post_sf_notes(self): - """Tests /project/?form=salesforce POST with a note.""" - place = self.env.place - note_data = { - 'sales_force_id': 'test', - 'posted': arrow.get().isoformat(), - 'title': 'Test', - 'content': 'This is a test note.', - 'poster_name': 'test'} - data = { - 'sales_force_id': 'test', - 'name': 'test', - 'state': 'pending', - 'client': self.env.client.get_dictionary(), - 'place': place.get_dictionary(), - 'address': place.address.get_dictionary(), - 'contacts': [], - 'contact_methods': [], - 'notes': [note_data]} - response_data = self._test_post(data, {'form': 'salesforce'}) - filters = [Project.id == response_data['id']] - filters += [getattr(Note, k) == v for k, v in note_data.items()] - self.assertTrue( - db.session.query(Note)\ - .join(Project, Project.id == Note.project_id)\ - .filter(and_(*filters))\ - .first()) - - def test_post_sf_no_notes(self): - """Tests /project/?form=salesforce POST with a missing notes field. It - should 400. - """ - place = self.env.place - data = { - 'sales_force_id': 'test', - 'name': 'test', - 'state': 'pending', - 'client': self.env.client.get_dictionary(), - 'place': place.get_dictionary(), - 'address': place.address.get_dictionary(), - 'contacts': [], - 'contact_methods': []} - response = self.post( - self.url, data=data, query_string={'form': 'salesforce'}) - self.assertEqual(response.status_code, 400) - - # There are additional ways that posting with a note could fail, but those - # are already tested in the /note/ endpoint, so there's no need to test - # them here. -- GitLab From 9ed70b1d0292a2df77259baf20c84f4fe1fb03cd Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Tue, 10 May 2016 15:18:21 -0400 Subject: [PATCH 025/118] Add geography library. --- app/lib/geography.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 app/lib/geography.py diff --git a/app/lib/geography.py b/app/lib/geography.py new file mode 100644 index 0000000..57c94c8 --- /dev/null +++ b/app/lib/geography.py @@ -0,0 +1,20 @@ +"""Utilities for working with geographical entities.""" +# Valid strings representing valid provinces and states. +# +# TODO SQLAlchemy < 1.1 does not support the use of a python enum in Enum +# fields, so for now we have to use a list of strings. When 1.1 leaves +# beta, this should be ported to an Enum. +states = [ + 'AL', 'AK', 'AS', 'AZ', 'AR', 'CA', 'CO', 'CT', 'DE', 'DC', 'FM', 'FL', + 'GA', 'GU', 'HI', 'ID', 'IL', 'IN', 'IA', 'KS', 'KY', 'LA', 'ME', 'MH', + 'MD', 'MA', 'MI', 'MN', 'MS', 'MO', 'MT', 'NE', 'NV', 'NH', 'NJ', 'NM', + 'NY', 'NC', 'ND', 'MP', 'OH', 'OK', 'OR', 'PW', 'PA', 'PR', 'RI', 'SC', + 'SD', 'TN', 'TX', 'UT', 'VT', 'VI', 'VA', 'WA', 'WV', 'WI', 'WY' +] + +# Valid strings representing countries. +# +# TODO SQLAlchemy < 1.1 does not support the use of a python enum in Enum +# fields, so for now we have to use a list of strings. When 1.1 leaves +# beta, this should be ported to an Enum. +countries = ['United States of America'] -- GitLab From 25b0bdbb259a953ba17324bc4e893043fcaff903 Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Tue, 10 May 2016 15:22:16 -0400 Subject: [PATCH 026/118] Add an address model. --- app/models/base.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/app/models/base.py b/app/models/base.py index af252cb..9a8f5a9 100644 --- a/app/models/base.py +++ b/app/models/base.py @@ -3,7 +3,9 @@ import decimal import uuid from sqlalchemy.sql import func from sqlalchemy.ext.declarative import declared_attr + from app.lib.database import db +from app.lib import geography from app.models import columns @@ -50,3 +52,20 @@ class Tracked(object): """A mixin to include tracking datetime fields.""" created = db.Column(columns.Arrow, default=func.now()) updated = db.Column(columns.Arrow, onupdate=func.now()) + + +class Address(Model, Tracked, db.Model): + """An address (with street number, city, state, zip, ...). This does not + necessarily correlate with a physical location as defined in the + Location base model. + """ + street_address = db.Column(db.Unicode(255), nullable=False) + city = db.Column(db.Unicode(255), nullable=False) + county = db.Column(db.Unicode(255)) + state = db.Column( + db.Enum(*geography.states, name='geo_states'), + nullable=False) + country = db.Column( + db.Enum(*geography.countries, name='geo_countries'), + nullable=False) + postal_code = db.Column(db.Unicode(10)) -- GitLab From 27c7ed256bb8c999e44e39ddf0c93c944239749f Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Tue, 10 May 2016 15:23:05 -0400 Subject: [PATCH 027/118] Use address as a base class instead of a model. --- app/models/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/base.py b/app/models/base.py index 9a8f5a9..7ba3043 100644 --- a/app/models/base.py +++ b/app/models/base.py @@ -54,7 +54,7 @@ class Tracked(object): updated = db.Column(columns.Arrow, onupdate=func.now()) -class Address(Model, Tracked, db.Model): +class Address(object): """An address (with street number, city, state, zip, ...). This does not necessarily correlate with a physical location as defined in the Location base model. -- GitLab From 7a86a8f54269e157ba6b86591e4c2e7d9e991e55 Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Tue, 10 May 2016 17:00:00 -0400 Subject: [PATCH 028/118] Add geoalchemy dependency. --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 6315efc..ffb52df 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,6 +11,7 @@ Flask==0.10.1 Flask-Classy==0.6.10 Flask-SQLAlchemy==2.1 Flask-Testing==0.4.2 +GeoAlchemy2==0.3.0 httplib2==0.9.2 itsdangerous==0.24 Jinja2==2.8 -- GitLab From 7242be4d71e9bfccd69e8d47b0d0eddc0eb783c3 Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Tue, 10 May 2016 17:00:33 -0400 Subject: [PATCH 029/118] Add a location model. --- app/models/base.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/models/base.py b/app/models/base.py index 7ba3043..01cefe3 100644 --- a/app/models/base.py +++ b/app/models/base.py @@ -3,6 +3,7 @@ import decimal import uuid from sqlalchemy.sql import func from sqlalchemy.ext.declarative import declared_attr +from geoalchemy import GeometryColumn, Point from app.lib.database import db from app.lib import geography @@ -69,3 +70,8 @@ class Address(object): db.Enum(*geography.countries, name='geo_countries'), nullable=False) postal_code = db.Column(db.Unicode(10)) + + +class Location(object): + """A physical location (with latitude, longitude, and elevation).""" + point = GeometryColumn(Point(3), nullable=False) -- GitLab From cfe1e8028760c99f7320ad0cf721730d0682f1ba Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Tue, 10 May 2016 17:16:49 -0400 Subject: [PATCH 030/118] Fix salesforce unit tests for contact, place, and note. --- app/tests/test_contact.py | 42 ++++++++++++++++++++++++++------------- app/tests/test_note.py | 10 ++++++---- app/tests/test_place.py | 24 ++++++++++++---------- 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/app/tests/test_contact.py b/app/tests/test_contact.py index 4e539ce..438a242 100644 --- a/app/tests/test_contact.py +++ b/app/tests/test_contact.py @@ -1,7 +1,9 @@ """Unit tests for working with contacts.""" from sqlalchemy import and_ + +from app.lib.service import services from app.lib.database import db -from app.tests.base import RestTestCase +from app.tests.base import RestTestCase, UnprotectedRestTestCase from app.models.project import Project from app.models.contact import Contact, ContactMethod, ProjectContact @@ -379,13 +381,15 @@ class TestSalesForceContact(UnprotectedRestTestCase): def test_create(self): """Tests /contact/?form=salesforce POST with new contact.""" + contact_method = ContactMethod(method='sms', value='8885555555').get_dictionary() + contact_method.pop('contact_id') + contact_method.pop('id') data = { 'sales_force_id': 'test', 'name': 'test_name', 'contact_methods': [ - ContactMethod(method='sms', value='8885555555').get_dictionary(), - ContactMethod(method='fax', value='8886666666').get_dictionary() + contact_method ] } self._test_post(data, {'form': 'salesforce'}) @@ -393,13 +397,18 @@ class TestSalesForceContact(UnprotectedRestTestCase): def test_update(self): """Tests /contact/?form=salesforce POST with existing object.""" model = self.env.contact + contact_method_data = ContactMethod(method='sms', value='8884555555').get_dictionary() + contact_method_data.pop('id') + contact_method_data.pop('contact_id') + data = { 'sales_force_id': model.sales_force_id, - 'name': model.project_id, - 'posted': model.posted, - 'title': model.title, - 'content': 'This is an update of test note.', - 'poster_name': model.poster_name} + 'name': model.name, + 'contact_methods': + [ + contact_method_data + ] + } self._test_post(data, {'form': 'salesforce'}) class TestSalesForceProjectContactAuth(UnprotectedRestTestCase): @@ -408,7 +417,7 @@ class TestSalesForceProjectContactAuth(UnprotectedRestTestCase): Model = ProjectContact class TestSalesForceProjectContact(UnprotectedRestTestCase): - """Tests the /note/?form=salesforce POST endpoint.""" + """Tests the /contact/project/?form=salesforce POST endpoint.""" url = '/contact/project/' Model = ProjectContact @@ -423,16 +432,20 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): def test_create(self): """Tests /contact/project/?form=salesforce POST with new contact.""" project = self.env.project + contact_data = Contact(name='test',sales_force_id="testId").get_dictionary() + contact_data.pop('id') + contact_method = ContactMethod(method='sms', value='8885555555').get_dictionary() + contact_method.pop('contact_id') + contact_method.pop('id') data = { - 'contact': Contact(name='test',sales_force_id="testId").get_dictionary(), + 'contact': contact_data, 'project': {"sales_force_id": project.sales_force_id}, 'contact_methods': [ - ContactMethod(method='sms', value='8885555555').get_dictionary(), - ContactMethod(method='fax', value='8886666666').get_dictionary() + contact_method ] } - self._test_post(data, {'form': 'salesforce'}) + self._test_post(data=data, filter_data={'form': 'salesforce'}) def test_post_sf_new_project_contact_no_contact_methods(self): """Tests /contact/project?form=salesforce POST new project contact without contact methods""" @@ -493,7 +506,8 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): 'contact': contact.get_dictionary(), 'project': project_data, 'contact_methods': contact_method_data} - self._test_post(data, {'form': 'salesforce'}) + + self._test_post(data=data, filter_data={'form': 'salesforce'}) def test_post_sf_update_project_contact_no_contact_methods(self): """Tests /contact/project?form=salesforce POST updating project contact without contact methods""" diff --git a/app/tests/test_note.py b/app/tests/test_note.py index 02cea97..dee3900 100644 --- a/app/tests/test_note.py +++ b/app/tests/test_note.py @@ -2,8 +2,10 @@ import arrow import uuid from sqlalchemy import and_ + +from app.lib.service import services from app.lib.database import db -from app.tests.base import RestTestCase +from app.tests.base import RestTestCase, UnprotectedRestTestCase from app.models.note import Note @@ -179,7 +181,7 @@ class TestSalesForceNote(UnprotectedRestTestCase): 'referer': self.app.config['SALESFORCE_REFERRER']} def test_create(self): - """Tests /note/?form=salesforce POST with a note.""" + """Tests /note/?form=salesforce POST create with a note.""" data = { 'sales_force_id': 'test', 'project_id': self.env.project.id, @@ -190,12 +192,12 @@ class TestSalesForceNote(UnprotectedRestTestCase): self._test_post(data, {'form': 'salesforce'}) def test_update(self): - """Tests /note/?form=salesforce POST with a note.""" + """Tests /note/?form=salesforce POST update with a note.""" model = self.env.note_with_name data = { 'sales_force_id': model.sales_force_id, 'project_id': model.project_id, - 'posted': model.posted, + 'posted': model.posted.isoformat(), 'title': model.title, 'content': 'This is an update of test note.', 'poster_name': model.poster_name} diff --git a/app/tests/test_place.py b/app/tests/test_place.py index e402542..0bc2583 100644 --- a/app/tests/test_place.py +++ b/app/tests/test_place.py @@ -1,7 +1,9 @@ """Unit tests for working with Places and Addresses.""" from sqlalchemy import and_ + +from app.lib.service import services from app.lib.database import db -from app.tests.base import RestTestCase +from app.tests.base import RestTestCase, UnprotectedRestTestCase from app.models.project import Project from app.models.place import Place, Address @@ -259,17 +261,19 @@ class TestSalesForcePlace(UnprotectedRestTestCase): 'referer': self.app.config['SALESFORCE_REFERRER']} def test_create(self): - """Tests /place/?form=salesforce POST with an address.""" + """Tests /place/?form=salesforce POST create with an address.""" + address_data = Address( + street_address='15 MetroTech Center', + city='Brooklyn', + county='Kings', + state='NY', + country='United States of America', + postal_code='11210').get_dictionary() + address_data.pop('id') data = { 'sales_force_id': 'test', 'name': 'test place', - 'address': Address( - street_address='15 MetroTech Center', - city='Brooklyn', - county='Kings', - state='NY', - country='United States of America', - postal_code='11210').get_dictionary() + 'address': address_data } self._test_post(data, {'form': 'salesforce'}) @@ -286,7 +290,7 @@ class TestSalesForcePlace(UnprotectedRestTestCase): self.assertEqual(response.status_code, 400) def test_update(self): - """Tests /place/?form=salesforce POST with an address.""" + """Tests /place/?form=salesforce POST update with an address.""" model = self.env.place data = { 'sales_force_id': model.sales_force_id, -- GitLab From 7c8d432641493c95b8c8c9a621bae8c9ad9523e4 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Tue, 10 May 2016 17:19:17 -0400 Subject: [PATCH 031/118] Fix contact controller and modify note and place controller. --- app/controllers/contact.py | 8 ++++---- app/controllers/note.py | 1 + app/controllers/place.py | 5 +++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/controllers/contact.py b/app/controllers/contact.py index 149a465..586872c 100644 --- a/app/controllers/contact.py +++ b/app/controllers/contact.py @@ -1,6 +1,8 @@ """Controllers for managing contacts.""" from sqlalchemy import and_ from werkzeug.exceptions import BadRequest + +from app.lib.database import db from app.controllers.base import RestController from app.models.contact import Contact, ContactMethod, ProjectContact from app.models.project import Project @@ -126,9 +128,7 @@ class ProjectContactController(RestController): 'contacts and contact methods') contact_data.update({'contact_methods': contact_methods_data}) - contact = ContactController.post( - contact_data, - {'form': filter_data.get('form')}) + contact = ContactController().post(contact_data,{'form': filter_data.get('form')}) data.update({'contact_id': contact.id}) @@ -149,7 +149,7 @@ class ProjectContactController(RestController): data.update({'project_id': project.id}) model = db.session.query(self.Model)\ - .filter(_and( + .filter(and_( self.Model.contact_id == contact.id , self.Model.project_id == project.id))\ .first() diff --git a/app/controllers/note.py b/app/controllers/note.py index b7518f2..8145faf 100644 --- a/app/controllers/note.py +++ b/app/controllers/note.py @@ -1,4 +1,5 @@ """Controllers for making small notes on a project.""" +from app.lib.database import db from app.controllers.base import RestController from app.models.note import Note from app.forms.note import NoteForm diff --git a/app/controllers/place.py b/app/controllers/place.py index d64ab4b..6dd38d5 100644 --- a/app/controllers/place.py +++ b/app/controllers/place.py @@ -1,5 +1,6 @@ from werkzeug.exceptions import BadRequest +from app.lib.database import db from app.controllers.base import RestController from app.forms.place import AddressForm, PlaceForm from app.models.project import Project @@ -42,13 +43,13 @@ class PlaceController(RestController): 'When post a place from salesforce, please ' + 'include its salesforce id.') - address = place.address if place else None + address = model.address if model else None if address: address_data.update({'id': address.id}) address = AddressController().put(address.id, address_data, {}) else: address = AddressController().post(address_data, {}) - place_data.update({'address_id': address.id}) + data.update({'address_id': address.id}) if model: data.update({'id': model.id}) -- GitLab From 9c2bb7abcc0c2304da7a018c7456ae18acae4671 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Tue, 10 May 2016 17:24:34 -0400 Subject: [PATCH 032/118] Modify client, contact, place, and noteviews to access requests from salesforce. --- app/views/client.py | 37 +++++++++++++++++++++-- app/views/contact.py | 72 +++++++++++++++++++++++++++++++++++++++++--- app/views/note.py | 42 ++++++++++++++++++++++++-- app/views/place.py | 43 +++++++++++++++++++++----- 4 files changed, 179 insertions(+), 15 deletions(-) diff --git a/app/views/client.py b/app/views/client.py index 5536dc8..828f20e 100644 --- a/app/views/client.py +++ b/app/views/client.py @@ -1,16 +1,49 @@ """Views for managing clients.""" from werkzeug.exceptions import MethodNotAllowed from flask import request -from app.views.base import RestView + +from app.views.base import UnprotectedRestView from app.controllers.client import ClientController +from app.permissions.application import app_need, RoleNeed +from app.permissions.auth import auth_need, standard_login_need + -class ClientView(RestView): +class ClientView(UnprotectedRestView): """The client view.""" def get_controller(self): """Return an instance of the client controller.""" return ClientController() + @standard_login_need + def index(self): + return super(ClientView, self).index() + + @standard_login_need + def get(self, id_): + return super(ClientView, self).get(id_) + + def post(self): + """Check for an application and either a user or an application with + the project_post_salesforce role (and the salesforce form) before + posting a client. + """ + form = request.args.get('form') + + need = app_need + if form == 'salesforce': + need &= RoleNeed('project_post_salesforce') + else: + need &= auth_need + + with need: + return super(ClientView, self).post() + + @standard_login_need + def put(self, id_): + return super(ClientView, self).put(id_) + + @standard_login_need def delete(self, id_): """Not implemented.""" raise MethodNotAllowed( diff --git a/app/views/contact.py b/app/views/contact.py index 88be9ab..9e1318f 100644 --- a/app/views/contact.py +++ b/app/views/contact.py @@ -1,18 +1,53 @@ """Views for managing contacts.""" from werkzeug.exceptions import MethodNotAllowed from flask import request -from app.views.base import RestView + +from app.views.base import RestView, UnprotectedRestView from app.controllers.contact import ( ContactController, ContactMethodController, ProjectContactController) -from app.permissions.base import FormNeed +from app.permissions.base import FormNeed +from app.permissions.application import app_need, RoleNeed +from app.permissions.auth import auth_need, standard_login_need -class ContactView(RestView): +class ContactView(UnprotectedRestView): """The contact view.""" def get_controller(self): """Return an instance of the contact controller.""" return ContactController() + @standard_login_need + def index(self): + return super(ContactView, self).index() + + @standard_login_need + def get(self, id_): + return super(ContactView, self).get(id_) + + def post(self): + """Check for an application and either a user or an application with + the project_post_salesforce role (and the salesforce form) before + posting a contact. + """ + form = request.args.get('form') + + need = app_need + if form == 'salesforce': + need &= RoleNeed('project_post_salesforce') + else: + need &= auth_need + + with need: + return super(ContactView, self).post() + + @standard_login_need + def put(self, id_): + return super(ContactView, self).put(id_) + + @standard_login_need + def delete(self, id_): + return super(ContactView, self).delete(id_) + class ContactMethodView(RestView): """The contact method view.""" @@ -23,7 +58,7 @@ class ContactMethodView(RestView): return ContactMethodController() -class ProjectContactView(RestView): +class ProjectContactView(UnprotectedRestView): """A view for the project-contact m2m relationship.""" route_base = '/contact/project/' @@ -31,8 +66,37 @@ class ProjectContactView(RestView): """Return an instance of the project contact controller.""" return ProjectContactController() + @standard_login_need + def index(self): + return super(ProjectContactView, self).index() + + @standard_login_need + def get(self, id_): + return super(ProjectContactView, self).get(id_) + + def post(self): + """Check for an application and either a user or an application with + the project_post_salesforce role (and the salesforce form) before + posting a project conatct. + """ + form = request.args.get('form') + + need = app_need + if form == 'salesforce': + need &= RoleNeed('project_post_salesforce') + else: + need &= auth_need + + with need: + return super(ProjectContactView, self).post() + + @standard_login_need def put(self, id_): """Not implemented.""" raise MethodNotAllowed( 'Do not modify existing m2m relationships. Delete this and ' + 'create a new one instead.') + + @standard_login_need + def delete(self, id_): + return super(ProjectContactView, self).delete(id_) diff --git a/app/views/note.py b/app/views/note.py index 4a76d7b..64fde92 100644 --- a/app/views/note.py +++ b/app/views/note.py @@ -1,10 +1,48 @@ """Views for managing notes.""" -from app.views.base import RestView +from werkzeug.exceptions import MethodNotAllowed +from flask import request + +from app.views.base import UnprotectedRestView from app.controllers.note import NoteController +from app.permissions.application import app_need, RoleNeed +from app.permissions.auth import auth_need, standard_login_need + -class NoteView(RestView): +class NoteView(UnprotectedRestView): """The note view.""" def get_controller(self): """Return an instance of the note controller.""" return NoteController() + + @standard_login_need + def index(self): + return super(NoteView, self).index() + + @standard_login_need + def get(self, id_): + return super(NoteView, self).get(id_) + + def post(self): + """Check for an application and either a user or an application with + the project_post_salesforce role (and the salesforce form) before + posting a note. + """ + form = request.args.get('form') + + need = app_need + if form == 'salesforce': + need &= RoleNeed('project_post_salesforce') + else: + need &= auth_need + + with need: + return super(NoteView, self).post() + + @standard_login_need + def put(self, id_): + return super(NoteView, self).put(id_) + + @standard_login_need + def delete(self, id_): + return super(NoteView, self).delete(id_) diff --git a/app/views/place.py b/app/views/place.py index c03bcd5..ca46102 100644 --- a/app/views/place.py +++ b/app/views/place.py @@ -1,21 +1,50 @@ -from flask import request +"""Views for managing place and address.""" from werkzeug.exceptions import MethodNotAllowed +from flask import request from app.controllers.place import AddressController, PlaceController -from app.views.base import RestView +from app.views.base import RestView, UnprotectedRestView + +from app.permissions.application import app_need, RoleNeed +from app.permissions.auth import auth_need, standard_login_need -class PlaceView(RestView): +class PlaceView(UnprotectedRestView): """The Place view.""" def get_controller(self): """Return an instance of the Place controller.""" return PlaceController() - def delete(self, id_): - """Not implemented. - Args: - id_ - The object ID + @standard_login_need + def index(self): + return super(PlaceView, self).index() + + @standard_login_need + def get(self, id_): + return super(PlaceView, self).get(id_) + + def post(self): + """Check for an application and either a user or an application with + the project_post_salesforce role (and the salesforce form) before + posting a place. """ + form = request.args.get('form') + + need = app_need + if form == 'salesforce': + need &= RoleNeed('project_post_salesforce') + else: + need &= auth_need + + with need: + return super(PlaceView, self).post() + + @standard_login_need + def put(self, id_): + return super(PlaceView, self).put(id_) + + def delete(self, id_): + """Not implemented""" raise MethodNotAllowed( 'Places cannot be deleted. Cancel the project instead.') -- GitLab From 052e825657430a4abc680ba45765d806922dca68 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Mon, 16 May 2016 20:04:26 -0400 Subject: [PATCH 033/118] Rename expected json object field in projectcontact from project to project_sales_force_id and modify tests. --- app/controllers/contact.py | 19 ++++++++----------- app/tests/test_contact.py | 2 +- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/app/controllers/contact.py b/app/controllers/contact.py index 586872c..71cc777 100644 --- a/app/controllers/contact.py +++ b/app/controllers/contact.py @@ -58,6 +58,7 @@ class ContactController(RestController): else: model = super(ContactController, self).post(data, filter_data) + # Prevent salesforce from creating duplicate contact methods. for contact_method_data in contact_methods_data: contact_method = db.session.query(ContactMethod)\ .filter(and_( @@ -121,30 +122,26 @@ class ProjectContactController(RestController): try: contact_data = data['contact'] contact_methods_data = data['contact_methods'] - project_data = data['project'] + project_sales_force_id = data['project_sales_force_id'] except KeyError: raise BadRequest( 'When posting a project contact from salesforce embed ' + - 'contacts and contact methods') + 'project saleforce id, contacts, and contact methods') contact_data.update({'contact_methods': contact_methods_data}) contact = ContactController().post(contact_data,{'form': filter_data.get('form')}) data.update({'contact_id': contact.id}) - try: - project = db.session.query(Project)\ - .filter(Project.sales_force_id == project_data['sales_force_id'])\ - .first() - except KeyError: - raise BadRequest( - 'When posting a project contact from salesforce embed ' + - 'project salesforce id.') + project = db.session.query(Project)\ + .filter(Project.sales_force_id == project_sales_force_id)\ + .first() + if not project: raise BadRequest( 'Opportunity out of sync. When posting a project contact from salesforce project ' + - 'has to be tracked by the project service.') + 'the Opportunity has to be tracked by the project service.') data.update({'project_id': project.id}) diff --git a/app/tests/test_contact.py b/app/tests/test_contact.py index 438a242..f503716 100644 --- a/app/tests/test_contact.py +++ b/app/tests/test_contact.py @@ -439,7 +439,7 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): contact_method.pop('id') data = { 'contact': contact_data, - 'project': {"sales_force_id": project.sales_force_id}, + 'project_sales_force_id': project.sales_force_id, 'contact_methods': [ contact_method -- GitLab From 567842953a5f168fa6d6d5b5aaf81eb3cc2851e3 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Tue, 17 May 2016 14:40:15 -0400 Subject: [PATCH 034/118] When project contact post happens from salesforce on update don't attemtp to update M2M model. --- app/controllers/contact.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/controllers/contact.py b/app/controllers/contact.py index 71cc777..ea56552 100644 --- a/app/controllers/contact.py +++ b/app/controllers/contact.py @@ -151,10 +151,7 @@ class ProjectContactController(RestController): self.Model.project_id == project.id))\ .first() - if model: - data.update({'id': model.id}) - model = self.put(model.id, data, filter_data) - else: + if not model: model = super(ProjectContactController, self).post(data, filter_data) else: model = super(ProjectContactController, self).post(data, filter_data) -- GitLab From 7ddff88983a8578f8ba1f67c2a86679e58a6996e Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Tue, 17 May 2016 14:52:35 -0400 Subject: [PATCH 035/118] Remove base class that was not used. Potentially revisit base class. --- app/controllers/base.py | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/app/controllers/base.py b/app/controllers/base.py index 5e57b86..6b6c143 100644 --- a/app/controllers/base.py +++ b/app/controllers/base.py @@ -143,32 +143,3 @@ class RestController(object): db.session.delete(model) commit() return {} - -class SalesforceObjectController(RestController): - - def post(self, data, filter_data): - """ Post a new model. - If the post is from salesforce, insert if salesforce id - is new and update if it already exist. If post is not from - salesforce treat as regular Restful POST request - """ - if filter_data.get('form') == 'salesforce': - # Create/modify the object. - try: - model = db.session.query(self.Model)\ - .filter( - Model.sales_force_id == data['sales_force_id'])\ - .first() - except KeyError: - raise BadRequest( - 'When posting an object from salesforce, please ' + - 'include its salesforce id.') - if model: - data.update({'id': model.id}) - model = self.put(model.id, data, filter_data) - else: - model = super(type(self), self).post(data, filter_data) - else: - model = super(type(self), self).post(data, filter_data) - - return model -- GitLab From 1d8bd4c7898b960881bbb6bbb1c4f6753ce9d10a Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Tue, 17 May 2016 15:07:52 -0400 Subject: [PATCH 036/118] Update sqlalchemy. --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ffb52df..a0f1eac 100644 --- a/requirements.txt +++ b/requirements.txt @@ -35,7 +35,7 @@ redis==2.10.5 requests==2.6.2 rsa==3.3 six==1.10.0 -SQLAlchemy==1.0.12 +SQLAlchemy==1.0.13 texttable==0.8.4 wcwidth==0.1.6 websocket-client==0.35.0 -- GitLab From a7ff35fcb8c6b12499bb6d1aee0a80e7f8115942 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Tue, 17 May 2016 15:12:46 -0400 Subject: [PATCH 037/118] Fix base salesforce class. --- app/controllers/base.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/app/controllers/base.py b/app/controllers/base.py index 6b6c143..eda7d58 100644 --- a/app/controllers/base.py +++ b/app/controllers/base.py @@ -143,3 +143,33 @@ class RestController(object): db.session.delete(model) commit() return {} + +class SalesforceObjectController(RestController): + """ Base object controller for interacting with objects also represented in salesforce. + """ + def post(self, data, filter_data): + """ Post a new model. + If the post is from salesforce, insert if salesforce id + is new and update if it already exist. If post is not from + salesforce treat as regular Restful POST request + """ + if filter_data.get('form') == 'salesforce': + # Create/modify the object. + try: + model = db.session.query(self.Model)\ + .filter( + self.Model.sales_force_id == data['sales_force_id'])\ + .first() + except KeyError: + raise BadRequest( + 'When posting an object from salesforce, please ' + + 'include its salesforce id.') + if model: + data.update({'id': model.id}) + model = self.put(model.id, data, filter_data) + else: + model = super(SalesforceObjectController, self).post(data, filter_data) + else: + model = super(SalesforceObjectController, self).post(data, filter_data) + + return model -- GitLab From 393e85708823116c2f3692a934357916d3019a5b Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Tue, 17 May 2016 15:13:31 -0400 Subject: [PATCH 038/118] Use relative imports. --- app/__init__.py | 12 ++++++------ app/controllers/base.py | 3 ++- app/models/base.py | 6 +++--- app/permissions/application.py | 7 ++++--- app/permissions/auth.py | 8 ++++---- app/tests/test_application.py | 8 ++++---- app/tests/test_auth.py | 6 +++--- 7 files changed, 26 insertions(+), 24 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index 4553287..cbd49e1 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -6,22 +6,22 @@ def create_app(config): app = Flask(__name__) app.config.from_pyfile(config) - from app.lib import database + from .lib import database database.register(app) - from app.lib import red + from .lib import red red.register(app) - from app import views + from . import views views.register(app) - from app.lib import exceptions + from .lib import exceptions exceptions.register(app) - from app.lib import service + from .lib import service service.register(app) - from app.lib import session + from .lib import session session.register(app) with app.app_context(): diff --git a/app/controllers/base.py b/app/controllers/base.py index 6928f39..9cfa0bf 100644 --- a/app/controllers/base.py +++ b/app/controllers/base.py @@ -2,7 +2,8 @@ from sqlalchemy.exc import IntegrityError from werkzeug.datastructures import MultiDict from werkzeug.exceptions import NotFound, BadRequest from flask import current_app -from app.lib.database import db, commit + +from ..lib.database import db, commit class RestController(object): diff --git a/app/models/base.py b/app/models/base.py index 01cefe3..4818756 100644 --- a/app/models/base.py +++ b/app/models/base.py @@ -5,9 +5,9 @@ from sqlalchemy.sql import func from sqlalchemy.ext.declarative import declared_attr from geoalchemy import GeometryColumn, Point -from app.lib.database import db -from app.lib import geography -from app.models import columns +from ..lib.database import db +from ..lib import geography +from . import columns class Model(object): diff --git a/app/permissions/application.py b/app/permissions/application.py index ed798dd..b43f140 100644 --- a/app/permissions/application.py +++ b/app/permissions/application.py @@ -1,9 +1,10 @@ """Permissions for working with the app service.""" import requests from werkzeug.exceptions import Unauthorized -from app.permissions.base import Permission -from app.lib.red import redis -from app.lib.service import services + +from ..lib.red import redis +from ..lib.service import services +from .base import Permission class AppNeed(Permission): diff --git a/app/permissions/auth.py b/app/permissions/auth.py index fd2d4d3..6b4b20c 100644 --- a/app/permissions/auth.py +++ b/app/permissions/auth.py @@ -1,10 +1,10 @@ """Permissions to check authentication.""" from werkzeug.exceptions import Unauthorized -from app.lib.red import redis -from app.lib.service import services -from app.permissions.base import Permission -from app.permissions.application import app_need +from ..lib.red import redis +from ..lib.service import services +from .base import Permission +from .application import app_need class AuthNeed(Permission): diff --git a/app/tests/test_application.py b/app/tests/test_application.py index 2adced0..ca46a9e 100644 --- a/app/tests/test_application.py +++ b/app/tests/test_application.py @@ -1,8 +1,8 @@ """Test integration with the app service.""" -from app.tests.base import UnprotectedRestTestCase, AppProtectedTestCase -from app.lib.red import redis -from app.lib.service import services -from app.permissions.application import app_need, RoleNeed +from ..lib.red import redis +from ..lib.service import services +from ..permissions.application import app_need, RoleNeed +from .base import UnprotectedRestTestCase, AppProtectedTestCase class AppNeedTest(UnprotectedRestTestCase): diff --git a/app/tests/test_auth.py b/app/tests/test_auth.py index 39c71de..993e237 100644 --- a/app/tests/test_auth.py +++ b/app/tests/test_auth.py @@ -1,7 +1,7 @@ """Tests integration with the user service.""" -from app.lib.red import redis -from app.tests.base import RestTestCase -from app.permissions.auth import auth_need +from ..lib.red import redis +from ..permissions.auth import auth_need +from .base import RestTestCase class AuthNeedTest(RestTestCase): -- GitLab From 736ad1aaac801fab1bfa09fa882a8b308cff762c Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Tue, 17 May 2016 15:14:15 -0400 Subject: [PATCH 039/118] Add an address form mixin. --- app/forms/base.py | 16 ++++++++++++++++ app/forms/validators.py | 5 +++++ 2 files changed, 21 insertions(+) create mode 100644 app/forms/validators.py diff --git a/app/forms/base.py b/app/forms/base.py index 34ed4f9..5997d80 100644 --- a/app/forms/base.py +++ b/app/forms/base.py @@ -1,7 +1,23 @@ """Base classes for working with forms.""" import wtforms as wtf +from . import validators class Form(wtf.Form): """A base class for forms based on the base model mixin.""" id = wtf.IntegerField() + + +class AddressForm(object): + """A form for validating address information.""" + street_address = wtf.StringField( + validators=[wtf.validators.Required(), wtf.validators.Length(max=255)]) + city = wtf.StringField( + validators=[wtf.validators.Required(), wtf.validators.Length(max=255)]) + county = wtf.StringField( + validators=[wtf.validators.Length(max=255)]) + state = wtf.StringField( + validators=[wtf.validators.AnyOf(geography.states)]) + country = wtf.StringField( + validators=[wtf.validators.AnyOf(geography.countries)]) + postal_code = wtf.StringField(validators=[validators.zip_]) diff --git a/app/forms/validators.py b/app/forms/validators.py new file mode 100644 index 0000000..e9ee510 --- /dev/null +++ b/app/forms/validators.py @@ -0,0 +1,5 @@ +"""Custom form validators.""" + + +# A zip code validator. +zip_ = wtf.validators.Regexp(r'^\d{5}(-\d{4})?$') -- GitLab From 0aa8555bc3ac9a7ff1f1b541dc741fd0afdd66ae Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Tue, 17 May 2016 15:17:45 -0400 Subject: [PATCH 040/118] Add missing import. --- app/forms/validators.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/forms/validators.py b/app/forms/validators.py index e9ee510..9a1889d 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -1,4 +1,5 @@ """Custom form validators.""" +import wtforms as wtf # A zip code validator. -- GitLab From b9ff34819238b07bd1fa26bb4d8d1401328a99b1 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Tue, 17 May 2016 15:22:30 -0400 Subject: [PATCH 041/118] Inherit from SaleforceObjectController in client instead of repeating code. --- app/controllers/client.py | 33 ++------------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/app/controllers/client.py b/app/controllers/client.py index 0f9b33d..52edcfc 100644 --- a/app/controllers/client.py +++ b/app/controllers/client.py @@ -1,14 +1,12 @@ """Controllers for managing clients.""" -from werkzeug.exceptions import BadRequest - from app.lib.database import db -from app.controllers.base import RestController +from app.controllers.base import SalesforceObjectController from app.models.project import Project from app.models.client import Client from app.forms.client import ClientForm -class ClientController(RestController): +class ClientController(SalesforceObjectController): """The client controller.""" Model = Client constant_fields = ['sales_force_id'] @@ -25,30 +23,3 @@ class ClientController(RestController): def get_form(self, filter_data): """Return the client form.""" return ClientForm - - def post(self, data, filter_data): - """ Post a new model. - If the post is from salesforce use, insert if salesforce id - is new and update if it already exist. If post is not from - salesforce treat as regular Restful POST request - """ - if filter_data.get('form') == 'salesforce': - # Create/modify the client. - try: - model = db.session.query(self.Model)\ - .filter( - self.Model.sales_force_id == data['sales_force_id'])\ - .first() - except KeyError: - raise BadRequest( - 'When post a client from salesforce, please ' + - 'include its salesforce id.') - if model: - data.update({'id': model.id}) - model = self.put(model.id, data, filter_data) - else: - model = super(ClientController, self).post(data, filter_data) - else: - model = super(ClientController, self).post(data, filter_data) - - return model -- GitLab From 28931b75bda939b5b62ef50f16d1461eab027b00 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Tue, 17 May 2016 15:24:07 -0400 Subject: [PATCH 042/118] Inherit from SalesforceObjectController in ContactController. --- app/controllers/contact.py | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/app/controllers/contact.py b/app/controllers/contact.py index ea56552..b975e94 100644 --- a/app/controllers/contact.py +++ b/app/controllers/contact.py @@ -3,13 +3,13 @@ from sqlalchemy import and_ from werkzeug.exceptions import BadRequest from app.lib.database import db -from app.controllers.base import RestController +from app.controllers.base import RestController, SalesforceObjectController from app.models.contact import Contact, ContactMethod, ProjectContact from app.models.project import Project from app.forms.contact import ContactForm, ContactMethodForm, ProjectContactForm -class ContactController(RestController): +class ContactController(SalesforceObjectController): """The contact controller.""" Model = Contact constant_fields = ['sales_force_id'] @@ -43,20 +43,7 @@ class ContactController(RestController): 'When posting a contact from salesforce embed contact ' + 'methods') - try: - model = db.session.query(self.Model)\ - .filter( - self.Model.sales_force_id == data['sales_force_id'])\ - .first() - except KeyError: - raise BadRequest( - 'When post a client from salesforce, please ' + - 'include its salesforce id.') - if model: - data.update({'id': model.id}) - model = self.put(model.id, data, filter_data) - else: - model = super(ContactController, self).post(data, filter_data) + model = super(ContactController,self).post(data, filter_data) # Prevent salesforce from creating duplicate contact methods. for contact_method_data in contact_methods_data: -- GitLab From 97245c9030ef3085447b14d21106925b89a88008 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Wed, 18 May 2016 10:26:44 -0400 Subject: [PATCH 043/118] Refactor base salesforce object class --- app/controllers/base.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/app/controllers/base.py b/app/controllers/base.py index eda7d58..4c0c349 100644 --- a/app/controllers/base.py +++ b/app/controllers/base.py @@ -153,7 +153,9 @@ class SalesforceObjectController(RestController): is new and update if it already exist. If post is not from salesforce treat as regular Restful POST request """ - if filter_data.get('form') == 'salesforce': + if filter_data.get('form') != 'salesforce': + return super(SalesforceObjectController, self).post(data, filter_data) + else: # Create/modify the object. try: model = db.session.query(self.Model)\ @@ -166,10 +168,6 @@ class SalesforceObjectController(RestController): 'include its salesforce id.') if model: data.update({'id': model.id}) - model = self.put(model.id, data, filter_data) + return self.put(model.id, data, filter_data) else: - model = super(SalesforceObjectController, self).post(data, filter_data) - else: - model = super(SalesforceObjectController, self).post(data, filter_data) - - return model + return super(SalesforceObjectController, self).post(data, filter_data) -- GitLab From a264fdcc21238dccc278354e95093f951cdb56b7 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Wed, 18 May 2016 10:28:30 -0400 Subject: [PATCH 044/118] Add comments back about odd behaviour. --- app/controllers/project.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/controllers/project.py b/app/controllers/project.py index 8b7b101..fef8930 100644 --- a/app/controllers/project.py +++ b/app/controllers/project.py @@ -45,6 +45,13 @@ class ProjectController(RestController): This logs a model state change after saving a new model. It includes support for salesforce, which dumps all submodels (e.g client, contacts, ...) in addition to the model itself. + + Behavior with the embedded models is a little odd. Rather than + validating everything up front, we validate as we go. Except for + the project itself, an object will successfully save if all + subsequent saves succeeded. For example, this means that a client + may be created even if the request 400s due to a problem with the + project itself. """ # A project has to have a client and a place, so we create these models # before posting the project when performing a bulk upload. -- GitLab From f77d0efbccc6eb164e3747f481255ade22ceb2f9 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Wed, 18 May 2016 10:44:53 -0400 Subject: [PATCH 045/118] Inherit from salesforce object controller in the note controller. --- app/controllers/note.py | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/app/controllers/note.py b/app/controllers/note.py index 8145faf..5f8f734 100644 --- a/app/controllers/note.py +++ b/app/controllers/note.py @@ -1,11 +1,11 @@ """Controllers for making small notes on a project.""" from app.lib.database import db -from app.controllers.base import RestController +from app.controllers.base import SalesforceObjectController from app.models.note import Note from app.forms.note import NoteForm -class NoteController(RestController): +class NoteController(SalesforceObjectController): """The note controller.""" Model = Note constant_fields = [ @@ -14,30 +14,3 @@ class NoteController(RestController): def get_form(self, filter_data): """Return the note form.""" return NoteForm - - def post(self, data, filter_data): - """ Post a new model. - When the post is from salesforce use insert if salesforce id - is new and update if it already exist. If post is not from - salesforce treat as regular Restful POST request - """ - if filter_data.get('form') == 'salesforce': - # Create/modify the note. - try: - model = db.session.query(self.Model)\ - .filter( - self.Model.sales_force_id == data['sales_force_id'])\ - .first() - except KeyError: - raise BadRequest( - 'When post a note from salesforce, please ' + - 'include its salesforce id.') - if model: - data.update({'id': model.id}) - model = self.put(model.id, data, filter_data) - else: - model = super(NoteController, self).post(data, filter_data) - else: - model = super(NoteController, self).post(data, filter_data) - - return model -- GitLab From 83be6481c25b51bcb464d08d53975f2b2b7cbd81 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Wed, 18 May 2016 11:50:46 -0400 Subject: [PATCH 046/118] Remove the test for creation since salesforce will not be creating a contact using the salesforce form only updating. --- app/tests/test_contact.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/app/tests/test_contact.py b/app/tests/test_contact.py index f503716..269e592 100644 --- a/app/tests/test_contact.py +++ b/app/tests/test_contact.py @@ -379,21 +379,6 @@ class TestSalesForceContact(UnprotectedRestTestCase): self.app.config['SALESFORCE_KEY'], 'referer': self.app.config['SALESFORCE_REFERRER']} - def test_create(self): - """Tests /contact/?form=salesforce POST with new contact.""" - contact_method = ContactMethod(method='sms', value='8885555555').get_dictionary() - contact_method.pop('contact_id') - contact_method.pop('id') - data = { - 'sales_force_id': 'test', - 'name': 'test_name', - 'contact_methods': - [ - contact_method - ] - } - self._test_post(data, {'form': 'salesforce'}) - def test_update(self): """Tests /contact/?form=salesforce POST with existing object.""" model = self.env.contact -- GitLab From f23ab9223b4af9337bbe30ab6b6cb4b286796472 Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Wed, 18 May 2016 15:21:30 -0400 Subject: [PATCH 047/118] Fix geoalchemy imports. --- app/models/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/base.py b/app/models/base.py index 4818756..1294e0d 100644 --- a/app/models/base.py +++ b/app/models/base.py @@ -3,7 +3,7 @@ import decimal import uuid from sqlalchemy.sql import func from sqlalchemy.ext.declarative import declared_attr -from geoalchemy import GeometryColumn, Point +from geoalchemy2 import GeometryColumn, Point from ..lib.database import db from ..lib import geography -- GitLab From 952da4cff6d14388511cc6cba7bacb6a42559bd2 Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Wed, 18 May 2016 15:23:37 -0400 Subject: [PATCH 048/118] Port to geoalchemy2 implementation. --- app/models/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/base.py b/app/models/base.py index 1294e0d..5b472fc 100644 --- a/app/models/base.py +++ b/app/models/base.py @@ -3,7 +3,7 @@ import decimal import uuid from sqlalchemy.sql import func from sqlalchemy.ext.declarative import declared_attr -from geoalchemy2 import GeometryColumn, Point +from geoalchemy2 import Geometry from ..lib.database import db from ..lib import geography @@ -74,4 +74,4 @@ class Address(object): class Location(object): """A physical location (with latitude, longitude, and elevation).""" - point = GeometryColumn(Point(3), nullable=False) + point = db.Column(Geometry('POINT'), nullable=False) -- GitLab From 757fd9d82ddeaad8e0722f4bf356a74559ef6a74 Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Wed, 18 May 2016 15:26:20 -0400 Subject: [PATCH 049/118] Add missing geography input. --- app/forms/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/forms/base.py b/app/forms/base.py index 5997d80..1be257d 100644 --- a/app/forms/base.py +++ b/app/forms/base.py @@ -1,5 +1,6 @@ """Base classes for working with forms.""" import wtforms as wtf +from ..lib import geography from . import validators -- GitLab From 86cd6ea7c833479f9052774b38c0344f1d79f16a Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 19 May 2016 11:57:37 -0400 Subject: [PATCH 050/118] Modify tests to use expected object and not the get_dictionary() method for our models. --- app/tests/test_contact.py | 44 +++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/app/tests/test_contact.py b/app/tests/test_contact.py index 269e592..5db816f 100644 --- a/app/tests/test_contact.py +++ b/app/tests/test_contact.py @@ -379,7 +379,7 @@ class TestSalesForceContact(UnprotectedRestTestCase): self.app.config['SALESFORCE_KEY'], 'referer': self.app.config['SALESFORCE_REFERRER']} - def test_update(self): + def test_update_embedded_contact_method(self): """Tests /contact/?form=salesforce POST with existing object.""" model = self.env.contact contact_method_data = ContactMethod(method='sms', value='8884555555').get_dictionary() @@ -396,10 +396,24 @@ class TestSalesForceContact(UnprotectedRestTestCase): } self._test_post(data, {'form': 'salesforce'}) -class TestSalesForceProjectContactAuth(UnprotectedRestTestCase): - """Tests authentication /contact/?form=salesforce POST endpoint.""" - url = '/contact/project/' - Model = ProjectContact + def test_update(self): + """Tests /contact/?form=salesforce POST with existing object.""" + model = self.env.contact + + contact_method_data = ContactMethod(method='sms', value='8884555555').get_dictionary() + contact_method_data.pop('id') + contact_method_data.pop('contact_id') + + data = { + 'sales_force_id': model.sales_force_id, + 'name': 'John Doe', + 'contact_methods': + [ + contact_method_data + ] + } + self._test_post(data, {'form': 'salesforce'}) + class TestSalesForceProjectContact(UnprotectedRestTestCase): """Tests the /contact/project/?form=salesforce POST endpoint.""" @@ -417,17 +431,18 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): def test_create(self): """Tests /contact/project/?form=salesforce POST with new contact.""" project = self.env.project - contact_data = Contact(name='test',sales_force_id="testId").get_dictionary() - contact_data.pop('id') - contact_method = ContactMethod(method='sms', value='8885555555').get_dictionary() - contact_method.pop('contact_id') - contact_method.pop('id') data = { - 'contact': contact_data, + 'contact': { + 'name': 'test', + 'sales_force_id': 'testId' + }, 'project_sales_force_id': project.sales_force_id, 'contact_methods': [ - contact_method + { + 'method': 'sms', + 'value': '88855555555' + } ] } self._test_post(data=data, filter_data={'form': 'salesforce'}) @@ -436,7 +451,10 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): """Tests /contact/project?form=salesforce POST new project contact without contact methods""" project = self.env.project data = { - 'contact': Contact(name='test').get_dictionary(), + 'contact': { + 'name' : 'test', + 'sales_force_id': 'testId' + }, 'project': {"sales_force_id": project.sales_force_id} } response = self.post( -- GitLab From fc081b563f108177808302b38932ee702429a8b0 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 19 May 2016 13:33:13 -0400 Subject: [PATCH 051/118] Test salesforce contact controller without using get_dictionary(). --- app/tests/test_contact.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/app/tests/test_contact.py b/app/tests/test_contact.py index 5db816f..c99f551 100644 --- a/app/tests/test_contact.py +++ b/app/tests/test_contact.py @@ -382,16 +382,16 @@ class TestSalesForceContact(UnprotectedRestTestCase): def test_update_embedded_contact_method(self): """Tests /contact/?form=salesforce POST with existing object.""" model = self.env.contact - contact_method_data = ContactMethod(method='sms', value='8884555555').get_dictionary() - contact_method_data.pop('id') - contact_method_data.pop('contact_id') data = { 'sales_force_id': model.sales_force_id, 'name': model.name, 'contact_methods': [ - contact_method_data + { + 'method': 'sms', + 'value': '88845555555' + } ] } self._test_post(data, {'form': 'salesforce'}) @@ -400,16 +400,15 @@ class TestSalesForceContact(UnprotectedRestTestCase): """Tests /contact/?form=salesforce POST with existing object.""" model = self.env.contact - contact_method_data = ContactMethod(method='sms', value='8884555555').get_dictionary() - contact_method_data.pop('id') - contact_method_data.pop('contact_id') - data = { 'sales_force_id': model.sales_force_id, 'name': 'John Doe', 'contact_methods': [ - contact_method_data + { + 'method': 'sms', + 'value': '88855555555' + } ] } self._test_post(data, {'form': 'salesforce'}) -- GitLab From ec7fd12749f8c3527ea35276926fd6c53b8421ee Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 19 May 2016 14:04:06 -0400 Subject: [PATCH 052/118] Rename tests to shorter names and remove more get_dictionary() for object creation --- app/tests/test_contact.py | 47 +++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/app/tests/test_contact.py b/app/tests/test_contact.py index c99f551..15b83b2 100644 --- a/app/tests/test_contact.py +++ b/app/tests/test_contact.py @@ -446,15 +446,15 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): } self._test_post(data=data, filter_data={'form': 'salesforce'}) - def test_post_sf_new_project_contact_no_contact_methods(self): - """Tests /contact/project?form=salesforce POST new project contact without contact methods""" + def test_create_no_contact_methods(self): + """Tests /contact/project?form=salesforce POST new project contact without contact methods.""" project = self.env.project data = { 'contact': { 'name' : 'test', 'sales_force_id': 'testId' }, - 'project': {"sales_force_id": project.sales_force_id} + 'project_sales_force_id': project.sales_force_id } response = self.post( self.url, @@ -462,15 +462,21 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): query_string={'form': 'salesforce'}) self.assertEqual(response.status_code, 400) - def test_post_sf_new_project_contact_no_contact(self): - """Tests /contact/project?form=salesforce POST new project contact without contact methods""" + def test_create_no_contact(self): + """Tests /contact/project?form=salesforce POST new project contact without contact.""" project = self.env.project data = { - 'project': {"sales_force_id": project.sales_force_id}, + 'project_sales_force_id': project.sales_force_id, 'contact_methods': [ - ContactMethod(method='sms', value='8885555555').get_dictionary(), - ContactMethod(method='fax', value='8886666666').get_dictionary() + { + 'method': 'sms', + 'value': '88855555555' + }, + { + 'method': 'fax', + 'value': '8886666666' + } ] } response = self.post( @@ -491,8 +497,6 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): .filter(Project.id == model.project_id)\ .first() - project_data = {'sales_force_id' : project.sales_force_id} - contact_methods = db.session.query(ContactMethod)\ .filter(ContactMethod.contact_id == contact.id)\ .all() @@ -506,12 +510,12 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): data = { 'contact': contact.get_dictionary(), - 'project': project_data, + 'project_sales_force_id': project.sales_force_id, 'contact_methods': contact_method_data} self._test_post(data=data, filter_data={'form': 'salesforce'}) - def test_post_sf_update_project_contact_no_contact_methods(self): + def test_update_no_contact_methods(self): """Tests /contact/project?form=salesforce POST updating project contact without contact methods""" model = self.env.project_contact contact = db.session.query(Contact)\ @@ -521,11 +525,10 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): project = db.session.query(Project)\ .filter(Project.id == model.project_id)\ .first() - project_data = {'sales_force_id' : project.sales_force_id} data = { 'contact': contact.get_dictionary(), - 'project': project_data} + 'project_sales_force_id': project.sales_force_id} response = self.post( self.url, @@ -533,8 +536,8 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): query_string={'form': 'salesforce'}) self.assertEqual(response.status_code, 400) - def test_post_sf_update_project_contact_no_contact(self): - """Tests /contact/project?form=salesforce POST updating project contact without contact methods""" + def test_update_no_contact(self): + """Tests /contact/project?form=salesforce POST updating project contact without contact.""" model = self.env.project_contact project = db.session.query(Project)\ .filter(Project.id == model.project_id)\ @@ -543,8 +546,14 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): 'project': {"sales_force_id": project.sales_force_id}, 'contact_methods': [ - ContactMethod(method='sms', value='8885555555').get_dictionary(), - ContactMethod(method='fax', value='8886666666').get_dictionary() + { + 'method': 'sms', + 'value': '88855555555' + }, + { + 'method': 'fax', + 'value': '8886666666' + } ] } response = self.post( @@ -553,7 +562,7 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): query_string={'form': 'salesforce'}) self.assertEqual(response.status_code, 400) - def test_post_sf_update_project_contact_no_project(self): + def test_update_no_project_sales_force_id(self): """Tests /contact/project/?form=salesforce POST with existing project contact.""" model = self.env.project_contact contact = db.session.query(Contact)\ -- GitLab From 7cb4a7d36a8c69c269bafba718af951adf4748e3 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 19 May 2016 14:40:03 -0400 Subject: [PATCH 053/118] Stop using get_dictionary() for contact method object. --- app/tests/test_contact.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/tests/test_contact.py b/app/tests/test_contact.py index 15b83b2..58e98ba 100644 --- a/app/tests/test_contact.py +++ b/app/tests/test_contact.py @@ -575,9 +575,9 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): contact_method_data = [] for contact_method in contact_methods: - contact_method_object = contact_method.get_dictionary() - contact_method_object.pop("id") - contact_method_data.append(contact_method_object) + contact_method_data.append({ + 'method': contact_method.method, + 'value': contact_method.value }) data = { 'contact': contact.get_dictionary(), -- GitLab From 0cad2464ca252a2abb22f03a9c1ba3ef01a37eb3 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 19 May 2016 16:18:30 -0400 Subject: [PATCH 054/118] Making use of the existing dictionary on update for note object and include project salesforce id in request since salesforce is posting using the sfid. --- app/controllers/note.py | 25 +++++++++++++++++++++++++ app/tests/test_note.py | 21 +++++++++------------ 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/app/controllers/note.py b/app/controllers/note.py index 5f8f734..cc6cc68 100644 --- a/app/controllers/note.py +++ b/app/controllers/note.py @@ -2,6 +2,7 @@ from app.lib.database import db from app.controllers.base import SalesforceObjectController from app.models.note import Note +from app.model.project import Project from app.forms.note import NoteForm @@ -14,3 +15,27 @@ class NoteController(SalesforceObjectController): def get_form(self, filter_data): """Return the note form.""" return NoteForm + + def post(self, data, filter_data): + """ Post a new model. + If the post is from salesforce, insert if salesforce id + is new and update if it already exist. If post is not from + salesforce treat as regular Restful POST request + """ + if filter_data.get('form') != 'salesforce': + return super(NoteController, self).post(data, filter_data) + else: + # Create/modify the object. + try: + project = db.session.query(Project)\ + .filter( + Project.sales_force_id == data['project_sales_force_id'])\ + .first() + except KeyError: + raise BadRequest( + 'When posting a note from salesforce, please ' + + 'include its associated project salesforce id.') + + data.update({'project_id': project.id}) + + return super(NoteController, self).post(data, filter_data) diff --git a/app/tests/test_note.py b/app/tests/test_note.py index dee3900..0a5b79f 100644 --- a/app/tests/test_note.py +++ b/app/tests/test_note.py @@ -162,10 +162,6 @@ class TestNote(RestTestCase): model = self.env.note self._test_delete(model.id) -class TestSalesForceNoteAuth(UnprotectedRestTestCase): - """Tests authentication /note/?form=salesforce POST endpoint.""" - url = '/note/' - Model = Note class TestSalesForceNote(UnprotectedRestTestCase): """Tests the /note/?form=salesforce POST endpoint.""" @@ -182,9 +178,11 @@ class TestSalesForceNote(UnprotectedRestTestCase): def test_create(self): """Tests /note/?form=salesforce POST create with a note.""" + project = self.env.project + data = { 'sales_force_id': 'test', - 'project_id': self.env.project.id, + 'project_sales_force_id': project.sales_force_id, 'posted': arrow.get().isoformat(), 'title': 'Test', 'content': 'This is a test note.', @@ -194,11 +192,10 @@ class TestSalesForceNote(UnprotectedRestTestCase): def test_update(self): """Tests /note/?form=salesforce POST update with a note.""" model = self.env.note_with_name - data = { - 'sales_force_id': model.sales_force_id, - 'project_id': model.project_id, - 'posted': model.posted.isoformat(), - 'title': model.title, - 'content': 'This is an update of test note.', - 'poster_name': model.poster_name} + data = model.get_dictionary() + project = db.session.query(Project)\ + .filter(Project.id == model.project_id)\ + .first() + data.update({'project_sales_force_id': project.sales_force_id}) + data.update({'content': 'This is an update of test note.'}) self._test_post(data, {'form': 'salesforce'}) -- GitLab From e31d27f9abe22a2cc5d3d32fdaf7369884edb11c Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 19 May 2016 16:41:34 -0400 Subject: [PATCH 055/118] Remove unused test case in test-place. --- app/tests/test_place.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/tests/test_place.py b/app/tests/test_place.py index 0bc2583..ec5b70b 100644 --- a/app/tests/test_place.py +++ b/app/tests/test_place.py @@ -242,11 +242,6 @@ class TestAddress(RestTestCase): response = self.delete(self.url + str(model.id)) self.assertEqual(response.status_code, 405) -class TestSalesForcePlaceAuth(UnprotectedRestTestCase): - """Tests authentication /place/?form=salesforce POST endpoint.""" - url = '/place/' - Model = Place - class TestSalesForcePlace(UnprotectedRestTestCase): """Tests the /place/?form=salesforce POST endpoint.""" url = '/place/' -- GitLab From 0f9f7c15d9cf70a29a0ed8167ffce688460f4672 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 19 May 2016 17:14:14 -0400 Subject: [PATCH 056/118] Modify views to use salesforce role instead of salesforce_project_post --- app/views/client.py | 2 +- app/views/contact.py | 2 +- app/views/note.py | 2 +- app/views/place.py | 2 +- app/views/project.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/views/client.py b/app/views/client.py index 828f20e..ee83c9f 100644 --- a/app/views/client.py +++ b/app/views/client.py @@ -32,7 +32,7 @@ class ClientView(UnprotectedRestView): need = app_need if form == 'salesforce': - need &= RoleNeed('project_post_salesforce') + need &= RoleNeed('salesforce') else: need &= auth_need diff --git a/app/views/contact.py b/app/views/contact.py index 9e1318f..a3f92d1 100644 --- a/app/views/contact.py +++ b/app/views/contact.py @@ -33,7 +33,7 @@ class ContactView(UnprotectedRestView): need = app_need if form == 'salesforce': - need &= RoleNeed('project_post_salesforce') + need &= RoleNeed('salesforce') else: need &= auth_need diff --git a/app/views/note.py b/app/views/note.py index 64fde92..6a2f847 100644 --- a/app/views/note.py +++ b/app/views/note.py @@ -32,7 +32,7 @@ class NoteView(UnprotectedRestView): need = app_need if form == 'salesforce': - need &= RoleNeed('project_post_salesforce') + need &= RoleNeed('salesforce') else: need &= auth_need diff --git a/app/views/place.py b/app/views/place.py index ca46102..74cac89 100644 --- a/app/views/place.py +++ b/app/views/place.py @@ -32,7 +32,7 @@ class PlaceView(UnprotectedRestView): need = app_need if form == 'salesforce': - need &= RoleNeed('project_post_salesforce') + need &= RoleNeed('salesforce') else: need &= auth_need diff --git a/app/views/project.py b/app/views/project.py index 3f090c3..7fdca52 100644 --- a/app/views/project.py +++ b/app/views/project.py @@ -32,7 +32,7 @@ class ProjectView(UnprotectedRestView): need = app_need if form == 'salesforce': - need &= RoleNeed('project_post_salesforce') + need &= RoleNeed('salesforce') else: need &= auth_need -- GitLab From e4c5f4dbfb5ec5f2408205ab1a3db8442e04a6e7 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 19 May 2016 17:45:17 -0400 Subject: [PATCH 057/118] Remove unnecessary test classes. --- app/tests/test_contact.py | 5 ----- app/tests/test_project.py | 7 ------- 2 files changed, 12 deletions(-) diff --git a/app/tests/test_contact.py b/app/tests/test_contact.py index 58e98ba..e8aa515 100644 --- a/app/tests/test_contact.py +++ b/app/tests/test_contact.py @@ -361,11 +361,6 @@ class TestProjectContact(RestTestCase): model = self.env.project_contact self._test_delete(model.id) -class TestSalesForceContactAuth(UnprotectedRestTestCase): - """Tests authentication /contact/?form=salesforce POST endpoint.""" - url = '/contact/' - Model = Contact - class TestSalesForceContact(UnprotectedRestTestCase): """Tests the /note/?form=salesforce POST endpoint.""" url = '/contact/' diff --git a/app/tests/test_project.py b/app/tests/test_project.py index 844abf9..611b873 100644 --- a/app/tests/test_project.py +++ b/app/tests/test_project.py @@ -199,13 +199,6 @@ class TestProject(RestTestCase): response = self.delete(self.url + str(model.id)) self.assertEqual(response.status_code, 405) - -class TestSalesForceProjectAuth(UnprotectedRestTestCase): - """Tests authentication for the /project/?form=salesforce POST endpoint.""" - url = '/project/' - Model = Project - - class TestSalesForceProject(UnprotectedRestTestCase): """Tests the /project/?form=salesforce POST endpoint.""" url = '/project/' -- GitLab From 7cd7ddb420e9cbae48a1cdfe91a26acec26fa960 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 20 May 2016 11:05:49 -0400 Subject: [PATCH 058/118] Remove unnecessary elses. --- app/controllers/base.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/app/controllers/base.py b/app/controllers/base.py index 4c0c349..3ec5a25 100644 --- a/app/controllers/base.py +++ b/app/controllers/base.py @@ -155,19 +155,18 @@ class SalesforceObjectController(RestController): """ if filter_data.get('form') != 'salesforce': return super(SalesforceObjectController, self).post(data, filter_data) - else: - # Create/modify the object. - try: - model = db.session.query(self.Model)\ - .filter( - self.Model.sales_force_id == data['sales_force_id'])\ - .first() - except KeyError: - raise BadRequest( - 'When posting an object from salesforce, please ' + - 'include its salesforce id.') - if model: - data.update({'id': model.id}) - return self.put(model.id, data, filter_data) - else: - return super(SalesforceObjectController, self).post(data, filter_data) + # Create/modify the object. + try: + model = db.session.query(self.Model)\ + .filter( + self.Model.sales_force_id == data['sales_force_id'])\ + .first() + except KeyError: + raise BadRequest( + 'When posting an object from salesforce, please ' + + 'include its salesforce id.') + if model: + data.update({'id': model.id}) + return self.put(model.id, data, filter_data) + + return super(SalesforceObjectController, self).post(data, filter_data) -- GitLab From 233edb8facff0684e5b47754fd9968112d712c6e Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Mon, 23 May 2016 14:52:08 -0400 Subject: [PATCH 059/118] Add a postGIS point validator. --- app/forms/validators.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/forms/validators.py b/app/forms/validators.py index 9a1889d..005539c 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -4,3 +4,7 @@ import wtforms as wtf # A zip code validator. zip_ = wtf.validators.Regexp(r'^\d{5}(-\d{4})?$') + + +# A postGIS point validator. For example, POINT(-70 40). +point = wtf.validators.Regexp(r'POINT\(\-?\d{1,3}(\.\d+)? \-?\d{1,3}(\.\d+)?\)') -- GitLab From 91762b8fe47cb5407bbd7d2c85acac55c3e790d6 Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Mon, 23 May 2016 14:52:18 -0400 Subject: [PATCH 060/118] Add a location form. --- app/forms/base.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/forms/base.py b/app/forms/base.py index 1be257d..8118e93 100644 --- a/app/forms/base.py +++ b/app/forms/base.py @@ -22,3 +22,8 @@ class AddressForm(object): country = wtf.StringField( validators=[wtf.validators.AnyOf(geography.countries)]) postal_code = wtf.StringField(validators=[validators.zip_]) + + +class LocationForm(object): + """A form for validating locations.""" + point = wtf.StringField(validators=[validators.point]) -- GitLab From bfa78831848f350e37c9ddcb56a1485c0ea2464b Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Tue, 24 May 2016 16:57:00 -0400 Subject: [PATCH 061/118] Add json serialization of WKT/WKB elements. --- app/models/base.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/models/base.py b/app/models/base.py index 5b472fc..65bc0b8 100644 --- a/app/models/base.py +++ b/app/models/base.py @@ -4,6 +4,7 @@ import uuid from sqlalchemy.sql import func from sqlalchemy.ext.declarative import declared_attr from geoalchemy2 import Geometry +from geoalchemy2.elements import WKBElement, WKTElement from ..lib.database import db from ..lib import geography @@ -44,6 +45,8 @@ class Model(object): # If we cast decimals to floats, we end up with rounding # errors. Better to render them out to strings. value = str(value) + elif isinstance(value, (WKBElement, WKTElement)): + value = value.desc d[key] = value return d -- GitLab From 5ea052ff12a8f9dbf9b26fea5d8403d93b2ee449 Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Wed, 25 May 2016 10:53:42 -0400 Subject: [PATCH 062/118] Use ST_AsText() to get geoalchemy point text. --- app/models/base.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/models/base.py b/app/models/base.py index 65bc0b8..536a506 100644 --- a/app/models/base.py +++ b/app/models/base.py @@ -1,8 +1,10 @@ import arrow import decimal import uuid + from sqlalchemy.sql import func from sqlalchemy.ext.declarative import declared_attr + from geoalchemy2 import Geometry from geoalchemy2.elements import WKBElement, WKTElement @@ -46,7 +48,7 @@ class Model(object): # errors. Better to render them out to strings. value = str(value) elif isinstance(value, (WKBElement, WKTElement)): - value = value.desc + value = value.ST_AsText() d[key] = value return d -- GitLab From 75b8cfe70deb8ffd7a6e2bcd593f510d0f99c37b Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Wed, 25 May 2016 10:59:22 -0400 Subject: [PATCH 063/118] Fix ST_AsText() usage. --- app/models/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/base.py b/app/models/base.py index 536a506..8eef92f 100644 --- a/app/models/base.py +++ b/app/models/base.py @@ -48,7 +48,7 @@ class Model(object): # errors. Better to render them out to strings. value = str(value) elif isinstance(value, (WKBElement, WKTElement)): - value = value.ST_AsText() + value = db.session.scalar(value.ST_AsText()) d[key] = value return d -- GitLab From cbdd8158fc1f144f2cbedc94ca19b412390c2162 Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Wed, 25 May 2016 17:00:46 -0400 Subject: [PATCH 064/118] Add an external model. --- app/models/base.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/models/base.py b/app/models/base.py index 8eef92f..835e50e 100644 --- a/app/models/base.py +++ b/app/models/base.py @@ -80,3 +80,9 @@ class Address(object): class Location(object): """A physical location (with latitude, longitude, and elevation).""" point = db.Column(Geometry('POINT'), nullable=False) + + +class External(object): + """An external-facing model. These have a UUID key.""" + key = db.Column( + columns.GUID(), index=True, nullable=False, default=uuid.uuid4) -- GitLab From 9ddb81da1eb8b04da37072745a922632a36dd320 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 27 May 2016 15:39:38 -0400 Subject: [PATCH 065/118] Correctly import Project model in note controller and note test. --- app/controllers/note.py | 2 +- app/tests/test_note.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/note.py b/app/controllers/note.py index cc6cc68..ae609b4 100644 --- a/app/controllers/note.py +++ b/app/controllers/note.py @@ -2,7 +2,7 @@ from app.lib.database import db from app.controllers.base import SalesforceObjectController from app.models.note import Note -from app.model.project import Project +from app.models.project import Project from app.forms.note import NoteForm diff --git a/app/tests/test_note.py b/app/tests/test_note.py index 0a5b79f..24570c9 100644 --- a/app/tests/test_note.py +++ b/app/tests/test_note.py @@ -7,6 +7,7 @@ from app.lib.service import services from app.lib.database import db from app.tests.base import RestTestCase, UnprotectedRestTestCase from app.models.note import Note +from app.models.project import Project class TestNote(RestTestCase): -- GitLab From ee23ca5bd5bb14993cadec5205576c9d55e4a0de Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Tue, 31 May 2016 13:22:52 -0400 Subject: [PATCH 066/118] Fix typo in the base test case. --- app/tests/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/tests/base.py b/app/tests/base.py index 7689100..7419eb8 100644 --- a/app/tests/base.py +++ b/app/tests/base.py @@ -197,7 +197,7 @@ class UnprotectedRestTestCase(TestCase): response_data = self.parse_data(self.put( self.url + str(id_), data=data, - query_String=filter_data)) + query_string=filter_data)) return response_data -- GitLab From eb8346c7aa7cc5709c4d74d36f91b2ae540740ca Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Tue, 31 May 2016 13:23:06 -0400 Subject: [PATCH 067/118] Add missing argument in the rest controller put method. --- app/controllers/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/base.py b/app/controllers/base.py index 9cfa0bf..f52e09a 100644 --- a/app/controllers/base.py +++ b/app/controllers/base.py @@ -100,7 +100,7 @@ class RestController(object): def put(self, id_, data, filter_data): """Change an existing model using an id and dictionary data.""" - model = self.get(id_) + model = self.get(id_, filter_data) form = self.get_form(filter_data)(formdata=MultiDict(data)) if not str(form.id.data) == str(id_): -- GitLab From 87d645ac7a2bcb6b45aa04c36b9d02ceb8d97419 Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Thu, 2 Jun 2016 14:35:30 -0400 Subject: [PATCH 068/118] Case database integrity errors to a string for json serialization. --- app/controllers/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/base.py b/app/controllers/base.py index f52e09a..96da8af 100644 --- a/app/controllers/base.py +++ b/app/controllers/base.py @@ -93,7 +93,7 @@ class RestController(object): commit() except IntegrityError as e: raise ( - BadRequest(e) if current_app.config['DEBUG'] else + BadRequest(str(e)) if current_app.config['DEBUG'] else BadRequest) return model @@ -114,7 +114,7 @@ class RestController(object): commit() except IntegrityError as e: raise ( - BadRequest(e) if current_app.config['DEBUG'] else + BadRequest(str(e)) if current_app.config['DEBUG'] else BadRequest) return model -- GitLab From 809e1a484bfd08dad7af17f64fca93f215dd36d1 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Thu, 2 Jun 2016 17:21:52 -0400 Subject: [PATCH 069/118] Fix project tests. When client sfid or project location sfid changes on opportunity said object will be embedded. --- app/tests/test_project.py | 127 +++++++++++++++----------------------- 1 file changed, 51 insertions(+), 76 deletions(-) diff --git a/app/tests/test_project.py b/app/tests/test_project.py index 611b873..1c12d7c 100644 --- a/app/tests/test_project.py +++ b/app/tests/test_project.py @@ -212,8 +212,8 @@ class TestSalesForceProject(UnprotectedRestTestCase): self.app.config['SALESFORCE_KEY'], 'referer': self.app.config['SALESFORCE_REFERRER']} - def test_post_sf_client(self): - """Tests /project/?form=salesforce POST with a new client.""" + def test_post_sf_new_project(self): + """Tests /project/?form=salesforce POST.""" client_data = {'sales_force_id': 'test', 'name': 'test'} place = self.env.place data = { @@ -235,27 +235,35 @@ class TestSalesForceProject(UnprotectedRestTestCase): .first()) def test_post_sf_change_client(self): - """Tests /project/?form=salesforce POST with a modified client.""" - client = self.env.client - client_data = client.get_dictionary() - client_data.pop('id') - client_data.update({'name': 'foo'}) - place = self.env.place - data = { - 'sales_force_id': 'test', - 'name': 'test', - 'state': 'pending', - 'client': client_data, - 'place': place.get_dictionary(), - 'address': place.address.get_dictionary()} + """Tests /project/?form=salesforce POST with a new client.""" + + # New Client + client_data = {'sales_force_id': 'test1', 'name': 'foo'} + project = self.env.project + data = project.get_dictionary() + data.pop('id') + data.pop('client_id') + place_id = data.pop('place_id') + + data.update({'client': client_data}) + + place = db.session.query(Place)\ + .filter(Place.id==place_id)\ + .first() + + place_sales_force_id = place.get_dictionary()['sales_force_id'] + data.update({ + 'place': place_sales_force_id + }) + + response_data = self._test_post(data, {'form': 'salesforce'}) - # Check that the client was modified as expected. + # Check that the new client was added as expected. self.assertTrue( db.session.query(Client)\ .join(Project, Project.client_id == Client.id) .filter(and_( Project.id == response_data['id'], - Client.id == client.id, Client.name == client_data['name']))\ .first()) @@ -280,79 +288,46 @@ class TestSalesForceProject(UnprotectedRestTestCase): # those are already tested in the /client/ endpoint, so there's no need # to test them here. - def test_post_sf_new_place(self): - """Tests /project/?form=salesforce POST with a new place and address. - """ - place_data = {'sales_force_id': 'test', 'name': 'test'} - address_data = { - 'street_address': '15 MetroTech Center', - 'city': 'Brooklyn', - 'county': 'Kings', - 'state': 'NY', - 'country': 'United States of America', - 'postal_code': '11210'} - data = { - 'sales_force_id': 'test', - 'name': 'test', - 'state': 'pending', - 'client': self.env.client.get_dictionary(), - 'place': place_data, - 'address': address_data} - response_data = self._test_post(data, {'form': 'salesforce'}) - - filters = [Project.id == response_data['id']] - filters += [ - getattr(Place, k) == place_data.get(k) - for k in place_data.keys()] - filters += [ - getattr(Address, k) == address_data.get(k) - for k in address_data.keys()] - - self.assertTrue( - db.session.query(Address)\ - .join(Place, Place.address_id == Address.id)\ - .join(Project, Project.place_id == Place.id)\ - .filter(and_(*filters))\ - .first()) - def test_post_sf_change_place(self): - """Tests /project/?form=salesforce POST with a modified place and + """Tests /project/?form=salesforce POST with a new place and address. """ - place = self.env.place - place_data = place.get_dictionary() - place_data.pop('id') - new_place_data = {'name': 'foo'} - place_data.update(new_place_data) - address_data = place.address.get_dictionary() - address_data.pop('id') - - new_address_data = { + place_data = {'sales_force_id': 'test1', 'name': 'test1'} + address_data = { 'street_address': '111 N Fake St', 'city': 'Raleigh', 'county': 'Wake', 'state': 'NC', 'postal_code': '27615'} - address_data.update(new_address_data) - data = { - 'sales_force_id': 'test', - 'name': 'test', - 'state': 'pending', - 'client': self.env.client.get_dictionary(), - 'place': place_data, - 'address': address_data} + + project = self.env.project + data = project.get_dictionary() + data.pop('id') + client_id = data.pop('client_id') + data.pop('place_id') + data.update({ + 'place' : place_data, + 'address': address_data}) + + client = db.session.query(Client)\ + .filter(Client.id==client_id)\ + .first() + + client_sales_force_id = client.get_dictionary()['sales_force_id'] + + data.update({ + 'client': client_sales_force_id + }) + response_data = self._test_post(data, {'form': 'salesforce'}) - filters = [ - Project.id == response_data['id'], - Place.id == place.id, - Address.id == place.address.id] + filters = [Project.id == response_data['id']] filters += [ getattr(Place, k) == place_data.get(k) - for k in new_place_data.keys()] + for k in place_data.keys()] filters += [ getattr(Address, k) == address_data.get(k) - for k in new_address_data.keys()] + for k in address_data.keys()] self.assertTrue( db.session.query(Address)\ -- GitLab From 7d79cb72bc08ff3658d54e9345c84d22768334a7 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 3 Jun 2016 04:44:11 -0400 Subject: [PATCH 070/118] Printing out json response in parser error for easier debugging during testing. --- app/tests/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/tests/base.py b/app/tests/base.py index 7419eb8..961a4e0 100644 --- a/app/tests/base.py +++ b/app/tests/base.py @@ -74,7 +74,7 @@ class TestCase(testing.TestCase): def parse_data(self, response): """Checks that a response is valid and returns its data.""" - self.assertIn(response.status_code, {200, 201, 204}) + self.assertIn(response.status_code, {200, 201, 204}, response.json) data = json.loads(response.data.decode('utf-8')) self.assertIn('data', data) return data['data'] -- GitLab From 97fe2d4eb83198ce71847bad42b2b09807c4acc6 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 3 Jun 2016 04:48:46 -0400 Subject: [PATCH 071/118] Fix tests for project update on salesforce. Client id and location id shouldn't be contants because one can change the account and location associated to an opportunity in salesforce. --- app/tests/test_project.py | 153 +++++++++++++++++++++++++++++++++----- 1 file changed, 136 insertions(+), 17 deletions(-) diff --git a/app/tests/test_project.py b/app/tests/test_project.py index 1c12d7c..b3c6b56 100644 --- a/app/tests/test_project.py +++ b/app/tests/test_project.py @@ -178,20 +178,18 @@ class TestProject(RestTestCase): self.assertEqual(response.status_code, 400) def test_put_client_id(self): - """Tests /project/ PUT with a client id. It should 400.""" + """Tests /project/ PUT with a client id.""" model = self.env.project data = model.get_dictionary() data.update({'client_id': self.env.client.id}) - response = self.put(self.url + str(model.id), data=data) - self.assertEqual(response.status_code, 400) + self._test_put(model.id, data) def test_put_place_id(self): - """Tests /project/ PUT with a place id. It should 400.""" + """Tests /project/ PUT with a place id.""" model = self.env.project data = model.get_dictionary() data.update({'place_id': self.env.place.id}) - response = self.put(self.url + str(model.id), data=data) - self.assertEqual(response.status_code, 400) + self._test_put(model.id, data) def test_delete(self): """Tests /project/ DELETE. It should 405.""" @@ -235,29 +233,83 @@ class TestSalesForceProject(UnprotectedRestTestCase): .first()) def test_post_sf_change_client(self): - """Tests /project/?form=salesforce POST with a new client.""" - - # New Client - client_data = {'sales_force_id': 'test1', 'name': 'foo'} + """Tests /project/?form=salesforce POST with a change in orphaned client.""" project = self.env.project data = project.get_dictionary() data.pop('id') - data.pop('client_id') + client_id = data.pop('client_id') place_id = data.pop('place_id') + client = db.session.query(Client)\ + .filter(Client.id==client_id)\ + .first() + + client_data = client.get_dictionary() + client_data.pop('id') + client_data.update({'name': 'foo'}) + data.update({'client': client_data}) place = db.session.query(Place)\ .filter(Place.id==place_id)\ .first() + place_data = place.get_dictionary() + place_data.pop('id') + address_id = place_data.pop('address_id') + + + address = db.session.query(Address)\ + .filter(Address.id==address_id)\ + .first() + address_data = address.get_dictionary() + address_data.pop('id') - place_sales_force_id = place.get_dictionary()['sales_force_id'] data.update({ - 'place': place_sales_force_id + 'place': place_data, + 'address': address_data }) + response_data = self._test_post(data, {'form': 'salesforce'}) + # Check that the new client was added as expected. + self.assertTrue( + db.session.query(Client)\ + .join(Project, Project.client_id == Client.id) + .filter(and_( + Project.id == response_data['id'], + Client.name == client_data['name']))\ + .first()) + + def test_post_sf_new_client(self): + """Tests /project/?form=salesforce POST with a new client.""" + project = self.env.project + data = project.get_dictionary() + data.pop('id') + client_id = data.pop('client_id') + place_id = data.pop('place_id') + + # New Client + client_data = {'sales_force_id': 'test1', 'name': 'foo'} + data.update({'client': client_data}) + + place = db.session.query(Place)\ + .filter(Place.id==place_id)\ + .first() + place_data = place.get_dictionary() + + address_id = place_data.pop('address_id') + address = db.session.query(Address)\ + .filter(Address.id==address_id)\ + .first() + address_data = address.get_dictionary() + address_data.pop('id') + + data.update({ + 'place': place_data, + 'address': address_data + }) response_data = self._test_post(data, {'form': 'salesforce'}) + # Check that the new client was added as expected. self.assertTrue( db.session.query(Client)\ @@ -289,6 +341,68 @@ class TestSalesForceProject(UnprotectedRestTestCase): # to test them here. def test_post_sf_change_place(self): + """Tests /project/?form=salesforce POST with a modified place and + address. + """ + project = self.env.project + data = project.get_dictionary() + data.pop('id') + + place = db.session.query(Place)\ + .filter(Place.id == str(data['place_id']))\ + .first() + + client = db.session.query(Client)\ + .filter(Place.id == str(data['client_id']))\ + .first() + + client_data = client.get_dictionary() + client_data.pop('id') + + place_data = place.get_dictionary() + place_data.pop('id') + + new_place_data = {'name': 'foo'} + place_data.update(new_place_data) + + address_data = place.address.get_dictionary() + address_data.pop('id') + + new_address_data = { + 'street_address': '111 N Fake St', + 'city': 'Raleigh', + 'county': 'Wake', + 'state': 'NC', + 'postal_code': '27615'} + address_data.update(new_address_data) + + data.update({ + 'client': client_data, + 'place': place_data, + 'address': address_data + }) + + response_data = self._test_post(data, {'form': 'salesforce'}) + + filters = [ + Project.id == response_data['id'], + Place.id == place.id, + Address.id == place.address.id] + filters += [ + getattr(Place, k) == place_data.get(k) + for k in new_place_data.keys()] + filters += [ + getattr(Address, k) == address_data.get(k) + for k in new_address_data.keys()] + + self.assertTrue( + db.session.query(Address)\ + .join(Place, Place.address_id == Address.id)\ + .join(Project, Project.place_id == Place.id)\ + .filter(and_(*filters))\ + .first()) + + def test_post_sf_new_place(self): """Tests /project/?form=salesforce POST with a new place and address. """ @@ -298,25 +412,30 @@ class TestSalesForceProject(UnprotectedRestTestCase): 'city': 'Raleigh', 'county': 'Wake', 'state': 'NC', + 'country': 'United States of America', 'postal_code': '27615'} project = self.env.project data = project.get_dictionary() data.pop('id') + client_id = data.pop('client_id') - data.pop('place_id') + + place_id = data.pop('place_id') + data.update({ 'place' : place_data, 'address': address_data}) client = db.session.query(Client)\ - .filter(Client.id==client_id)\ + .filter(Client.id == str(client_id))\ .first() - client_sales_force_id = client.get_dictionary()['sales_force_id'] + client_data = client.get_dictionary() + client_data.pop('id') data.update({ - 'client': client_sales_force_id + 'client': client_data }) response_data = self._test_post(data, {'form': 'salesforce'}) -- GitLab From 12b9c8b518c2628c9f7bc748432db13366603627 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 3 Jun 2016 05:18:18 -0400 Subject: [PATCH 072/118] Modify project controller to always have the embedded objects. Only update address if it has changed allow putting when client salesforce id changes or when place salesforce id changed. --- app/controllers/project.py | 123 ++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 57 deletions(-) diff --git a/app/controllers/project.py b/app/controllers/project.py index fef8930..f3a0e32 100644 --- a/app/controllers/project.py +++ b/app/controllers/project.py @@ -22,7 +22,7 @@ from app.forms.project import ProjectForm class ProjectController(RestController): """The project controller.""" Model = Project - constant_fields = ['sales_force_id', 'client_id', 'place_id'] + constant_fields = ['sales_force_id'] filters = { 'q': lambda d: and_(*[ Project.name.ilike('%{}%'.format(term)) @@ -40,7 +40,7 @@ class ProjectController(RestController): self.commit() def post(self, data, filter_data): - """Post a new model. + """Post a new model or updates a model(only from salesforce). This logs a model state change after saving a new model. It includes support for salesforce, which dumps all submodels (e.g @@ -53,9 +53,71 @@ class ProjectController(RestController): may be created even if the request 400s due to a problem with the project itself. """ - # A project has to have a client and a place, so we create these models - # before posting the project when performing a bulk upload. + # A project has to have a client and a place, so we create/change these models + # before posting the project when performing an embedded upload. if filter_data.get('form') == 'salesforce': + try: + client_data = data['client'] + place_data = data['place'] + address_data = data['address'] + except KeyError: + raise BadRequest( + 'SalesForce projects require an embedded client, place, ' + + 'and address.') + + # Create/modify the client. + try: + client = db.session.query(Client)\ + .filter( + Client.sales_force_id == client_data['sales_force_id'])\ + .first() + except KeyError: + raise BadRequest( + 'When embedding a client in a salesforce project, please ' + + 'include its salesforce id.') + + if client: + client_data.update({'id': client.id}) + client = ClientController().put(client.id, client_data, {}) + else: + client = ClientController().post(client_data, {}) + data.update({'client_id': client.id}) + + # Create/modify the Place and Address(if necessary) + try: + place = db.session.query(Place)\ + .filter( + Place.sales_force_id == place_data['sales_force_id'])\ + .first() + except KeyError: + raise BadRequest( + 'When embedding a place in a salesforce project, please ' + + 'include its salesforce id.') + + address = place.address if place else None + if address: + address_different = False + for k in address_data.keys(): + if not getattr(Address, k) == address_data.get(k): + address_different = True + break + if address_different: + address_data.update({'id': address.id}) + address = AddressController().put(address.id, address_data, {}) + else: + address = AddressController().post(address_data, {}) + + place_data.update({'address_id': address.id}) + + if place: + place_data.update({'id': place.id}) + place = PlaceController().put(place.id, place_data, {}) + else: + place = PlaceController().post(place_data, {}) + + data.update({'place_id': place.id}) + + # Create/modify the project try: model = db.session.query(self.Model)\ .filter( @@ -65,63 +127,10 @@ class ProjectController(RestController): raise BadRequest( 'When posting a project from salesforce, please ' + 'include its salesforce id.') - if model: data.update({'id': model.id}) model = self.put(model.id, data, filter_data) else: - try: - client_data = data['client'] - place_data = data['place'] - address_data = data['address'] - except KeyError: - raise BadRequest( - 'SalesForce projects require an embedded client, place, ' + - 'and address.') - - # Create/modify the client. - try: - client = db.session.query(Client)\ - .filter( - Client.sales_force_id == client_data['sales_force_id'])\ - .first() - except KeyError: - raise BadRequest( - 'When embedding a client in a salesforce project, please ' + - 'include its salesforce id.') - if client: - client_data.update({'id': client.id}) - client = ClientController().put(client.id, client_data, {}) - else: - client = ClientController().post(client_data, {}) - data.update({'client_id': client.id}) - - # Create/modify the place and address. - try: - place = db.session.query(Place)\ - .filter( - Place.sales_force_id == place_data['sales_force_id'])\ - .first() - except KeyError: - raise BadRequest( - 'When embedding a place in a salesforce project, please ' + - 'include its salesforce id.') - - address = place.address if place else None - if address: - address_data.update({'id': address.id}) - address = AddressController().put(address.id, address_data, {}) - else: - address = AddressController().post(address_data, {}) - place_data.update({'address_id': address.id}) - - if place: - place_data.update({'id': place.id}) - place = PlaceController().put(place.id, place_data, {}) - else: - place = PlaceController().post(place_data, {}) - data.update({'place_id': place.id}) - model = super(ProjectController, self).post(data, filter_data) else: model = super(ProjectController, self).post(data, filter_data) -- GitLab From 58226abe98715528fea3c60f03f06af7f4d3ab4f Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 3 Jun 2016 16:05:19 -0400 Subject: [PATCH 073/118] Fix typo and change to address. --- app/controllers/project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/project.py b/app/controllers/project.py index f3a0e32..e4d23aa 100644 --- a/app/controllers/project.py +++ b/app/controllers/project.py @@ -98,7 +98,7 @@ class ProjectController(RestController): if address: address_different = False for k in address_data.keys(): - if not getattr(Address, k) == address_data.get(k): + if not getattr(address, k) == address_data.get(k): address_different = True break if address_different: -- GitLab From 2c9b4bde426afa58668e594cdb353f4fbd8417c7 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 3 Jun 2016 16:09:12 -0400 Subject: [PATCH 074/118] Change the expected role to salesforce for contact post method. --- app/views/contact.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/contact.py b/app/views/contact.py index a3f92d1..eaed8ca 100644 --- a/app/views/contact.py +++ b/app/views/contact.py @@ -83,7 +83,7 @@ class ProjectContactView(UnprotectedRestView): need = app_need if form == 'salesforce': - need &= RoleNeed('project_post_salesforce') + need &= RoleNeed('salesforce') else: need &= auth_need -- GitLab From 102ace95c80a6ccc16582a74d65300e46b68f3eb Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Mon, 13 Jun 2016 12:34:23 -0400 Subject: [PATCH 075/118] Fix the exception content for the put() method in the base controller. --- app/controllers/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/base.py b/app/controllers/base.py index 96da8af..8d5ad7a 100644 --- a/app/controllers/base.py +++ b/app/controllers/base.py @@ -106,7 +106,7 @@ class RestController(object): if not str(form.id.data) == str(id_): raise BadRequest(['The id in the model and the uri do not match.']) if not form.validate(): - raise BadRequest(form.errors.values()) + raise BadRequest(form.errors) form.populate_obj(model) db.session.add(model) -- GitLab From 3b4d2cae702f84e4e5a6147d4b25aa6fda0aaeca Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Wed, 15 Jun 2016 14:30:50 -0400 Subject: [PATCH 076/118] Add a custom string field that does not coerce null values. --- app/forms/fields.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/forms/fields.py b/app/forms/fields.py index be879ff..f911d45 100644 --- a/app/forms/fields.py +++ b/app/forms/fields.py @@ -4,6 +4,18 @@ import arrow import wtforms as wtf +class String(wtf.StringField): + """A string field implementation that supports null values.""" + def process_formdata(self, valuelist): + if valuelist and valuelist[0] is not None: + self.data = valuelist[0] + else: + self.data = None + + def _value(self): + return str(self.data) if self.data is not None else None + + class Arrow(wtf.Field): """A field that accepts any input that arrow finds an acceptable datetime. """ -- GitLab From 99edace40605395a8893b81c455cfc73fa9b532c Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Wed, 15 Jun 2016 14:31:09 -0400 Subject: [PATCH 077/118] Add a custom AnyOf validator that permits null values. --- app/forms/validators.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/forms/validators.py b/app/forms/validators.py index 005539c..7ef79bb 100644 --- a/app/forms/validators.py +++ b/app/forms/validators.py @@ -2,6 +2,14 @@ import wtforms as wtf +class AnyOf(wtf.validators.AnyOf): + """A custom AnyOf validator that accepts null values.""" + def __call__(self, form, field): + """Call the parent validator if the field data is not None.""" + if field.data is not None: + super(AnyOf, self).__call__(form, field) + + # A zip code validator. zip_ = wtf.validators.Regexp(r'^\d{5}(-\d{4})?$') -- GitLab From 970ca6044cfc8a4cfae895d7d54079a2ab84665f Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 13:49:52 -0400 Subject: [PATCH 078/118] Add FormNeed and salesforce_need. --- app/permissions/application.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/app/permissions/application.py b/app/permissions/application.py index ed798dd..75d8191 100644 --- a/app/permissions/application.py +++ b/app/permissions/application.py @@ -108,3 +108,28 @@ class RoleNeed(Permission): return False data = data['data'] return len(data) > 0 + +class FormNeed(Permission): + """Checks that the current requests has the correct form query value. """ + @property + def error(self): + """Return an error based on the role.""" + return Unauthorized( + 'Please authenticate with an application with the {} role.'\ + .format(self.role)) + + def __init__(self, form): + """Initialize with a form.""" + self.form = form + + def is_met(self): + from flask import request + form = request.args.get('form') + return form == self.form + + + +salesforce_need = app_need & ( + (FormNeed('salesforce') & RoleNeed('salesforce')) | + (~FormNeed('salesforce') & auth_need) +) -- GitLab From 9d5b3d5a6faa046712b94a60ffdc24fc5ea2cb52 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 13:50:32 -0400 Subject: [PATCH 079/118] Add base salesforce view. --- app/views/base.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/app/views/base.py b/app/views/base.py index 1a175c5..8297c5c 100644 --- a/app/views/base.py +++ b/app/views/base.py @@ -69,3 +69,29 @@ class RestView(UnprotectedRestView): """A view wrapper for RESTful controllers that _does_ offer API protection. """ decorators = (standard_login_need,) + +class SalesforceObjectView(UnprotectedRestView) + """A view wrapper for Salesforce object that offers API protection""" + def get_controller(self): + """Return a controller instance to use for database interactions.""" + pass + + @standard_login_need + def index(self): + return super(SalesforceObjectView, self).index() + + @standard_login_need + def get(self, id_): + return super(SalesforceObjectView, self).get(id_) + + @salesforce_need + def post(self): + return super(SalesforceObjectView, self).post() + + @standard_login_need + def put(self, id_): + return super(SalesforceObjectView, self).put(id_) + + @standard_login_need + def delete(self, id_): + return super(SalesforceObjectView, self).delete(id_) -- GitLab From 085af7b332e21a9d42ccb2dd57150c4a58789ac9 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 13:53:24 -0400 Subject: [PATCH 080/118] Refactor views to use base salesforce view. --- app/views/client.py | 32 ++------------------ app/views/contact.py | 69 +++----------------------------------------- app/views/note.py | 38 ++---------------------- app/views/place.py | 31 ++------------------ app/views/project.py | 32 ++------------------ 5 files changed, 13 insertions(+), 189 deletions(-) diff --git a/app/views/client.py b/app/views/client.py index ee83c9f..ba0f372 100644 --- a/app/views/client.py +++ b/app/views/client.py @@ -2,11 +2,11 @@ from werkzeug.exceptions import MethodNotAllowed from flask import request -from app.views.base import UnprotectedRestView +from app.views.base import SalesforceObjectView from app.controllers.client import ClientController from app.permissions.application import app_need, RoleNeed -from app.permissions.auth import auth_need, standard_login_need +from app.permissions.auth import auth_need, standard_login_need, salesforce_need class ClientView(UnprotectedRestView): @@ -15,34 +15,6 @@ class ClientView(UnprotectedRestView): """Return an instance of the client controller.""" return ClientController() - @standard_login_need - def index(self): - return super(ClientView, self).index() - - @standard_login_need - def get(self, id_): - return super(ClientView, self).get(id_) - - def post(self): - """Check for an application and either a user or an application with - the project_post_salesforce role (and the salesforce form) before - posting a client. - """ - form = request.args.get('form') - - need = app_need - if form == 'salesforce': - need &= RoleNeed('salesforce') - else: - need &= auth_need - - with need: - return super(ClientView, self).post() - - @standard_login_need - def put(self, id_): - return super(ClientView, self).put(id_) - @standard_login_need def delete(self, id_): """Not implemented.""" diff --git a/app/views/contact.py b/app/views/contact.py index eaed8ca..d90c8da 100644 --- a/app/views/contact.py +++ b/app/views/contact.py @@ -2,53 +2,20 @@ from werkzeug.exceptions import MethodNotAllowed from flask import request -from app.views.base import RestView, UnprotectedRestView +from app.views.base import RestView, SalesforceObjectView from app.controllers.contact import ( ContactController, ContactMethodController, ProjectContactController) from app.permissions.base import FormNeed from app.permissions.application import app_need, RoleNeed -from app.permissions.auth import auth_need, standard_login_need +from app.permissions.auth import auth_need, standard_login_need, salesforce_need -class ContactView(UnprotectedRestView): +class ContactView(SalesforceObjectView): """The contact view.""" def get_controller(self): """Return an instance of the contact controller.""" return ContactController() - @standard_login_need - def index(self): - return super(ContactView, self).index() - - @standard_login_need - def get(self, id_): - return super(ContactView, self).get(id_) - - def post(self): - """Check for an application and either a user or an application with - the project_post_salesforce role (and the salesforce form) before - posting a contact. - """ - form = request.args.get('form') - - need = app_need - if form == 'salesforce': - need &= RoleNeed('salesforce') - else: - need &= auth_need - - with need: - return super(ContactView, self).post() - - @standard_login_need - def put(self, id_): - return super(ContactView, self).put(id_) - - @standard_login_need - def delete(self, id_): - return super(ContactView, self).delete(id_) - - class ContactMethodView(RestView): """The contact method view.""" route_base = '/contact/method/' @@ -58,7 +25,7 @@ class ContactMethodView(RestView): return ContactMethodController() -class ProjectContactView(UnprotectedRestView): +class ProjectContactView(SalesforceObjectView): """A view for the project-contact m2m relationship.""" route_base = '/contact/project/' @@ -66,37 +33,9 @@ class ProjectContactView(UnprotectedRestView): """Return an instance of the project contact controller.""" return ProjectContactController() - @standard_login_need - def index(self): - return super(ProjectContactView, self).index() - - @standard_login_need - def get(self, id_): - return super(ProjectContactView, self).get(id_) - - def post(self): - """Check for an application and either a user or an application with - the project_post_salesforce role (and the salesforce form) before - posting a project conatct. - """ - form = request.args.get('form') - - need = app_need - if form == 'salesforce': - need &= RoleNeed('salesforce') - else: - need &= auth_need - - with need: - return super(ProjectContactView, self).post() - @standard_login_need def put(self, id_): """Not implemented.""" raise MethodNotAllowed( 'Do not modify existing m2m relationships. Delete this and ' + 'create a new one instead.') - - @standard_login_need - def delete(self, id_): - return super(ProjectContactView, self).delete(id_) diff --git a/app/views/note.py b/app/views/note.py index 6a2f847..631582f 100644 --- a/app/views/note.py +++ b/app/views/note.py @@ -2,47 +2,15 @@ from werkzeug.exceptions import MethodNotAllowed from flask import request -from app.views.base import UnprotectedRestView +from app.views.base import SalesforceObjectView from app.controllers.note import NoteController from app.permissions.application import app_need, RoleNeed -from app.permissions.auth import auth_need, standard_login_need +from app.permissions.auth import auth_need, standard_login_need, salesforce_need -class NoteView(UnprotectedRestView): +class NoteView(SalesforceObjectView): """The note view.""" def get_controller(self): """Return an instance of the note controller.""" return NoteController() - - @standard_login_need - def index(self): - return super(NoteView, self).index() - - @standard_login_need - def get(self, id_): - return super(NoteView, self).get(id_) - - def post(self): - """Check for an application and either a user or an application with - the project_post_salesforce role (and the salesforce form) before - posting a note. - """ - form = request.args.get('form') - - need = app_need - if form == 'salesforce': - need &= RoleNeed('salesforce') - else: - need &= auth_need - - with need: - return super(NoteView, self).post() - - @standard_login_need - def put(self, id_): - return super(NoteView, self).put(id_) - - @standard_login_need - def delete(self, id_): - return super(NoteView, self).delete(id_) diff --git a/app/views/place.py b/app/views/place.py index 74cac89..60673c4 100644 --- a/app/views/place.py +++ b/app/views/place.py @@ -3,46 +3,19 @@ from werkzeug.exceptions import MethodNotAllowed from flask import request from app.controllers.place import AddressController, PlaceController -from app.views.base import RestView, UnprotectedRestView +from app.views.base import RestView, SalesforceObjectView from app.permissions.application import app_need, RoleNeed from app.permissions.auth import auth_need, standard_login_need -class PlaceView(UnprotectedRestView): +class PlaceView(SalesforceObjectView): """The Place view.""" def get_controller(self): """Return an instance of the Place controller.""" return PlaceController() @standard_login_need - def index(self): - return super(PlaceView, self).index() - - @standard_login_need - def get(self, id_): - return super(PlaceView, self).get(id_) - - def post(self): - """Check for an application and either a user or an application with - the project_post_salesforce role (and the salesforce form) before - posting a place. - """ - form = request.args.get('form') - - need = app_need - if form == 'salesforce': - need &= RoleNeed('salesforce') - else: - need &= auth_need - - with need: - return super(PlaceView, self).post() - - @standard_login_need - def put(self, id_): - return super(PlaceView, self).put(id_) - def delete(self, id_): """Not implemented""" raise MethodNotAllowed( diff --git a/app/views/project.py b/app/views/project.py index 7fdca52..5116fcf 100644 --- a/app/views/project.py +++ b/app/views/project.py @@ -2,47 +2,19 @@ from werkzeug.exceptions import MethodNotAllowed from flask import request -from app.views.base import UnprotectedRestView +from app.views.base import SalesforceObjectView from app.controllers.project import ProjectController from app.permissions.application import app_need, RoleNeed from app.permissions.auth import auth_need, standard_login_need -class ProjectView(UnprotectedRestView): +class ProjectView(SalesforceObjectView): """The project view.""" def get_controller(self): """Return an instance of the project controller.""" return ProjectController() - @standard_login_need - def index(self): - return super(ProjectView, self).index() - - @standard_login_need - def get(self, id_): - return super(ProjectView, self).get(id_) - - def post(self): - """Check for an application and either a user or an application with - the project_post_salesforce role (and the salesforce form) before - posting a project. - """ - form = request.args.get('form') - - need = app_need - if form == 'salesforce': - need &= RoleNeed('salesforce') - else: - need &= auth_need - - with need: - return super(ProjectView, self).post() - - @standard_login_need - def put(self, id_): - return super(ProjectView, self).put(id_) - def delete(self, id_): """Not implemented""" raise MethodNotAllowed( -- GitLab From b18df6e4c412b75d06643d9f517e80c42ec50b72 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 14:04:56 -0400 Subject: [PATCH 081/118] Fix style issues. --- app/controllers/base.py | 1 + app/controllers/note.py | 1 + app/controllers/place.py | 3 ++- app/controllers/project.py | 22 ++++++++-------------- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/app/controllers/base.py b/app/controllers/base.py index 3ec5a25..8bf9a5d 100644 --- a/app/controllers/base.py +++ b/app/controllers/base.py @@ -149,6 +149,7 @@ class SalesforceObjectController(RestController): """ def post(self, data, filter_data): """ Post a new model. + If the post is from salesforce, insert if salesforce id is new and update if it already exist. If post is not from salesforce treat as regular Restful POST request diff --git a/app/controllers/note.py b/app/controllers/note.py index ae609b4..811ebf1 100644 --- a/app/controllers/note.py +++ b/app/controllers/note.py @@ -18,6 +18,7 @@ class NoteController(SalesforceObjectController): def post(self, data, filter_data): """ Post a new model. + If the post is from salesforce, insert if salesforce id is new and update if it already exist. If post is not from salesforce treat as regular Restful POST request diff --git a/app/controllers/place.py b/app/controllers/place.py index 6dd38d5..d701aa3 100644 --- a/app/controllers/place.py +++ b/app/controllers/place.py @@ -18,7 +18,8 @@ class PlaceController(RestController): def post(self, data, filter_data): """ Post a new model. - If the post is from salesforce use, insert if salesforce id + + When the post is from salesforce, insert the object if salesforce id is new and update if it already exist. If post is not from salesforce treat as regular Restful POST request. diff --git a/app/controllers/project.py b/app/controllers/project.py index e4d23aa..679b02c 100644 --- a/app/controllers/project.py +++ b/app/controllers/project.py @@ -46,11 +46,11 @@ class ProjectController(RestController): includes support for salesforce, which dumps all submodels (e.g client, contacts, ...) in addition to the model itself. - Behavior with the embedded models is a little odd. Rather than - validating everything up front, we validate as we go. Except for - the project itself, an object will successfully save if all - subsequent saves succeeded. For example, this means that a client - may be created even if the request 400s due to a problem with the + Behavior with the embedded models is a little odd. Rather than + validating everything up front, we validate as we go. Except for + the project itself, an object will successfully save if all + subsequent saves succeeded. For example, this means that a client + may be created even if the request 400s due to a problem with the project itself. """ # A project has to have a client and a place, so we create/change these models @@ -81,7 +81,7 @@ class ProjectController(RestController): client = ClientController().put(client.id, client_data, {}) else: client = ClientController().post(client_data, {}) - data.update({'client_id': client.id}) + data.update({'client_id': client.id}) # Create/modify the Place and Address(if necessary) try: @@ -96,14 +96,8 @@ class ProjectController(RestController): address = place.address if place else None if address: - address_different = False - for k in address_data.keys(): - if not getattr(address, k) == address_data.get(k): - address_different = True - break - if address_different: - address_data.update({'id': address.id}) - address = AddressController().put(address.id, address_data, {}) + address_data.update({'id': address.id}) + address = AddressController().put(address.id, address_data, {}) else: address = AddressController().post(address_data, {}) -- GitLab From 9cbf009232021618f9cefd811d2096271f6a6297 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 14:06:42 -0400 Subject: [PATCH 082/118] Refactor selecting or posting contact_method and fix style issues. --- app/controllers/contact.py | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/app/controllers/contact.py b/app/controllers/contact.py index b975e94..fe7eda3 100644 --- a/app/controllers/contact.py +++ b/app/controllers/contact.py @@ -30,6 +30,7 @@ class ContactController(SalesforceObjectController): def post(self, data, filter_data): """ Post a new model. + If the post is from salesforce use, insert if salesforce id is new and update if it already exist. If post is not from salesforce treat as regular Restful POST request @@ -46,20 +47,16 @@ class ContactController(SalesforceObjectController): model = super(ContactController,self).post(data, filter_data) # Prevent salesforce from creating duplicate contact methods. - for contact_method_data in contact_methods_data: - contact_method = db.session.query(ContactMethod)\ - .filter(and_( - ContactMethod.contact_id == model.id, - ContactMethod.method == \ - contact_method_data['method'], - ContactMethod.value == \ - contact_method_data['value']))\ - .first() - if not contact_method: - contact_method_data.update({'contact_id': model.id}) - contact_method = ContactMethodController().post( - contact_method_data, - {}) + contact_method_data.update({'contact_id': model.id}) + contact_method_filters = [ + getattr(ContactMethod, k) == v for k, v in contact_method_data.items()] + # Get a matching contact method or post a new one + contact_method = ( + db.session.query(ContactMethod)\ + .filter(and_(*contact_method_filters))\ + .first() or + ContactMethodController().post(contact_method_data, {}) + ) else: model = super(ContactController, self).post(data, filter_data) @@ -98,6 +95,7 @@ class ProjectContactController(RestController): def post(self, data, filter_data): """ Post a new model. + If the post is from salesforce use, insert if salesforce id is new and update if it already exist. If post is not from salesforce treat as regular Restful POST request @@ -119,19 +117,14 @@ class ProjectContactController(RestController): contact = ContactController().post(contact_data,{'form': filter_data.get('form')}) data.update({'contact_id': contact.id}) - project = db.session.query(Project)\ .filter(Project.sales_force_id == project_sales_force_id)\ .first() - - if not project: raise BadRequest( 'Opportunity out of sync. When posting a project contact from salesforce project ' + 'the Opportunity has to be tracked by the project service.') - data.update({'project_id': project.id}) - model = db.session.query(self.Model)\ .filter(and_( self.Model.contact_id == contact.id , -- GitLab From 1f428572c4fd05cdfaa0deb6835e43421bfdd425 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 14:08:32 -0400 Subject: [PATCH 083/118] Add base salesforce test case. --- app/tests/base.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/tests/base.py b/app/tests/base.py index 961a4e0..ae3cc06 100644 --- a/app/tests/base.py +++ b/app/tests/base.py @@ -74,7 +74,7 @@ class TestCase(testing.TestCase): def parse_data(self, response): """Checks that a response is valid and returns its data.""" - self.assertIn(response.status_code, {200, 201, 204}, response.json) + self.assertIn(response.status_code, {200, 201, 204}) data = json.loads(response.data.decode('utf-8')) self.assertIn('data', data) return data['data'] @@ -267,3 +267,13 @@ class RestTestCase(AppProtectedTestCase): self.app.config['HEADER_AUTH_KEY']: self.fake_app.key, self.app.config['HEADER_AUTH_TOKEN']: self.fake_app.token}) return headers + +class SalesforceTestCase(UnprotectedRestTestCase): + """ Base test case for requests from salesforce.""" + @property + def global_headers(self): + """Add app headers to authenticate a salesforce client.""" + return { + services.config['headers']['app_key']: \ + self.app.config['SALESFORCE_KEY'], + 'referer': self.app.config['SALESFORCE_REFERRER']} -- GitLab From d9a67b8576e1cec32950462fc5d6861aa432e962 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 14:09:54 -0400 Subject: [PATCH 084/118] Add salesforce postable helper function. Add other client that will represent an orphaned client. --- app/tests/environment.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/tests/environment.py b/app/tests/environment.py index ce4b306..60a8fb2 100644 --- a/app/tests/environment.py +++ b/app/tests/environment.py @@ -29,10 +29,21 @@ class Environment(object): commit() return model + def salesforcePostable(self, savedSFObject): + """ Pops the fiels SF objects do not include (id, created, posted). """ + savedSFObject.pop('id') + savedSFObject.pop('created') + savedSFObject.pop('posted') + return savedSFobject + @property def client(self): return self.add(Client(sales_force_id='test', name='test')) + @property + def otherclient(self): + return self.add(Client(sales_force_id='othertest', name='test')) + @property def address(self): return self.add(Address( -- GitLab From b07d18116f0df3f348c8e3f0ba4677bb5c9890f2 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 14:14:50 -0400 Subject: [PATCH 085/118] Use salesforce test case and create dictionary instead of using one generated by the address object. --- app/tests/test_place.py | 48 ++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/app/tests/test_place.py b/app/tests/test_place.py index ec5b70b..1733f17 100644 --- a/app/tests/test_place.py +++ b/app/tests/test_place.py @@ -3,7 +3,7 @@ from sqlalchemy import and_ from app.lib.service import services from app.lib.database import db -from app.tests.base import RestTestCase, UnprotectedRestTestCase +from app.tests.base import RestTestCase, SalesforceTestCase from app.models.project import Project from app.models.place import Place, Address @@ -242,29 +242,20 @@ class TestAddress(RestTestCase): response = self.delete(self.url + str(model.id)) self.assertEqual(response.status_code, 405) -class TestSalesForcePlace(UnprotectedRestTestCase): +class TestSalesForcePlace(SalesforceTestCase): """Tests the /place/?form=salesforce POST endpoint.""" url = '/place/' Model = Place - @property - def global_headers(self): - """Add app headers to authenticate a salesforce client.""" - return { - services.config['headers']['app_key']: \ - self.app.config['SALESFORCE_KEY'], - 'referer': self.app.config['SALESFORCE_REFERRER']} - def test_create(self): """Tests /place/?form=salesforce POST create with an address.""" - address_data = Address( - street_address='15 MetroTech Center', - city='Brooklyn', - county='Kings', - state='NY', - country='United States of America', - postal_code='11210').get_dictionary() - address_data.pop('id') + address_data ={ + 'street_address':'15 MetroTech Center', + 'city':'Brooklyn', + 'county':'Kings', + 'state':'NY', + 'country':'United States of America', + 'postal_code':'11210'} data = { 'sales_force_id': 'test', 'name': 'test place', @@ -273,7 +264,7 @@ class TestSalesForcePlace(UnprotectedRestTestCase): self._test_post(data, {'form': 'salesforce'}) def test_post_sf_new_place_no_address(self): - """ Tests /place/?form=salesforce without an address""" + """ Tests /place/?form=salesforce without an address. It should 400.""" data = { 'sales_force_id': 'test', 'name': 'test place', @@ -287,22 +278,21 @@ class TestSalesForcePlace(UnprotectedRestTestCase): def test_update(self): """Tests /place/?form=salesforce POST update with an address.""" model = self.env.place + address_data = { + 'street_address':'2 MetroTech Center', + 'city':'Brooklyn', + 'county':'Kings', + 'state':'NY', + 'country':'United States of America', + 'postal_code':'11210'} data = { 'sales_force_id': model.sales_force_id, 'name': model.name, - 'address': Address( - street_address='2 MetroTech Center', - city='Brooklyn', - county='Kings', - state='NY', - country='United States of America', - postal_code='11210').get_dictionary() - } + 'address': address_data self._test_post(data, {'form': 'salesforce'}) - def test_post_sf_update_place_no_address(self): - """ Tests /place/?form=salesforce without an address""" + """ Tests /place/?form=salesforce without an address. It should 400.""" model = self.env.place data = { 'sales_force_id': model.sales_force_id, -- GitLab From 72aeeddc473e66346978838f3c134deabad260d2 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 14:34:34 -0400 Subject: [PATCH 086/118] Add negative test cases for create and use salesforce postable. --- app/tests/test_note.py | 106 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 2 deletions(-) diff --git a/app/tests/test_note.py b/app/tests/test_note.py index 24570c9..07eac2d 100644 --- a/app/tests/test_note.py +++ b/app/tests/test_note.py @@ -194,9 +194,111 @@ class TestSalesForceNote(UnprotectedRestTestCase): """Tests /note/?form=salesforce POST update with a note.""" model = self.env.note_with_name data = model.get_dictionary() + data = self.env.salesforcePostable(data) project = db.session.query(Project)\ .filter(Project.id == model.project_id)\ .first() - data.update({'project_sales_force_id': project.sales_force_id}) - data.update({'content': 'This is an update of test note.'}) + data.update({ + 'project_sales_force_id': project.sales_force_id, + 'content': 'This is an update of test note.'}) self._test_post(data, {'form': 'salesforce'}) + + def test_post_no_title(): + """Tests /note/?form=salesforce POST create without note title. This should 400.""" + project = self.env.project + + data = { + 'sales_force_id': 'test', + 'project_sales_force_id': project.sales_force_id, + 'posted': arrow.get().isoformat(), + 'content': 'This is a test note.', + 'poster_name': 'test'} + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_post_no_content(): + """Tests /note/?form=salesforce POST create without content. This should 400.""" + project = self.env.project + + data = { + 'sales_force_id': 'test', + 'project_sales_force_id': project.sales_force_id, + 'posted': arrow.get().isoformat(), + 'title': "Test", + 'poster_name': 'test'} + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_post_no_poster_name(self): + """Tests /note/?form=salesforce POST create without poster_name. This should 400.""" + project = self.env.project + + data = { + 'sales_force_id': 'test', + 'project_sales_force_id': project.sales_force_id, + 'posted': arrow.get().isoformat(), + 'title': 'Test', + 'content': 'This is a test note.'} + + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_post_no_sf_id(self): + """Tests /note/?form=salesforce POST create without sales_force_id. This should 400.""" + project = self.env.project + + data = { + 'project_sales_force_id': project.sales_force_id, + 'posted': arrow.get().isoformat(), + 'title': 'Test', + 'content': 'This is a test note.', + 'poster_name': 'test'} + + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_post_no_project_sf_id(self): + """Tests /note/?form=salesforce POST create without project_sales_force_id. This should 400.""" + project = self.env.project + + data = { + 'sales_force_id': 'test', + 'posted': arrow.get().isoformat(), + 'title': 'Test', + 'content': 'This is a test note.', + 'poster_name': 'test'} + + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_post_no_posted(self): + """Tests /note/?form=salesforce POST create without posted. This should 400.""" + project = self.env.project + + data = { + 'sales_force_id': 'test', + 'project_sales_force_id': project.sales_force_id, + 'title': 'Test', + 'content': 'This is a test note.', + 'poster_name': 'test'} + + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) -- GitLab From ec87cc1d953fe92baf699a244a8c955076d2a5c1 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 14:38:44 -0400 Subject: [PATCH 087/118] Refactor selecting contact method as a list comprehension, use salesforce postable, and add explanation to some comments when returning 404. --- app/tests/test_contact.py | 109 +++++++++++++------------------------- 1 file changed, 36 insertions(+), 73 deletions(-) diff --git a/app/tests/test_contact.py b/app/tests/test_contact.py index e8aa515..29366b3 100644 --- a/app/tests/test_contact.py +++ b/app/tests/test_contact.py @@ -3,7 +3,7 @@ from sqlalchemy import and_ from app.lib.service import services from app.lib.database import db -from app.tests.base import RestTestCase, UnprotectedRestTestCase +from app.tests.base import RestTestCase, SalesforceTestCase from app.models.project import Project from app.models.contact import Contact, ContactMethod, ProjectContact @@ -361,19 +361,11 @@ class TestProjectContact(RestTestCase): model = self.env.project_contact self._test_delete(model.id) -class TestSalesForceContact(UnprotectedRestTestCase): +class TestSalesForceContact(SalesforceTestCase): """Tests the /note/?form=salesforce POST endpoint.""" url = '/contact/' Model = Contact - @property - def global_headers(self): - """Add app headers to authenticate a salesforce client.""" - return { - services.config['headers']['app_key']: \ - self.app.config['SALESFORCE_KEY'], - 'referer': self.app.config['SALESFORCE_REFERRER']} - def test_update_embedded_contact_method(self): """Tests /contact/?form=salesforce POST with existing object.""" model = self.env.contact @@ -382,12 +374,10 @@ class TestSalesForceContact(UnprotectedRestTestCase): 'sales_force_id': model.sales_force_id, 'name': model.name, 'contact_methods': - [ - { - 'method': 'sms', - 'value': '88845555555' - } - ] + [{ + 'method': 'sms', + 'value': '88845555555' + }] } self._test_post(data, {'form': 'salesforce'}) @@ -399,29 +389,19 @@ class TestSalesForceContact(UnprotectedRestTestCase): 'sales_force_id': model.sales_force_id, 'name': 'John Doe', 'contact_methods': - [ - { - 'method': 'sms', - 'value': '88855555555' - } - ] + [{ + 'method': 'sms', + 'value': '88855555555' + }] } self._test_post(data, {'form': 'salesforce'}) -class TestSalesForceProjectContact(UnprotectedRestTestCase): +class TestSalesForceProjectContact(SalesforceTestCase): """Tests the /contact/project/?form=salesforce POST endpoint.""" url = '/contact/project/' Model = ProjectContact - @property - def global_headers(self): - """Add app headers to authenticate a salesforce client.""" - return { - services.config['headers']['app_key']: \ - self.app.config['SALESFORCE_KEY'], - 'referer': self.app.config['SALESFORCE_REFERRER']} - def test_create(self): """Tests /contact/project/?form=salesforce POST with new contact.""" project = self.env.project @@ -431,18 +411,15 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): 'sales_force_id': 'testId' }, 'project_sales_force_id': project.sales_force_id, - 'contact_methods': - [ - { - 'method': 'sms', - 'value': '88855555555' - } - ] + 'contact_methods':[{ + 'method': 'sms', + 'value': '88855555555' + }] } self._test_post(data=data, filter_data={'form': 'salesforce'}) def test_create_no_contact_methods(self): - """Tests /contact/project?form=salesforce POST new project contact without contact methods.""" + """Tests /contact/project?form=salesforce POST new project contact without contact methods. It should 400.""" project = self.env.project data = { 'contact': { @@ -458,21 +435,14 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): self.assertEqual(response.status_code, 400) def test_create_no_contact(self): - """Tests /contact/project?form=salesforce POST new project contact without contact.""" + """Tests /contact/project?form=salesforce POST new project contact without contact. It should 400.""" project = self.env.project data = { 'project_sales_force_id': project.sales_force_id, - 'contact_methods': - [ - { - 'method': 'sms', - 'value': '88855555555' - }, - { - 'method': 'fax', - 'value': '8886666666' - } - ] + 'contact_methods':[{ + 'method': 'sms', + 'value': '88855555555' + }] } response = self.post( self.url, @@ -511,18 +481,22 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): self._test_post(data=data, filter_data={'form': 'salesforce'}) def test_update_no_contact_methods(self): - """Tests /contact/project?form=salesforce POST updating project contact without contact methods""" + """Tests /contact/project?form=salesforce POST updating project contact without contact methods. It should 400.""" model = self.env.project_contact contact = db.session.query(Contact)\ .filter(Contact.id == model.contact_id)\ .first() + contact_data = contact.get_dictionary() + contact_data.pop('id') + contact_data.pop('created') + contact_data.pop('updated') project = db.session.query(Project)\ .filter(Project.id == model.project_id)\ .first() data = { - 'contact': contact.get_dictionary(), + 'contact': contact_data, 'project_sales_force_id': project.sales_force_id} response = self.post( @@ -532,24 +506,17 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): self.assertEqual(response.status_code, 400) def test_update_no_contact(self): - """Tests /contact/project?form=salesforce POST updating project contact without contact.""" + """Tests /contact/project?form=salesforce POST updating project contact without contact. It should 400.""" model = self.env.project_contact project = db.session.query(Project)\ .filter(Project.id == model.project_id)\ .first() data = { 'project': {"sales_force_id": project.sales_force_id}, - 'contact_methods': - [ - { - 'method': 'sms', - 'value': '88855555555' - }, - { - 'method': 'fax', - 'value': '8886666666' - } - ] + 'contact_methods':[{ + 'method': 'sms', + 'value': '88855555555' + }] } response = self.post( self.url, @@ -558,7 +525,9 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): self.assertEqual(response.status_code, 400) def test_update_no_project_sales_force_id(self): - """Tests /contact/project/?form=salesforce POST with existing project contact.""" + """Tests /contact/project/?form=salesforce POST with existing project contact + without project_sales_force_id. It should 400. + """ model = self.env.project_contact contact = db.session.query(Contact)\ .filter(Contact.id == model.contact_id)\ @@ -567,13 +536,7 @@ class TestSalesForceProjectContact(UnprotectedRestTestCase): contact_methods = db.session.query(ContactMethod)\ .filter(ContactMethod.contact_id == contact.id)\ .all() - - contact_method_data = [] - for contact_method in contact_methods: - contact_method_data.append({ - 'method': contact_method.method, - 'value': contact_method.value }) - + contact_method_data = [{'method': c.method, 'value': c.value} for c in contact_methods] data = { 'contact': contact.get_dictionary(), 'contact_methods': contact_method_data} -- GitLab From b013121509f944963ab21824601465f229be092b Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 15:12:35 -0400 Subject: [PATCH 088/118] Add missing negative test cases. --- app/tests/test_note.py | 88 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 6 deletions(-) diff --git a/app/tests/test_note.py b/app/tests/test_note.py index 07eac2d..61f94c8 100644 --- a/app/tests/test_note.py +++ b/app/tests/test_note.py @@ -203,7 +203,7 @@ class TestSalesForceNote(UnprotectedRestTestCase): 'content': 'This is an update of test note.'}) self._test_post(data, {'form': 'salesforce'}) - def test_post_no_title(): + def test_create_missing_title(): """Tests /note/?form=salesforce POST create without note title. This should 400.""" project = self.env.project @@ -219,7 +219,7 @@ class TestSalesForceNote(UnprotectedRestTestCase): query_string={'form': 'salesforce'}) self.assertEqual(response.status_code, 400) - def test_post_no_content(): + def test_create_missing_content(): """Tests /note/?form=salesforce POST create without content. This should 400.""" project = self.env.project @@ -235,7 +235,7 @@ class TestSalesForceNote(UnprotectedRestTestCase): query_string={'form': 'salesforce'}) self.assertEqual(response.status_code, 400) - def test_post_no_poster_name(self): + def test_create_missing_poster_name(self): """Tests /note/?form=salesforce POST create without poster_name. This should 400.""" project = self.env.project @@ -252,7 +252,7 @@ class TestSalesForceNote(UnprotectedRestTestCase): query_string={'form': 'salesforce'}) self.assertEqual(response.status_code, 400) - def test_post_no_sf_id(self): + def test_create_missing_sf_id(self): """Tests /note/?form=salesforce POST create without sales_force_id. This should 400.""" project = self.env.project @@ -269,7 +269,7 @@ class TestSalesForceNote(UnprotectedRestTestCase): query_string={'form': 'salesforce'}) self.assertEqual(response.status_code, 400) - def test_post_no_project_sf_id(self): + def test_create_missing_project_sf_id(self): """Tests /note/?form=salesforce POST create without project_sales_force_id. This should 400.""" project = self.env.project @@ -286,7 +286,7 @@ class TestSalesForceNote(UnprotectedRestTestCase): query_string={'form': 'salesforce'}) self.assertEqual(response.status_code, 400) - def test_post_no_posted(self): + def test_create_missing_posted(self): """Tests /note/?form=salesforce POST create without posted. This should 400.""" project = self.env.project @@ -302,3 +302,79 @@ class TestSalesForceNote(UnprotectedRestTestCase): data=data, query_string={'form': 'salesforce'}) self.assertEqual(response.status_code, 400) + + def test_update_missing_title(): + """Tests /note/?form=salesforce POST update without note title. This should 400.""" + model = self.env.note_with_name + data = model.get_dictionary() + data = self.env.salesforcePostable(data) + data.pop('title') + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_update_missing_content(): + """Tests /note/?form=salesforce POST update without content. This should 400.""" + model = self.env.note_with_name + data = model.get_dictionary() + data = self.env.salesforcePostable(data) + data.pop('content') + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_update_missing_poster_name(self): + """Tests /note/?form=salesforce POST update without poster_name. This should 400.""" + model = self.env.note_with_name + data = model.get_dictionary() + data = self.env.salesforcePostable(data) + data.pop('poster_name') + + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_update_missing_sf_id(self): + """Tests /note/?form=salesforce POST update without sales_force_id. This should 400.""" + model = self.env.note_with_name + data = model.get_dictionary() + data = self.env.salesforcePostable(data) + data.pop('sales_force_id') + + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_update_missing_project_sf_id(self): + """Tests /note/?form=salesforce POST update without project_sales_force_id. This should 400.""" + model = self.env.note_with_name + data = model.get_dictionary() + data = self.env.salesforcePostable(data) + data.pop('project_sales_force_id') + + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_update_missing_posted(self): + """Tests /note/?form=salesforce POST update without posted. This should 400.""" + model = self.env.note_with_name + data = model.get_dictionary() + data = self.env.salesforcePostable(data) + data.pop('posted') + + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) -- GitLab From 37528a429b8fcf97b98bdd446d22e249186d3c09 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 16:13:59 -0400 Subject: [PATCH 089/118] Move salesforce need to Auth and import the form need. --- app/permissions/application.py | 7 -- app/permissions/auth.py | 7 +- app/tests/test_project.py | 221 ++++++++++++++++++++++----------- 3 files changed, 157 insertions(+), 78 deletions(-) diff --git a/app/permissions/application.py b/app/permissions/application.py index 75d8191..e37541a 100644 --- a/app/permissions/application.py +++ b/app/permissions/application.py @@ -126,10 +126,3 @@ class FormNeed(Permission): from flask import request form = request.args.get('form') return form == self.form - - - -salesforce_need = app_need & ( - (FormNeed('salesforce') & RoleNeed('salesforce')) | - (~FormNeed('salesforce') & auth_need) -) diff --git a/app/permissions/auth.py b/app/permissions/auth.py index fd2d4d3..bd65e66 100644 --- a/app/permissions/auth.py +++ b/app/permissions/auth.py @@ -4,7 +4,7 @@ from werkzeug.exceptions import Unauthorized from app.lib.red import redis from app.lib.service import services from app.permissions.base import Permission -from app.permissions.application import app_need +from app.permissions.application import app_need, FormNeed, RoleNeed class AuthNeed(Permission): @@ -68,3 +68,8 @@ auth_need = AuthNeed() standard_login_need = app_need & auth_need + +salesforce_need = app_need & ( + (FormNeed('salesforce') & RoleNeed('salesforce')) | + (~FormNeed('salesforce') & auth_need) +) diff --git a/app/tests/test_project.py b/app/tests/test_project.py index b3c6b56..abe5129 100644 --- a/app/tests/test_project.py +++ b/app/tests/test_project.py @@ -5,7 +5,7 @@ from sqlalchemy import and_ from app.lib.service import services from app.lib.database import db -from app.tests.base import RestTestCase, UnprotectedRestTestCase +from app.tests.base import RestTestCase, SalesforceTestCase from app.models.client import Client from app.models.contact import Contact, ContactMethod, ProjectContact from app.models.place import Place, Address @@ -197,20 +197,12 @@ class TestProject(RestTestCase): response = self.delete(self.url + str(model.id)) self.assertEqual(response.status_code, 405) -class TestSalesForceProject(UnprotectedRestTestCase): +class TestSalesForceProject(SalesforceTestCase): """Tests the /project/?form=salesforce POST endpoint.""" url = '/project/' Model = Project - @property - def global_headers(self): - """Add app headers to authenticate a salesforce client.""" - return { - services.config['headers']['app_key']: \ - self.app.config['SALESFORCE_KEY'], - 'referer': self.app.config['SALESFORCE_REFERRER']} - - def test_post_sf_new_project(self): + def test_create(self): """Tests /project/?form=salesforce POST.""" client_data = {'sales_force_id': 'test', 'name': 'test'} place = self.env.place @@ -232,20 +224,20 @@ class TestSalesForceProject(UnprotectedRestTestCase): Client.sales_force_id == client_data['sales_force_id']))\ .first()) - def test_post_sf_change_client(self): - """Tests /project/?form=salesforce POST with a change in orphaned client.""" + def test_update_change_client(self): + """Tests /project/?form=salesforce POST with a change in orphaned client. + This happens when a client is no longer associated to an opportunity and + reassociated to another opportunity. + """ project = self.env.project + orphaned_client = self.env.otherclient data = project.get_dictionary() - data.pop('id') - client_id = data.pop('client_id') + data = self.env.salesforcePostable(data) + data.pop('client_id') place_id = data.pop('place_id') - - client = db.session.query(Client)\ - .filter(Client.id==client_id)\ - .first() - - client_data = client.get_dictionary() - client_data.pop('id') + + client_data = orphaned_client.get_dictionary() + client_data = self.env.salesforcePostable(client_data) client_data.update({'name': 'foo'}) data.update({'client': client_data}) @@ -254,15 +246,14 @@ class TestSalesForceProject(UnprotectedRestTestCase): .filter(Place.id==place_id)\ .first() place_data = place.get_dictionary() - place_data.pop('id') - address_id = place_data.pop('address_id') + place_data = self.env.salesforcePostable(place_data) address = db.session.query(Address)\ .filter(Address.id==address_id)\ .first() address_data = address.get_dictionary() - address_data.pop('id') + address_data = self.env.salesforcePostable(address_data) data.update({ 'place': place_data, @@ -279,33 +270,34 @@ class TestSalesForceProject(UnprotectedRestTestCase): Client.name == client_data['name']))\ .first()) - def test_post_sf_new_client(self): - """Tests /project/?form=salesforce POST with a new client.""" + def test_update_new_client(self): + """Tests /project/?form=salesforce POST update with a new client.""" project = self.env.project data = project.get_dictionary() - data.pop('id') + data = self.env.salesforcePostable(data) client_id = data.pop('client_id') place_id = data.pop('place_id') - # New Client - client_data = {'sales_force_id': 'test1', 'name': 'foo'} - data.update({'client': client_data}) - place = db.session.query(Place)\ .filter(Place.id==place_id)\ .first() place_data = place.get_dictionary() + place_data = self.env.salesforcePostable(place_data) address_id = place_data.pop('address_id') address = db.session.query(Address)\ .filter(Address.id==address_id)\ .first() address_data = address.get_dictionary() - address_data.pop('id') + address_data = self.env.salesforcePostable(address_data) + + # New Client + client_data = {'sales_force_id': 'test1', 'name': 'foo'} data.update({ 'place': place_data, - 'address': address_data + 'address': address_data, + 'client': client_data }) response_data = self._test_post(data, {'form': 'salesforce'}) @@ -319,8 +311,8 @@ class TestSalesForceProject(UnprotectedRestTestCase): Client.name == client_data['name']))\ .first()) - def test_post_sf_no_client(self): - """Tests /project/?form=salesforce POST with no client. This should + def test_create_no_client(self): + """Tests /project/?form=salesforce POST create with no client. This should 400. """ place = self.env.place @@ -336,37 +328,71 @@ class TestSalesForceProject(UnprotectedRestTestCase): query_string={'form': 'salesforce'}) self.assertEqual(response.status_code, 400) + def test_update_no_client(self): + """Tests /project/?form=salesforce POST update with no client. This should + 400. + """ + project = self.env.project + data = project.get_dictionary() + data = self.env.salesforcePostable(data) + client_id = data.pop('client_id') + place_id = data.pop('place_id') + + place = db.session.query(Place)\ + .filter(Place.id==place_id)\ + .first() + place_data = place.get_dictionary() + place_data = self.env.salesforcePostable(place_data) + + address_id = place_data.pop('address_id') + address = db.session.query(Address)\ + .filter(Address.id==address_id)\ + .first() + address_data = address.get_dictionary() + address_data = self.env.salesforcePostable(address_data) + + data.update({ + 'address' : address_data, + 'place' : place_data + }) + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + # There are additional ways that posting with a client could fail, but # those are already tested in the /client/ endpoint, so there's no need # to test them here. - def test_post_sf_change_place(self): - """Tests /project/?form=salesforce POST with a modified place and + def test_update_new_address(self): + """Tests /project/?form=salesforce POST update with a modified place and address. """ project = self.env.project data = project.get_dictionary() - data.pop('id') - - place = db.session.query(Place)\ - .filter(Place.id == str(data['place_id']))\ - .first() - + data = self.env.salesforcePostable(data) + client_id = data.pop('client_id') + place_id = data.pop('place_id') + client = db.session.query(Client)\ - .filter(Place.id == str(data['client_id']))\ + .filter(Client.id == str(client_id))\ .first() - client_data = client.get_dictionary() - client_data.pop('id') + client_data = self.env.salesforcePostable(client_data) + place = db.session.query(Place)\ + .filter(Place.id==place_id)\ + .first() place_data = place.get_dictionary() - place_data.pop('id') + place_data = self.env.salesforcePostable(place_data) - new_place_data = {'name': 'foo'} - place_data.update(new_place_data) - - address_data = place.address.get_dictionary() - address_data.pop('id') + address_id = place_data.pop('address_id') + address = db.session.query(Address)\ + .filter(Address.id==address_id)\ + .first() + address_data = address.get_dictionary() + address_data = self.env.salesforcePostable(address_data) new_address_data = { 'street_address': '111 N Fake St', @@ -402,8 +428,8 @@ class TestSalesForceProject(UnprotectedRestTestCase): .filter(and_(*filters))\ .first()) - def test_post_sf_new_place(self): - """Tests /project/?form=salesforce POST with a new place and + def test_update_new_place(self): + """Tests /project/?form=salesforce POST update with a new place and address. """ place_data = {'sales_force_id': 'test1', 'name': 'test1'} @@ -417,29 +443,22 @@ class TestSalesForceProject(UnprotectedRestTestCase): project = self.env.project data = project.get_dictionary() - data.pop('id') - + data = self.env.salesforcePostable(data) client_id = data.pop('client_id') - place_id = data.pop('place_id') - data.update({ 'place' : place_data, 'address': address_data}) - client = db.session.query(Client)\ .filter(Client.id == str(client_id))\ .first() - client_data = client.get_dictionary() - client_data.pop('id') - + client_data = self.env.salesforcePostable(client_data) data.update({ 'client': client_data }) response_data = self._test_post(data, {'form': 'salesforce'}) - filters = [Project.id == response_data['id']] filters += [ getattr(Place, k) == place_data.get(k) @@ -447,7 +466,6 @@ class TestSalesForceProject(UnprotectedRestTestCase): filters += [ getattr(Address, k) == address_data.get(k) for k in address_data.keys()] - self.assertTrue( db.session.query(Address)\ .join(Place, Place.address_id == Address.id)\ @@ -455,8 +473,8 @@ class TestSalesForceProject(UnprotectedRestTestCase): .filter(and_(*filters))\ .first()) - def test_post_sf_no_place(self): - """Tests /project/?form=salesforce POST with no place. It should 400. + def test_create_no_place(self): + """Tests /project/?form=salesforce POST create with no place. It should 400. """ address_data = { 'street_address': '15 MetroTech Center', @@ -465,11 +483,15 @@ class TestSalesForceProject(UnprotectedRestTestCase): 'state': 'NY', 'country': 'United States of America', 'postal_code': '11210'} + + client_data = self.env.client.get_dictionary() + client_data = self.env.salesforcePostable(client_data) + data = { 'sales_force_id': 'test', 'name': 'test', 'state': 'pending', - 'client': self.env.client.get_dictionary(), + 'client': client_data, 'address': address_data} response = self.post( self.url, @@ -477,15 +499,46 @@ class TestSalesForceProject(UnprotectedRestTestCase): query_string={'form': 'salesforce'}) self.assertEqual(response.status_code, 400) - def test_post_sf_no_address(self): + def test_update_no_place(self): + """Tests /project/?form=salesforce POST update with no place. It should 400. + """ + project = self.env.project + data = project.get_dictionary() + data = self.env.salesforcePostable(data) + client_id = data.pop('client_id') + place_id = data.pop('place_id') + client = db.session.query(Client)\ + .filter(Client.id == str(client_id))\ + .first() + client_data = client.get_dictionary() + client_data = self.env.salesforcePostable(client_data) + address_data = { + 'street_address': '15 MetroTech Center', + 'city': 'Brooklyn', + 'county': 'Kings', + 'state': 'NY', + 'country': 'United States of America', + 'postal_code': '11210'} + data.update({ + 'address': address_data, + 'client': client_data}) + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_create_no_address(self): """Tests /project/?form=salesforce POST with no address. It should 400. """ place_data = {'sales_force_id': 'test', 'name': 'test'} + client_data = self.env.client.get_dictionary() + client_data = self.env.salesforcePostable(client_data) data = { 'sales_force_id': 'test', 'name': 'test', 'state': 'pending', - 'client': self.env.client.get_dictionary(), + 'client': client_data, 'place': place_data} response = self.post( self.url, @@ -493,6 +546,34 @@ class TestSalesForceProject(UnprotectedRestTestCase): query_string={'form': 'salesforce'}) self.assertEqual(response.status_code, 400) + def test_update_no_address(self): + """Tests /project/?form=salesforce POST with no address. It should 400. + """ + project = self.env.project + data = project.get_dictionary() + data = self.env.salesforcePostable(data) + client_id = data.pop('client_id') + place_id = data.pop('place_id') + client = db.session.query(Client)\ + .filter(Client.id == str(client_id))\ + .first() + client_data = client.get_dictionary() + client_data = self.env.salesforcePostable(client_data) + place = db.session.query(Place)\ + .filter(Place.id==place_id)\ + .first() + place_data = place.get_dictionary() + place_data = self.env.salesforcePostable(place_data) + place_data.pop('address_id') + data.update({ + 'client':client_data, + 'place': place_data}) + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + # There are additional ways that posting with a place and address could # fail, but those are already tested in the /address/ and /place/ # endpoints, so there's no need to test them here. -- GitLab From 29571b2c4f94d5073fb0e64393fd9742dfca9176 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 16:15:10 -0400 Subject: [PATCH 090/118] Fix typo and import salesforce_need. --- app/views/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/base.py b/app/views/base.py index 8297c5c..f37f3d1 100644 --- a/app/views/base.py +++ b/app/views/base.py @@ -4,7 +4,7 @@ from flask import jsonify, request, current_app from flask.ext.classy import FlaskView from app.lib.database import db from app.lib.red import redis -from app.permissions.auth import standard_login_need +from app.permissions.auth import standard_login_need, salesforce_need class View(FlaskView): @@ -70,7 +70,7 @@ class RestView(UnprotectedRestView): """ decorators = (standard_login_need,) -class SalesforceObjectView(UnprotectedRestView) +class SalesforceObjectView(UnprotectedRestView): """A view wrapper for Salesforce object that offers API protection""" def get_controller(self): """Return a controller instance to use for database interactions.""" -- GitLab From 845f3be09ade66d6f2edcb799fb44061abc2d68e Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 16:17:28 -0400 Subject: [PATCH 091/118] Remove unnecessary imports and use SalesforceObjectView. --- app/views/client.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/views/client.py b/app/views/client.py index ba0f372..8646f4e 100644 --- a/app/views/client.py +++ b/app/views/client.py @@ -5,11 +5,10 @@ from flask import request from app.views.base import SalesforceObjectView from app.controllers.client import ClientController -from app.permissions.application import app_need, RoleNeed -from app.permissions.auth import auth_need, standard_login_need, salesforce_need +from app.permissions.auth import auth_need, standard_login_need -class ClientView(UnprotectedRestView): +class ClientView(SalesforceObjectView): """The client view.""" def get_controller(self): """Return an instance of the client controller.""" -- GitLab From 395070a48c4597c746fb743abaf87d7742990cb5 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 16:57:22 -0400 Subject: [PATCH 092/118] Fixing typy in salesforcePostable helper function. --- app/tests/environment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/tests/environment.py b/app/tests/environment.py index 60a8fb2..fce5479 100644 --- a/app/tests/environment.py +++ b/app/tests/environment.py @@ -33,8 +33,8 @@ class Environment(object): """ Pops the fiels SF objects do not include (id, created, posted). """ savedSFObject.pop('id') savedSFObject.pop('created') - savedSFObject.pop('posted') - return savedSFobject + savedSFObject.pop('updated') + return savedSFObject @property def client(self): -- GitLab From 10ce5bb50e22f9653e9d12c6030736b02d1b3e0f Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 16:59:35 -0400 Subject: [PATCH 093/118] Fix typo in Form need error. --- app/permissions/application.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/permissions/application.py b/app/permissions/application.py index e37541a..f611448 100644 --- a/app/permissions/application.py +++ b/app/permissions/application.py @@ -113,10 +113,10 @@ class FormNeed(Permission): """Checks that the current requests has the correct form query value. """ @property def error(self): - """Return an error based on the role.""" + """Return an error based on the form.""" return Unauthorized( - 'Please authenticate with an application with the {} role.'\ - .format(self.role)) + 'Please authenticate with an application using the {} form.'\ + .format(self.form)) def __init__(self, form): """Initialize with a form.""" -- GitLab From c41297c0c9d9f5481dc710ee32691175e3e89df1 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 17:08:32 -0400 Subject: [PATCH 094/118] Fix style issues with dictionary in list. --- app/tests/test_contact.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/app/tests/test_contact.py b/app/tests/test_contact.py index 29366b3..2921d7f 100644 --- a/app/tests/test_contact.py +++ b/app/tests/test_contact.py @@ -373,11 +373,9 @@ class TestSalesForceContact(SalesforceTestCase): data = { 'sales_force_id': model.sales_force_id, 'name': model.name, - 'contact_methods': - [{ - 'method': 'sms', - 'value': '88845555555' - }] + 'contact_methods':[{ + 'method': 'sms', + 'value': '88845555555'}] } self._test_post(data, {'form': 'salesforce'}) @@ -388,8 +386,7 @@ class TestSalesForceContact(SalesforceTestCase): data = { 'sales_force_id': model.sales_force_id, 'name': 'John Doe', - 'contact_methods': - [{ + 'contact_methods':[{ 'method': 'sms', 'value': '88855555555' }] -- GitLab From a946d8cc9f3f23a15ddf6aba26e8364b6033ac44 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 17:14:00 -0400 Subject: [PATCH 095/118] Use a dictionary instead of generated dictionary test create for salesforce project. --- app/tests/test_project.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/app/tests/test_project.py b/app/tests/test_project.py index abe5129..80a394c 100644 --- a/app/tests/test_project.py +++ b/app/tests/test_project.py @@ -205,14 +205,21 @@ class TestSalesForceProject(SalesforceTestCase): def test_create(self): """Tests /project/?form=salesforce POST.""" client_data = {'sales_force_id': 'test', 'name': 'test'} - place = self.env.place + place_data = {'sales_force_id': 'test', 'name': 'test'} + address_data = { + 'street_address': '111 N Fake St', + 'city': 'Raleigh', + 'county': 'Wake', + 'state': 'NC', + 'country': 'United States of America', + 'postal_code': '27615'} data = { 'sales_force_id': 'test', 'name': 'test', 'state': 'pending', 'client': client_data, - 'place': place.get_dictionary(), - 'address': place.address.get_dictionary()} + 'place': place_data, + 'address': address_data} response_data = self._test_post(data, {'form': 'salesforce'}) # Check that a client was posted with the new data. self.assertTrue( -- GitLab From f1e5ae70b0d49ab8553c32b4cea4515b952ebb12 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 17:24:09 -0400 Subject: [PATCH 096/118] Use list comprehension to put together contact_methods. --- app/tests/test_contact.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/tests/test_contact.py b/app/tests/test_contact.py index 2921d7f..b712413 100644 --- a/app/tests/test_contact.py +++ b/app/tests/test_contact.py @@ -463,11 +463,7 @@ class TestSalesForceProjectContact(SalesforceTestCase): .filter(ContactMethod.contact_id == contact.id)\ .all() - contact_method_data = [] - for contact_method in contact_methods: - contact_method_object = contact_method.get_dictionary() - contact_method_object.pop("id") - contact_method_data.append(contact_method_object) + contact_method_data = contact_method_data = [{'method': c.method, 'value': c.value} for c in contact_methods] data = { -- GitLab From 7ecb891357809facf65d3ccfc0781fb6bb17686e Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 17:27:38 -0400 Subject: [PATCH 097/118] Fix copy error. --- app/tests/test_contact.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/tests/test_contact.py b/app/tests/test_contact.py index b712413..02515fb 100644 --- a/app/tests/test_contact.py +++ b/app/tests/test_contact.py @@ -463,7 +463,7 @@ class TestSalesForceProjectContact(SalesforceTestCase): .filter(ContactMethod.contact_id == contact.id)\ .all() - contact_method_data = contact_method_data = [{'method': c.method, 'value': c.value} for c in contact_methods] + contact_method_data = [{'method': c.method, 'value': c.value} for c in contact_methods] data = { -- GitLab From 12134577b9627275962eacfe2b3d2313bb183b1c Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 17:51:02 -0400 Subject: [PATCH 098/118] Use snake case not camel in function name. --- app/tests/environment.py | 10 ++++----- app/tests/test_note.py | 14 ++++++------ app/tests/test_project.py | 46 +++++++++++++++++++-------------------- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/app/tests/environment.py b/app/tests/environment.py index fce5479..2d498c6 100644 --- a/app/tests/environment.py +++ b/app/tests/environment.py @@ -29,12 +29,12 @@ class Environment(object): commit() return model - def salesforcePostable(self, savedSFObject): + def salesforce_postable(self, saved_sf_object): """ Pops the fiels SF objects do not include (id, created, posted). """ - savedSFObject.pop('id') - savedSFObject.pop('created') - savedSFObject.pop('updated') - return savedSFObject + saved_sf_object.pop('id') + saved_sf_object.pop('created') + saved_sf_object.pop('updated') + return saved_sf_object @property def client(self): diff --git a/app/tests/test_note.py b/app/tests/test_note.py index 61f94c8..d84ac31 100644 --- a/app/tests/test_note.py +++ b/app/tests/test_note.py @@ -194,7 +194,7 @@ class TestSalesForceNote(UnprotectedRestTestCase): """Tests /note/?form=salesforce POST update with a note.""" model = self.env.note_with_name data = model.get_dictionary() - data = self.env.salesforcePostable(data) + data = self.env.salesforce_postable(data) project = db.session.query(Project)\ .filter(Project.id == model.project_id)\ .first() @@ -307,7 +307,7 @@ class TestSalesForceNote(UnprotectedRestTestCase): """Tests /note/?form=salesforce POST update without note title. This should 400.""" model = self.env.note_with_name data = model.get_dictionary() - data = self.env.salesforcePostable(data) + data = self.env.salesforce_postable(data) data.pop('title') response = self.post( self.url, @@ -319,7 +319,7 @@ class TestSalesForceNote(UnprotectedRestTestCase): """Tests /note/?form=salesforce POST update without content. This should 400.""" model = self.env.note_with_name data = model.get_dictionary() - data = self.env.salesforcePostable(data) + data = self.env.salesforce_postable(data) data.pop('content') response = self.post( self.url, @@ -331,7 +331,7 @@ class TestSalesForceNote(UnprotectedRestTestCase): """Tests /note/?form=salesforce POST update without poster_name. This should 400.""" model = self.env.note_with_name data = model.get_dictionary() - data = self.env.salesforcePostable(data) + data = self.env.salesforce_postable(data) data.pop('poster_name') response = self.post( @@ -344,7 +344,7 @@ class TestSalesForceNote(UnprotectedRestTestCase): """Tests /note/?form=salesforce POST update without sales_force_id. This should 400.""" model = self.env.note_with_name data = model.get_dictionary() - data = self.env.salesforcePostable(data) + data = self.env.salesforce_postable(data) data.pop('sales_force_id') response = self.post( @@ -357,7 +357,7 @@ class TestSalesForceNote(UnprotectedRestTestCase): """Tests /note/?form=salesforce POST update without project_sales_force_id. This should 400.""" model = self.env.note_with_name data = model.get_dictionary() - data = self.env.salesforcePostable(data) + data = self.env.salesforce_postable(data) data.pop('project_sales_force_id') response = self.post( @@ -370,7 +370,7 @@ class TestSalesForceNote(UnprotectedRestTestCase): """Tests /note/?form=salesforce POST update without posted. This should 400.""" model = self.env.note_with_name data = model.get_dictionary() - data = self.env.salesforcePostable(data) + data = self.env.salesforce_postable(data) data.pop('posted') response = self.post( diff --git a/app/tests/test_project.py b/app/tests/test_project.py index 80a394c..68cebe7 100644 --- a/app/tests/test_project.py +++ b/app/tests/test_project.py @@ -239,12 +239,12 @@ class TestSalesForceProject(SalesforceTestCase): project = self.env.project orphaned_client = self.env.otherclient data = project.get_dictionary() - data = self.env.salesforcePostable(data) + data = self.env.salesforce_postable(data) data.pop('client_id') place_id = data.pop('place_id') client_data = orphaned_client.get_dictionary() - client_data = self.env.salesforcePostable(client_data) + client_data = self.env.salesforce_postable(client_data) client_data.update({'name': 'foo'}) data.update({'client': client_data}) @@ -253,14 +253,14 @@ class TestSalesForceProject(SalesforceTestCase): .filter(Place.id==place_id)\ .first() place_data = place.get_dictionary() - place_data = self.env.salesforcePostable(place_data) + place_data = self.env.salesforce_postable(place_data) address = db.session.query(Address)\ .filter(Address.id==address_id)\ .first() address_data = address.get_dictionary() - address_data = self.env.salesforcePostable(address_data) + address_data = self.env.salesforce_postable(address_data) data.update({ 'place': place_data, @@ -281,7 +281,7 @@ class TestSalesForceProject(SalesforceTestCase): """Tests /project/?form=salesforce POST update with a new client.""" project = self.env.project data = project.get_dictionary() - data = self.env.salesforcePostable(data) + data = self.env.salesforce_postable(data) client_id = data.pop('client_id') place_id = data.pop('place_id') @@ -289,14 +289,14 @@ class TestSalesForceProject(SalesforceTestCase): .filter(Place.id==place_id)\ .first() place_data = place.get_dictionary() - place_data = self.env.salesforcePostable(place_data) + place_data = self.env.salesforce_postable(place_data) address_id = place_data.pop('address_id') address = db.session.query(Address)\ .filter(Address.id==address_id)\ .first() address_data = address.get_dictionary() - address_data = self.env.salesforcePostable(address_data) + address_data = self.env.salesforce_postable(address_data) # New Client client_data = {'sales_force_id': 'test1', 'name': 'foo'} @@ -341,7 +341,7 @@ class TestSalesForceProject(SalesforceTestCase): """ project = self.env.project data = project.get_dictionary() - data = self.env.salesforcePostable(data) + data = self.env.salesforce_postable(data) client_id = data.pop('client_id') place_id = data.pop('place_id') @@ -349,14 +349,14 @@ class TestSalesForceProject(SalesforceTestCase): .filter(Place.id==place_id)\ .first() place_data = place.get_dictionary() - place_data = self.env.salesforcePostable(place_data) + place_data = self.env.salesforce_postable(place_data) address_id = place_data.pop('address_id') address = db.session.query(Address)\ .filter(Address.id==address_id)\ .first() address_data = address.get_dictionary() - address_data = self.env.salesforcePostable(address_data) + address_data = self.env.salesforce_postable(address_data) data.update({ 'address' : address_data, @@ -378,7 +378,7 @@ class TestSalesForceProject(SalesforceTestCase): """ project = self.env.project data = project.get_dictionary() - data = self.env.salesforcePostable(data) + data = self.env.salesforce_postable(data) client_id = data.pop('client_id') place_id = data.pop('place_id') @@ -386,20 +386,20 @@ class TestSalesForceProject(SalesforceTestCase): .filter(Client.id == str(client_id))\ .first() client_data = client.get_dictionary() - client_data = self.env.salesforcePostable(client_data) + client_data = self.env.salesforce_postable(client_data) place = db.session.query(Place)\ .filter(Place.id==place_id)\ .first() place_data = place.get_dictionary() - place_data = self.env.salesforcePostable(place_data) + place_data = self.env.salesforce_postable(place_data) address_id = place_data.pop('address_id') address = db.session.query(Address)\ .filter(Address.id==address_id)\ .first() address_data = address.get_dictionary() - address_data = self.env.salesforcePostable(address_data) + address_data = self.env.salesforce_postable(address_data) new_address_data = { 'street_address': '111 N Fake St', @@ -450,7 +450,7 @@ class TestSalesForceProject(SalesforceTestCase): project = self.env.project data = project.get_dictionary() - data = self.env.salesforcePostable(data) + data = self.env.salesforce_postable(data) client_id = data.pop('client_id') place_id = data.pop('place_id') data.update({ @@ -460,7 +460,7 @@ class TestSalesForceProject(SalesforceTestCase): .filter(Client.id == str(client_id))\ .first() client_data = client.get_dictionary() - client_data = self.env.salesforcePostable(client_data) + client_data = self.env.salesforce_postable(client_data) data.update({ 'client': client_data }) @@ -492,7 +492,7 @@ class TestSalesForceProject(SalesforceTestCase): 'postal_code': '11210'} client_data = self.env.client.get_dictionary() - client_data = self.env.salesforcePostable(client_data) + client_data = self.env.salesforce_postable(client_data) data = { 'sales_force_id': 'test', @@ -511,14 +511,14 @@ class TestSalesForceProject(SalesforceTestCase): """ project = self.env.project data = project.get_dictionary() - data = self.env.salesforcePostable(data) + data = self.env.salesforce_postable(data) client_id = data.pop('client_id') place_id = data.pop('place_id') client = db.session.query(Client)\ .filter(Client.id == str(client_id))\ .first() client_data = client.get_dictionary() - client_data = self.env.salesforcePostable(client_data) + client_data = self.env.salesforce_postable(client_data) address_data = { 'street_address': '15 MetroTech Center', 'city': 'Brooklyn', @@ -540,7 +540,7 @@ class TestSalesForceProject(SalesforceTestCase): """ place_data = {'sales_force_id': 'test', 'name': 'test'} client_data = self.env.client.get_dictionary() - client_data = self.env.salesforcePostable(client_data) + client_data = self.env.salesforce_postable(client_data) data = { 'sales_force_id': 'test', 'name': 'test', @@ -558,19 +558,19 @@ class TestSalesForceProject(SalesforceTestCase): """ project = self.env.project data = project.get_dictionary() - data = self.env.salesforcePostable(data) + data = self.env.salesforce_postable(data) client_id = data.pop('client_id') place_id = data.pop('place_id') client = db.session.query(Client)\ .filter(Client.id == str(client_id))\ .first() client_data = client.get_dictionary() - client_data = self.env.salesforcePostable(client_data) + client_data = self.env.salesforce_postable(client_data) place = db.session.query(Place)\ .filter(Place.id==place_id)\ .first() place_data = place.get_dictionary() - place_data = self.env.salesforcePostable(place_data) + place_data = self.env.salesforce_postable(place_data) place_data.pop('address_id') data.update({ 'client':client_data, -- GitLab From 93b16ff44aea8bff708e17f0d66ccf807a6028bf Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 18:02:24 -0400 Subject: [PATCH 099/118] Add test case for updating client from Salesforce. --- app/tests/test_client.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/tests/test_client.py b/app/tests/test_client.py index 8cd2451..53ddef5 100644 --- a/app/tests/test_client.py +++ b/app/tests/test_client.py @@ -1,7 +1,7 @@ """Unit tests for clients.""" from sqlalchemy import and_ from app.lib.database import db -from app.tests.base import RestTestCase +from app.tests.base import RestTestCase, SalesforceTestCase from app.models.client import Client @@ -80,3 +80,15 @@ class TestClient(RestTestCase): model = self.env.client response = self.delete(self.url + str(model.id)) self.assertEqual(response.status_code, 405) + +class TestClient(SalesforceTestCase): + """Tests the /client/ endpoints.""" + url = '/client/' + Model = Client + + def test_update(self): + """Tests /client/?form=salesforce POST update.""" + data = self.env.client.get_dictionary() + data = self.env.salesforce_postable(data) + data.update({'name': 'John Doe'}) + self._test_post(data, {'form': 'salesforce'}) -- GitLab From 2b7d07d70b66fa534e297cb31841d2d30887e747 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 18:41:43 -0400 Subject: [PATCH 100/118] Fix bug introduced when refactoring. --- app/controllers/contact.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/app/controllers/contact.py b/app/controllers/contact.py index fe7eda3..25164ce 100644 --- a/app/controllers/contact.py +++ b/app/controllers/contact.py @@ -47,16 +47,17 @@ class ContactController(SalesforceObjectController): model = super(ContactController,self).post(data, filter_data) # Prevent salesforce from creating duplicate contact methods. - contact_method_data.update({'contact_id': model.id}) - contact_method_filters = [ - getattr(ContactMethod, k) == v for k, v in contact_method_data.items()] - # Get a matching contact method or post a new one - contact_method = ( - db.session.query(ContactMethod)\ - .filter(and_(*contact_method_filters))\ - .first() or - ContactMethodController().post(contact_method_data, {}) - ) + for contact_method_data in contact_methods_data: + contact_method_data.update({'contact_id': model.id}) + contact_method_filters = [ + getattr(ContactMethod, k) == v for k, v in contact_method_data.items()] + # Get a matching contact method or post a new one + contact_method = ( + db.session.query(ContactMethod)\ + .filter(and_(*contact_method_filters))\ + .first() or + ContactMethodController().post(contact_method_data, {}) + ) else: model = super(ContactController, self).post(data, filter_data) -- GitLab From b9df3c616c7d4385edfd5bfb8265d27f84fd4a6b Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 18:42:13 -0400 Subject: [PATCH 101/118] Add missing import. --- app/controllers/note.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/controllers/note.py b/app/controllers/note.py index 811ebf1..895d243 100644 --- a/app/controllers/note.py +++ b/app/controllers/note.py @@ -4,6 +4,7 @@ from app.controllers.base import SalesforceObjectController from app.models.note import Note from app.models.project import Project from app.forms.note import NoteForm +from werkzeug.exceptions import BadRequest class NoteController(SalesforceObjectController): -- GitLab From 6e1c9b8a7f2c6f329c5a6269399d43b0e26047ba Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 18:43:08 -0400 Subject: [PATCH 102/118] Add missing self arguments. --- app/tests/test_note.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/tests/test_note.py b/app/tests/test_note.py index d84ac31..07645e7 100644 --- a/app/tests/test_note.py +++ b/app/tests/test_note.py @@ -203,7 +203,7 @@ class TestSalesForceNote(UnprotectedRestTestCase): 'content': 'This is an update of test note.'}) self._test_post(data, {'form': 'salesforce'}) - def test_create_missing_title(): + def test_create_missing_title(self): """Tests /note/?form=salesforce POST create without note title. This should 400.""" project = self.env.project @@ -219,7 +219,7 @@ class TestSalesForceNote(UnprotectedRestTestCase): query_string={'form': 'salesforce'}) self.assertEqual(response.status_code, 400) - def test_create_missing_content(): + def test_create_missing_content(self): """Tests /note/?form=salesforce POST create without content. This should 400.""" project = self.env.project @@ -303,7 +303,7 @@ class TestSalesForceNote(UnprotectedRestTestCase): query_string={'form': 'salesforce'}) self.assertEqual(response.status_code, 400) - def test_update_missing_title(): + def test_update_missing_title(self): """Tests /note/?form=salesforce POST update without note title. This should 400.""" model = self.env.note_with_name data = model.get_dictionary() @@ -315,7 +315,7 @@ class TestSalesForceNote(UnprotectedRestTestCase): query_string={'form': 'salesforce'}) self.assertEqual(response.status_code, 400) - def test_update_missing_content(): + def test_update_missing_content(self): """Tests /note/?form=salesforce POST update without content. This should 400.""" model = self.env.note_with_name data = model.get_dictionary() @@ -358,7 +358,7 @@ class TestSalesForceNote(UnprotectedRestTestCase): model = self.env.note_with_name data = model.get_dictionary() data = self.env.salesforce_postable(data) - data.pop('project_sales_force_id') + data.pop('project_id') response = self.post( self.url, -- GitLab From 3d328ea55f093fedb541af9418aa22c71c41cb80 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 18:43:45 -0400 Subject: [PATCH 103/118] Make style fix. --- app/tests/test_place.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/tests/test_place.py b/app/tests/test_place.py index 1733f17..f919ab0 100644 --- a/app/tests/test_place.py +++ b/app/tests/test_place.py @@ -288,7 +288,7 @@ class TestSalesForcePlace(SalesforceTestCase): data = { 'sales_force_id': model.sales_force_id, 'name': model.name, - 'address': address_data + 'address': address_data} self._test_post(data, {'form': 'salesforce'}) def test_post_sf_update_place_no_address(self): -- GitLab From 7ddeb68f2653ac0068432239af37ca8ce7970d23 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Fri, 17 Jun 2016 18:45:12 -0400 Subject: [PATCH 104/118] Fix typo in variable names. --- app/tests/test_project.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/tests/test_project.py b/app/tests/test_project.py index 68cebe7..5a8661b 100644 --- a/app/tests/test_project.py +++ b/app/tests/test_project.py @@ -254,6 +254,7 @@ class TestSalesForceProject(SalesforceTestCase): .first() place_data = place.get_dictionary() place_data = self.env.salesforce_postable(place_data) + address_id = place_data.pop('address_id') address = db.session.query(Address)\ @@ -423,10 +424,10 @@ class TestSalesForceProject(SalesforceTestCase): Address.id == place.address.id] filters += [ getattr(Place, k) == place_data.get(k) - for k in new_place_data.keys()] + for k in place_data.keys()] filters += [ getattr(Address, k) == address_data.get(k) - for k in new_address_data.keys()] + for k in address_data.keys()] self.assertTrue( db.session.query(Address)\ -- GitLab From 42bcb7c9f9b83757a2574ec2dc63d88b8730a206 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Mon, 20 Jun 2016 16:21:31 -0400 Subject: [PATCH 105/118] Refactor test note to use salesforce base test and allow postername to be updated. --- app/controllers/base.py | 25 ++++++++++++++++--------- app/controllers/note.py | 20 ++++++++++++++++++-- app/tests/test_note.py | 21 ++------------------- 3 files changed, 36 insertions(+), 30 deletions(-) diff --git a/app/controllers/base.py b/app/controllers/base.py index 8bf9a5d..1fc3209 100644 --- a/app/controllers/base.py +++ b/app/controllers/base.py @@ -147,16 +147,10 @@ class RestController(object): class SalesforceObjectController(RestController): """ Base object controller for interacting with objects also represented in salesforce. """ - def post(self, data, filter_data): - """ Post a new model. - - If the post is from salesforce, insert if salesforce id - is new and update if it already exist. If post is not from - salesforce treat as regular Restful POST request + def retrieveModel(self, data): + """Retrieve the base model using salesforce id, if it exists. Otherwise return + None. """ - if filter_data.get('form') != 'salesforce': - return super(SalesforceObjectController, self).post(data, filter_data) - # Create/modify the object. try: model = db.session.query(self.Model)\ .filter( @@ -166,6 +160,19 @@ class SalesforceObjectController(RestController): raise BadRequest( 'When posting an object from salesforce, please ' + 'include its salesforce id.') + return model + + def post(self, data, filter_data): + """ Post a new model. + + If the post is from salesforce, insert if salesforce id + is new and update if it already exist. If post is not from + salesforce treat as regular Restful POST request + """ + if filter_data.get('form') != 'salesforce': + return super(SalesforceObjectController, self).post(data, filter_data) + # Create/modify the object. + model = self.retrieveModel(data) if model: data.update({'id': model.id}) return self.put(model.id, data, filter_data) diff --git a/app/controllers/note.py b/app/controllers/note.py index 895d243..cd0469a 100644 --- a/app/controllers/note.py +++ b/app/controllers/note.py @@ -11,7 +11,7 @@ class NoteController(SalesforceObjectController): """The note controller.""" Model = Note constant_fields = [ - 'sales_force_id', 'project_id', 'posted', 'poster_uuid', 'poster_name'] + 'sales_force_id', 'project_id', 'posted', 'poster_uuid'] def get_form(self, filter_data): """Return the note form.""" @@ -39,5 +39,21 @@ class NoteController(SalesforceObjectController): 'include its associated project salesforce id.') data.update({'project_id': project.id}) - + model = self.retrieveModel(data) + try: + posted = data['posted'] + poster_name = data['poster_name'] + content = data['content'] + title = data['title'] + except KeyError: + raise BadRequest( + 'When posting a note from salesforce, please '+ + 'include posted, poster_name, title, and content') + if model: + data.update({'id': model.id}) + # Pop constant fields for update + for field in self.constant_fields: + data.pop(field, None) + data.update({'project_id': project.id}) + return self.put(model.id, data, filter_data) return super(NoteController, self).post(data, filter_data) diff --git a/app/tests/test_note.py b/app/tests/test_note.py index 07645e7..44c679e 100644 --- a/app/tests/test_note.py +++ b/app/tests/test_note.py @@ -5,7 +5,7 @@ from sqlalchemy import and_ from app.lib.service import services from app.lib.database import db -from app.tests.base import RestTestCase, UnprotectedRestTestCase +from app.tests.base import RestTestCase, SalesforceTestCase from app.models.note import Note from app.models.project import Project @@ -149,34 +149,17 @@ class TestNote(RestTestCase): response = self.put(self.url + str(model.id), data=data) self.assertEqual(response.status_code, 400) - def test_put_poster_name(self): - """Tests /note/ PUT with a poster name. It should 400.""" - model = self.env.note_with_name - new_data = {'poster_name': 'foo'} - data = model.get_dictionary() - data.update(new_data) - response = self.put(self.url + str(model.id), data=data) - self.assertEqual(response.status_code, 400) - def test_delete(self): """Tests /note/ DELETE.""" model = self.env.note self._test_delete(model.id) -class TestSalesForceNote(UnprotectedRestTestCase): +class TestSalesForceNote(SalesforceTestCase): """Tests the /note/?form=salesforce POST endpoint.""" url = '/note/' Model = Note - @property - def global_headers(self): - """Add app headers to authenticate a salesforce client.""" - return { - services.config['headers']['app_key']: \ - self.app.config['SALESFORCE_KEY'], - 'referer': self.app.config['SALESFORCE_REFERRER']} - def test_create(self): """Tests /note/?form=salesforce POST create with a note.""" project = self.env.project -- GitLab From 1c50b13a8e15f43ccbda29dec96d8680ea9290b6 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Tue, 21 Jun 2016 10:22:13 -0400 Subject: [PATCH 106/118] Follow pep8 79 character limit on test_contact.py. --- app/tests/test_contact.py | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/app/tests/test_contact.py b/app/tests/test_contact.py index 02515fb..aff1a4b 100644 --- a/app/tests/test_contact.py +++ b/app/tests/test_contact.py @@ -278,7 +278,8 @@ class TestContactMethod(RestTestCase): # negative cases are left out for the sake of brevity. def test_put_contact_id(self): - """Tests /contact/method/ PUT with a new contact_id. It should 400. + """Tests /contact/method/ PUT with a new contact_id. It should + 400. """ model = self.env.email_contact_method data = model.get_dictionary() @@ -287,7 +288,8 @@ class TestContactMethod(RestTestCase): self.assertEqual(response.status_code, 400) def test_put_method(self): - """Tests /contact/method/ PUT with a new method. It should 400.""" + """Tests /contact/method/ PUT with a new method. It should 400. + """ model = self.env.email_contact_method data = model.get_dictionary() data.update({'method': 'phone', 'value': '5555555555'}) @@ -331,7 +333,8 @@ class TestProjectContact(RestTestCase): .first()) def test_post_bad_contact(self): - """Tests /contact/project/ POST with a bad contact id. It should 400.""" + """Tests /contact/project/ POST with a bad contact id. It should 400. + """ data = { 'project_id': self.env.project.id, 'contact_id': 1 @@ -340,7 +343,8 @@ class TestProjectContact(RestTestCase): self.assertEqual(response.status_code, 400) def test_post_bad_project(self): - """Tests /contact/project/ POST with a bad project id. It should 400.""" + """Tests /contact/project/ POST with a bad project id. It should 400. + """ data = { 'project_id': 1, 'contact_id': self.env.contact.id @@ -416,7 +420,8 @@ class TestSalesForceProjectContact(SalesforceTestCase): self._test_post(data=data, filter_data={'form': 'salesforce'}) def test_create_no_contact_methods(self): - """Tests /contact/project?form=salesforce POST new project contact without contact methods. It should 400.""" + """Tests /contact/project?form=salesforce POST new project contact + without contact methods. It should 400.""" project = self.env.project data = { 'contact': { @@ -432,7 +437,8 @@ class TestSalesForceProjectContact(SalesforceTestCase): self.assertEqual(response.status_code, 400) def test_create_no_contact(self): - """Tests /contact/project?form=salesforce POST new project contact without contact. It should 400.""" + """Tests /contact/project?form=salesforce POST new project contact + without contact. It should 400.""" project = self.env.project data = { 'project_sales_force_id': project.sales_force_id, @@ -449,7 +455,8 @@ class TestSalesForceProjectContact(SalesforceTestCase): def test_update(self): - """Tests /contact/project/?form=salesforce POST with existing project contact.""" + """Tests /contact/project/?form=salesforce POST with existing project + contact.""" model = self.env.project_contact contact = db.session.query(Contact)\ .filter(Contact.id == model.contact_id)\ @@ -474,7 +481,8 @@ class TestSalesForceProjectContact(SalesforceTestCase): self._test_post(data=data, filter_data={'form': 'salesforce'}) def test_update_no_contact_methods(self): - """Tests /contact/project?form=salesforce POST updating project contact without contact methods. It should 400.""" + """Tests /contact/project?form=salesforce POST updating project + contact without contact methods. It should 400.""" model = self.env.project_contact contact = db.session.query(Contact)\ .filter(Contact.id == model.contact_id)\ @@ -499,7 +507,8 @@ class TestSalesForceProjectContact(SalesforceTestCase): self.assertEqual(response.status_code, 400) def test_update_no_contact(self): - """Tests /contact/project?form=salesforce POST updating project contact without contact. It should 400.""" + """Tests /contact/project?form=salesforce POST updating project + contact without contact. It should 400.""" model = self.env.project_contact project = db.session.query(Project)\ .filter(Project.id == model.project_id)\ @@ -518,9 +527,8 @@ class TestSalesForceProjectContact(SalesforceTestCase): self.assertEqual(response.status_code, 400) def test_update_no_project_sales_force_id(self): - """Tests /contact/project/?form=salesforce POST with existing project contact - without project_sales_force_id. It should 400. - """ + """Tests /contact/project/?form=salesforce POST with existing project + contact without project_sales_force_id. It should 400.""" model = self.env.project_contact contact = db.session.query(Contact)\ .filter(Contact.id == model.contact_id)\ -- GitLab From 9d1549f81a26f4fc0911906149293d202cbee426 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Tue, 21 Jun 2016 10:26:15 -0400 Subject: [PATCH 107/118] Remove extra space in docstring. --- app/tests/environment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/tests/environment.py b/app/tests/environment.py index 2d498c6..d218051 100644 --- a/app/tests/environment.py +++ b/app/tests/environment.py @@ -30,7 +30,7 @@ class Environment(object): return model def salesforce_postable(self, saved_sf_object): - """ Pops the fiels SF objects do not include (id, created, posted). """ + """Pops the fiels SF objects do not include (id, created, posted). """ saved_sf_object.pop('id') saved_sf_object.pop('created') saved_sf_object.pop('updated') -- GitLab From 9c5ac64ae6cde374b5d489da2024616f0cf3dc10 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Tue, 21 Jun 2016 10:51:15 -0400 Subject: [PATCH 108/118] Add docstring for other client and fix longs lines in test_place.py. --- app/tests/environment.py | 3 +++ app/tests/test_place.py | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/tests/environment.py b/app/tests/environment.py index d218051..a5f4d13 100644 --- a/app/tests/environment.py +++ b/app/tests/environment.py @@ -42,6 +42,9 @@ class Environment(object): @property def otherclient(self): + """This client is only used as an orphaned client that gets + reparented with a project on a project update with its salesforce id. + """ return self.add(Client(sales_force_id='othertest', name='test')) @property diff --git a/app/tests/test_place.py b/app/tests/test_place.py index f919ab0..53d9cc1 100644 --- a/app/tests/test_place.py +++ b/app/tests/test_place.py @@ -264,7 +264,8 @@ class TestSalesForcePlace(SalesforceTestCase): self._test_post(data, {'form': 'salesforce'}) def test_post_sf_new_place_no_address(self): - """ Tests /place/?form=salesforce without an address. It should 400.""" + """ Tests /place/?form=salesforce without an address. It should 400. + """ data = { 'sales_force_id': 'test', 'name': 'test place', @@ -292,7 +293,8 @@ class TestSalesForcePlace(SalesforceTestCase): self._test_post(data, {'form': 'salesforce'}) def test_post_sf_update_place_no_address(self): - """ Tests /place/?form=salesforce without an address. It should 400.""" + """ Tests /place/?form=salesforce without an address. It should 400. + """ model = self.env.place data = { 'sales_force_id': model.sales_force_id, -- GitLab From 55934567b275076fe85d8a9415ddcf7fee3c5694 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Tue, 21 Jun 2016 12:10:01 -0400 Subject: [PATCH 109/118] Create the object instead of using the salesforce_postable method. --- app/tests/test_note.py | 89 +++++++--- app/tests/test_project.py | 336 ++++++++++++++++++++------------------ 2 files changed, 240 insertions(+), 185 deletions(-) diff --git a/app/tests/test_note.py b/app/tests/test_note.py index 44c679e..32b967c 100644 --- a/app/tests/test_note.py +++ b/app/tests/test_note.py @@ -176,14 +176,17 @@ class TestSalesForceNote(SalesforceTestCase): def test_update(self): """Tests /note/?form=salesforce POST update with a note.""" model = self.env.note_with_name - data = model.get_dictionary() - data = self.env.salesforce_postable(data) + data = { + 'sales_force_id': model.sales_force_id, + 'posted': model.posted.isoformat(), + 'title': model.title, + 'content': 'This is an update of test note.', + 'poster_name': model.poster_name} project = db.session.query(Project)\ .filter(Project.id == model.project_id)\ .first() data.update({ - 'project_sales_force_id': project.sales_force_id, - 'content': 'This is an update of test note.'}) + 'project_sales_force_id': project.sales_force_id}) self._test_post(data, {'form': 'salesforce'}) def test_create_missing_title(self): @@ -289,9 +292,16 @@ class TestSalesForceNote(SalesforceTestCase): def test_update_missing_title(self): """Tests /note/?form=salesforce POST update without note title. This should 400.""" model = self.env.note_with_name - data = model.get_dictionary() - data = self.env.salesforce_postable(data) - data.pop('title') + data = { + 'sales_force_id': model.sales_force_id, + 'posted': model.posted.isoformat(), + 'content': 'This is an update of test note.', + 'poster_name': model.poster_name} + project = db.session.query(Project)\ + .filter(Project.id == model.project_id)\ + .first() + data.update({ + 'project_sales_force_id': project.sales_force_id}) response = self.post( self.url, data=data, @@ -301,9 +311,16 @@ class TestSalesForceNote(SalesforceTestCase): def test_update_missing_content(self): """Tests /note/?form=salesforce POST update without content. This should 400.""" model = self.env.note_with_name - data = model.get_dictionary() - data = self.env.salesforce_postable(data) - data.pop('content') + data = { + 'sales_force_id': model.sales_force_id, + 'posted': model.posted.isoformat(), + 'title': model.title, + 'poster_name': model.poster_name} + project = db.session.query(Project)\ + .filter(Project.id == model.project_id)\ + .first() + data.update({ + 'project_sales_force_id': project.sales_force_id}) response = self.post( self.url, data=data, @@ -313,10 +330,16 @@ class TestSalesForceNote(SalesforceTestCase): def test_update_missing_poster_name(self): """Tests /note/?form=salesforce POST update without poster_name. This should 400.""" model = self.env.note_with_name - data = model.get_dictionary() - data = self.env.salesforce_postable(data) - data.pop('poster_name') - + data = { + 'sales_force_id': model.sales_force_id, + 'posted': model.posted.isoformat(), + 'title': model.title, + 'content': 'This is an update of test note.'} + project = db.session.query(Project)\ + .filter(Project.id == model.project_id)\ + .first() + data.update({ + 'project_sales_force_id': project.sales_force_id}) response = self.post( self.url, data=data, @@ -326,10 +349,16 @@ class TestSalesForceNote(SalesforceTestCase): def test_update_missing_sf_id(self): """Tests /note/?form=salesforce POST update without sales_force_id. This should 400.""" model = self.env.note_with_name - data = model.get_dictionary() - data = self.env.salesforce_postable(data) - data.pop('sales_force_id') - + data = { + 'posted': model.posted.isoformat(), + 'title': model.title, + 'content': 'This is an update of test note.', + 'poster_name': model.poster_name} + project = db.session.query(Project)\ + .filter(Project.id == model.project_id)\ + .first() + data.update({ + 'project_sales_force_id': project.sales_force_id}) response = self.post( self.url, data=data, @@ -339,10 +368,12 @@ class TestSalesForceNote(SalesforceTestCase): def test_update_missing_project_sf_id(self): """Tests /note/?form=salesforce POST update without project_sales_force_id. This should 400.""" model = self.env.note_with_name - data = model.get_dictionary() - data = self.env.salesforce_postable(data) - data.pop('project_id') - + data = { + 'sales_force_id': model.sales_force_id, + 'posted': model.posted.isoformat(), + 'title': model.title, + 'content': 'This is an update of test note.', + 'poster_name': model.poster_name} response = self.post( self.url, data=data, @@ -352,10 +383,16 @@ class TestSalesForceNote(SalesforceTestCase): def test_update_missing_posted(self): """Tests /note/?form=salesforce POST update without posted. This should 400.""" model = self.env.note_with_name - data = model.get_dictionary() - data = self.env.salesforce_postable(data) - data.pop('posted') - + data = { + 'sales_force_id': model.sales_force_id, + 'title': model.title, + 'content': 'This is an update of test note.', + 'poster_name': model.poster_name} + project = db.session.query(Project)\ + .filter(Project.id == model.project_id)\ + .first() + data.update({ + 'project_sales_force_id': project.sales_force_id}) response = self.post( self.url, data=data, diff --git a/app/tests/test_project.py b/app/tests/test_project.py index 5a8661b..67b2e49 100644 --- a/app/tests/test_project.py +++ b/app/tests/test_project.py @@ -236,37 +236,41 @@ class TestSalesForceProject(SalesforceTestCase): This happens when a client is no longer associated to an opportunity and reassociated to another opportunity. """ - project = self.env.project - orphaned_client = self.env.otherclient - data = project.get_dictionary() - data = self.env.salesforce_postable(data) - data.pop('client_id') - place_id = data.pop('place_id') - - client_data = orphaned_client.get_dictionary() - client_data = self.env.salesforce_postable(client_data) - client_data.update({'name': 'foo'}) - - data.update({'client': client_data}) - + model = self.env.project + client = db.session.query(Client)\ + .filter(Client.id == model.client_id)\ + .first() place = db.session.query(Place)\ - .filter(Place.id==place_id)\ - .first() - place_data = place.get_dictionary() - place_data = self.env.salesforce_postable(place_data) - address_id = place_data.pop('address_id') - - - address = db.session.query(Address)\ - .filter(Address.id==address_id)\ - .first() - address_data = address.get_dictionary() - address_data = self.env.salesforce_postable(address_data) - - data.update({ + .filter(Place.id == model.place_id)\ + .first() + client_data = { + 'sales_force_id': client.sales_force_id, + 'name': client.name} + place_data = { + 'sales_force_id': place.sales_force_id, + 'name': place.name} + address_data = { + 'street_address': place.address.street_address, + 'city': place.address.city, + 'county': place.address.county, + 'state': place.address.state, + 'country': place.address.country, + 'postal_code': place.address.postal_code} + data = { + 'sales_force_id': model.sales_force_id, + 'name': model.name, + 'state': model.state, + 'client': client_data, 'place': place_data, - 'address': address_data - }) + 'address': address_data} + orphaned_client = self.env.otherclient + new_client_data = { + 'sales_force_id': orphaned_client.sales_force_id, + 'name': orphaned_client.name + } + new_client_data.update({'name': 'new'}) + + data.update({'client': new_client_data}) response_data = self._test_post(data, {'form': 'salesforce'}) # Check that the new client was added as expected. @@ -275,39 +279,41 @@ class TestSalesForceProject(SalesforceTestCase): .join(Project, Project.client_id == Client.id) .filter(and_( Project.id == response_data['id'], - Client.name == client_data['name']))\ + Client.name == new_client_data['name']))\ .first()) def test_update_new_client(self): """Tests /project/?form=salesforce POST update with a new client.""" - project = self.env.project - data = project.get_dictionary() - data = self.env.salesforce_postable(data) - client_id = data.pop('client_id') - place_id = data.pop('place_id') - + model = self.env.project + client = db.session.query(Client)\ + .filter(Client.id == model.client_id)\ + .first() place = db.session.query(Place)\ - .filter(Place.id==place_id)\ - .first() - place_data = place.get_dictionary() - place_data = self.env.salesforce_postable(place_data) - - address_id = place_data.pop('address_id') - address = db.session.query(Address)\ - .filter(Address.id==address_id)\ - .first() - address_data = address.get_dictionary() - address_data = self.env.salesforce_postable(address_data) - - # New Client - client_data = {'sales_force_id': 'test1', 'name': 'foo'} - - data.update({ + .filter(Place.id == model.place_id)\ + .first() + client_data = { + 'sales_force_id': client.sales_force_id, + 'name': client.name} + place_data = { + 'sales_force_id': place.sales_force_id, + 'name': place.name} + address_data = { + 'street_address': place.address.street_address, + 'city': place.address.city, + 'county': place.address.county, + 'state': place.address.state, + 'country': place.address.country, + 'postal_code': place.address.postal_code} + data = { + 'sales_force_id': model.sales_force_id, + 'name': model.name, + 'state': model.state, + 'client': client_data, 'place': place_data, - 'address': address_data, - 'client': client_data - }) - + 'address': address_data} + # New Client + new_client_data = {'sales_force_id': 'test1', 'name': 'foo'} + data.update({'client': new_client_data}) response_data = self._test_post(data, {'form': 'salesforce'}) # Check that the new client was added as expected. @@ -316,7 +322,7 @@ class TestSalesForceProject(SalesforceTestCase): .join(Project, Project.client_id == Client.id) .filter(and_( Project.id == response_data['id'], - Client.name == client_data['name']))\ + Client.name == new_client_data['name']))\ .first()) def test_create_no_client(self): @@ -340,29 +346,26 @@ class TestSalesForceProject(SalesforceTestCase): """Tests /project/?form=salesforce POST update with no client. This should 400. """ - project = self.env.project - data = project.get_dictionary() - data = self.env.salesforce_postable(data) - client_id = data.pop('client_id') - place_id = data.pop('place_id') - + model = self.env.project place = db.session.query(Place)\ - .filter(Place.id==place_id)\ - .first() - place_data = place.get_dictionary() - place_data = self.env.salesforce_postable(place_data) - - address_id = place_data.pop('address_id') - address = db.session.query(Address)\ - .filter(Address.id==address_id)\ - .first() - address_data = address.get_dictionary() - address_data = self.env.salesforce_postable(address_data) - - data.update({ - 'address' : address_data, - 'place' : place_data - }) + .filter(Place.id == model.place_id)\ + .first() + place_data = { + 'sales_force_id': place.sales_force_id, + 'name': place.name} + address_data = { + 'street_address': place.address.street_address, + 'city': place.address.city, + 'county': place.address.county, + 'state': place.address.state, + 'country': place.address.country, + 'postal_code': place.address.postal_code} + data = { + 'sales_force_id': model.sales_force_id, + 'name': model.name, + 'state': model.state, + 'place': place_data, + 'address': address_data} response = self.post( self.url, data=data, @@ -374,46 +377,40 @@ class TestSalesForceProject(SalesforceTestCase): # to test them here. def test_update_new_address(self): - """Tests /project/?form=salesforce POST update with a modified place and - address. - """ - project = self.env.project - data = project.get_dictionary() - data = self.env.salesforce_postable(data) - client_id = data.pop('client_id') - place_id = data.pop('place_id') - + """Tests /project/?form=salesforce POST update with a modified place + and address.""" + model = self.env.project client = db.session.query(Client)\ - .filter(Client.id == str(client_id))\ - .first() - client_data = client.get_dictionary() - client_data = self.env.salesforce_postable(client_data) - + .filter(Client.id == model.client_id)\ + .first() place = db.session.query(Place)\ - .filter(Place.id==place_id)\ - .first() - place_data = place.get_dictionary() - place_data = self.env.salesforce_postable(place_data) - - address_id = place_data.pop('address_id') - address = db.session.query(Address)\ - .filter(Address.id==address_id)\ - .first() - address_data = address.get_dictionary() - address_data = self.env.salesforce_postable(address_data) + .filter(Place.id == model.place_id)\ + .first() + client_data = { + 'sales_force_id': client.sales_force_id, + 'name': client.name} + place_data = { + 'sales_force_id': place.sales_force_id, + 'name': place.name} + data = { + 'sales_force_id': model.sales_force_id, + 'name': model.name, + 'state': model.state, + 'client': client_data, + 'place': place_data} new_address_data = { 'street_address': '111 N Fake St', 'city': 'Raleigh', 'county': 'Wake', 'state': 'NC', + 'country': 'United States of America', 'postal_code': '27615'} - address_data.update(new_address_data) data.update({ 'client': client_data, 'place': place_data, - 'address': address_data + 'address': new_address_data }) response_data = self._test_post(data, {'form': 'salesforce'}) @@ -426,8 +423,8 @@ class TestSalesForceProject(SalesforceTestCase): getattr(Place, k) == place_data.get(k) for k in place_data.keys()] filters += [ - getattr(Address, k) == address_data.get(k) - for k in address_data.keys()] + getattr(Address, k) == new_address_data.get(k) + for k in new_address_data.keys()] self.assertTrue( db.session.query(Address)\ @@ -440,40 +437,42 @@ class TestSalesForceProject(SalesforceTestCase): """Tests /project/?form=salesforce POST update with a new place and address. """ - place_data = {'sales_force_id': 'test1', 'name': 'test1'} - address_data = { + model = self.env.project + client = db.session.query(Client)\ + .filter(Client.id == model.client_id)\ + .first() + place = db.session.query(Place)\ + .filter(Place.id == model.place_id)\ + .first() + client_data = { + 'sales_force_id': client.sales_force_id, + 'name': client.name} + data = { + 'sales_force_id': model.sales_force_id, + 'name': model.name, + 'state': model.state, + 'client': client_data} + + new_place_data = {'sales_force_id': 'test1', 'name': 'test1'} + new_address_data = { 'street_address': '111 N Fake St', 'city': 'Raleigh', 'county': 'Wake', 'state': 'NC', 'country': 'United States of America', 'postal_code': '27615'} - - project = self.env.project - data = project.get_dictionary() - data = self.env.salesforce_postable(data) - client_id = data.pop('client_id') - place_id = data.pop('place_id') - data.update({ - 'place' : place_data, - 'address': address_data}) - client = db.session.query(Client)\ - .filter(Client.id == str(client_id))\ - .first() - client_data = client.get_dictionary() - client_data = self.env.salesforce_postable(client_data) data.update({ - 'client': client_data - }) + 'place' : new_place_data, + 'address': new_address_data}) response_data = self._test_post(data, {'form': 'salesforce'}) filters = [Project.id == response_data['id']] filters += [ - getattr(Place, k) == place_data.get(k) - for k in place_data.keys()] + getattr(Place, k) == new_place_data.get(k) + for k in new_place_data.keys()] filters += [ - getattr(Address, k) == address_data.get(k) - for k in address_data.keys()] + getattr(Address, k) == new_address_data.get(k) + for k in new_address_data.keys()] self.assertTrue( db.session.query(Address)\ .join(Place, Place.address_id == Address.id)\ @@ -510,26 +509,37 @@ class TestSalesForceProject(SalesforceTestCase): def test_update_no_place(self): """Tests /project/?form=salesforce POST update with no place. It should 400. """ - project = self.env.project - data = project.get_dictionary() - data = self.env.salesforce_postable(data) - client_id = data.pop('client_id') - place_id = data.pop('place_id') + model = self.env.project client = db.session.query(Client)\ - .filter(Client.id == str(client_id))\ - .first() - client_data = client.get_dictionary() - client_data = self.env.salesforce_postable(client_data) + .filter(Client.id == model.client_id)\ + .first() + place = db.session.query(Place)\ + .filter(Place.id == model.place_id)\ + .first() + client_data = { + 'sales_force_id': client.sales_force_id, + 'name': client.name} address_data = { + 'street_address': place.address.street_address, + 'city': place.address.city, + 'county': place.address.county, + 'state': place.address.state, + 'country': place.address.country, + 'postal_code': place.address.postal_code} + data = { + 'sales_force_id': model.sales_force_id, + 'name': model.name, + 'state': model.state, + 'client': client_data, + 'address': address_data} + new_address_data = { 'street_address': '15 MetroTech Center', 'city': 'Brooklyn', 'county': 'Kings', 'state': 'NY', 'country': 'United States of America', 'postal_code': '11210'} - data.update({ - 'address': address_data, - 'client': client_data}) + data.update({'address': new_address_data}) response = self.post( self.url, data=data, @@ -557,25 +567,33 @@ class TestSalesForceProject(SalesforceTestCase): def test_update_no_address(self): """Tests /project/?form=salesforce POST with no address. It should 400. """ - project = self.env.project - data = project.get_dictionary() - data = self.env.salesforce_postable(data) - client_id = data.pop('client_id') - place_id = data.pop('place_id') + model = self.env.project client = db.session.query(Client)\ - .filter(Client.id == str(client_id))\ - .first() - client_data = client.get_dictionary() - client_data = self.env.salesforce_postable(client_data) + .filter(Client.id == model.client_id)\ + .first() place = db.session.query(Place)\ - .filter(Place.id==place_id)\ - .first() - place_data = place.get_dictionary() - place_data = self.env.salesforce_postable(place_data) - place_data.pop('address_id') - data.update({ - 'client':client_data, - 'place': place_data}) + .filter(Place.id == model.place_id)\ + .first() + client_data = { + 'sales_force_id': client.sales_force_id, + 'name': client.name} + place_data = { + 'sales_force_id': place.sales_force_id, + 'name': place.name} + address_data = { + 'street_address': place.address.street_address, + 'city': place.address.city, + 'county': place.address.county, + 'state': place.address.state, + 'country': place.address.country, + 'postal_code': place.address.postal_code} + data = { + 'sales_force_id': model.sales_force_id, + 'name': model.name, + 'state': model.state, + 'client': client_data, + 'place': place_data} + data.update({'name': 'new_name'}) response = self.post( self.url, data=data, -- GitLab From 784c7ae4dbf2013a3809e3f9b3943a05fe7035bc Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Tue, 21 Jun 2016 12:20:27 -0400 Subject: [PATCH 110/118] Fixing the rest of the methods that used saleforce postable and remove saleforce_postable. --- app/tests/environment.py | 7 ------- app/tests/test_client.py | 6 ++++-- app/tests/test_project.py | 26 +++++++++++++++----------- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/app/tests/environment.py b/app/tests/environment.py index a5f4d13..5682609 100644 --- a/app/tests/environment.py +++ b/app/tests/environment.py @@ -29,13 +29,6 @@ class Environment(object): commit() return model - def salesforce_postable(self, saved_sf_object): - """Pops the fiels SF objects do not include (id, created, posted). """ - saved_sf_object.pop('id') - saved_sf_object.pop('created') - saved_sf_object.pop('updated') - return saved_sf_object - @property def client(self): return self.add(Client(sales_force_id='test', name='test')) diff --git a/app/tests/test_client.py b/app/tests/test_client.py index 53ddef5..6fa0375 100644 --- a/app/tests/test_client.py +++ b/app/tests/test_client.py @@ -88,7 +88,9 @@ class TestClient(SalesforceTestCase): def test_update(self): """Tests /client/?form=salesforce POST update.""" - data = self.env.client.get_dictionary() - data = self.env.salesforce_postable(data) + model = self.env.client + data = { + 'sales_force_id':client.sales_force_id, + 'name': client.name} data.update({'name': 'John Doe'}) self._test_post(data, {'form': 'salesforce'}) diff --git a/app/tests/test_project.py b/app/tests/test_project.py index 67b2e49..d68581e 100644 --- a/app/tests/test_project.py +++ b/app/tests/test_project.py @@ -483,17 +483,15 @@ class TestSalesForceProject(SalesforceTestCase): def test_create_no_place(self): """Tests /project/?form=salesforce POST create with no place. It should 400. """ + client_data = {'sales_force_id': 'test', 'name': 'test'} + place_data = {'sales_force_id': 'test', 'name': 'test'} address_data = { - 'street_address': '15 MetroTech Center', - 'city': 'Brooklyn', - 'county': 'Kings', - 'state': 'NY', + 'street_address': '111 N Fake St', + 'city': 'Raleigh', + 'county': 'Wake', + 'state': 'NC', 'country': 'United States of America', - 'postal_code': '11210'} - - client_data = self.env.client.get_dictionary() - client_data = self.env.salesforce_postable(client_data) - + 'postal_code': '27615'} data = { 'sales_force_id': 'test', 'name': 'test', @@ -549,9 +547,15 @@ class TestSalesForceProject(SalesforceTestCase): def test_create_no_address(self): """Tests /project/?form=salesforce POST with no address. It should 400. """ + client_data = {'sales_force_id': 'test', 'name': 'test'} place_data = {'sales_force_id': 'test', 'name': 'test'} - client_data = self.env.client.get_dictionary() - client_data = self.env.salesforce_postable(client_data) + address_data = { + 'street_address': '111 N Fake St', + 'city': 'Raleigh', + 'county': 'Wake', + 'state': 'NC', + 'country': 'United States of America', + 'postal_code': '27615'} data = { 'sales_force_id': 'test', 'name': 'test', -- GitLab From 5d6b88de59678a9bd4be158978afef05ddd9192d Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Tue, 21 Jun 2016 12:24:33 -0400 Subject: [PATCH 111/118] Fix error in test_update. --- app/tests/test_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/tests/test_client.py b/app/tests/test_client.py index 6fa0375..4195348 100644 --- a/app/tests/test_client.py +++ b/app/tests/test_client.py @@ -90,7 +90,7 @@ class TestClient(SalesforceTestCase): """Tests /client/?form=salesforce POST update.""" model = self.env.client data = { - 'sales_force_id':client.sales_force_id, - 'name': client.name} + 'sales_force_id':model.sales_force_id, + 'name': model.name} data.update({'name': 'John Doe'}) self._test_post(data, {'form': 'salesforce'}) -- GitLab From 9ad839a365efe136dc9f95990bfe764304047983 Mon Sep 17 00:00:00 2001 From: astex <0astex@gmail.com> Date: Tue, 21 Jun 2016 13:40:59 -0400 Subject: [PATCH 112/118] Fix note validation implementation and unit tests. --- app/controllers/note.py | 26 ++++++++------------------ app/forms/note.py | 14 ++++++++------ app/tests/environment.py | 1 + app/tests/test_note.py | 15 +++++++++++++++ 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/app/controllers/note.py b/app/controllers/note.py index cd0469a..e1e4b3f 100644 --- a/app/controllers/note.py +++ b/app/controllers/note.py @@ -31,7 +31,8 @@ class NoteController(SalesforceObjectController): try: project = db.session.query(Project)\ .filter( - Project.sales_force_id == data['project_sales_force_id'])\ + Project.sales_force_id == + data['project_sales_force_id'])\ .first() except KeyError: raise BadRequest( @@ -40,20 +41,9 @@ class NoteController(SalesforceObjectController): data.update({'project_id': project.id}) model = self.retrieveModel(data) - try: - posted = data['posted'] - poster_name = data['poster_name'] - content = data['content'] - title = data['title'] - except KeyError: - raise BadRequest( - 'When posting a note from salesforce, please '+ - 'include posted, poster_name, title, and content') - if model: - data.update({'id': model.id}) - # Pop constant fields for update - for field in self.constant_fields: - data.pop(field, None) - data.update({'project_id': project.id}) - return self.put(model.id, data, filter_data) - return super(NoteController, self).post(data, filter_data) + + if not model: + return super(NoteController, self).post(data, filter_data) + + data.update({'id': model.id}) + return self.put(model.id, data, filter_data) diff --git a/app/forms/note.py b/app/forms/note.py index b092c64..6dc02ab 100644 --- a/app/forms/note.py +++ b/app/forms/note.py @@ -6,13 +6,15 @@ from app.forms.base import Form class NoteForm(Form): """A form for validating notes.""" - sales_force_id = wtf.StringField( - validators=[wtf.validators.Length(max=255)]) - project_id = wtf.IntegerField(validators=[wtf.validators.DataRequired()]) + sales_force_id = wtf.StringField(validators=[ + wtf.validators.Length(max=255)]) + project_id = wtf.IntegerField(validators=[wtf.validators.Required()]) - posted = fields.Arrow() - title = wtf.StringField(validators=[wtf.validators.Length(max=255)]) - content = wtf.StringField() + posted = fields.Arrow(validators=[wtf.validators.Required()]) + title = wtf.StringField(validators=[ + wtf.validators.Required(), + wtf.validators.Length(max=255)]) + content = wtf.StringField(validators=[wtf.validators.Required()]) poster_uuid = fields.UUID() poster_name = wtf.StringField(validators=[ diff --git a/app/tests/environment.py b/app/tests/environment.py index 5682609..73c95aa 100644 --- a/app/tests/environment.py +++ b/app/tests/environment.py @@ -108,6 +108,7 @@ class Environment(object): """A note with a poster uuid.""" return self.add(Note( project_id=self.project.id, + posted=arrow.get().isoformat(), title='Test', content='This is a test note.', poster_uuid=str(uuid.uuid4()))) diff --git a/app/tests/test_note.py b/app/tests/test_note.py index 32b967c..3a90c93 100644 --- a/app/tests/test_note.py +++ b/app/tests/test_note.py @@ -29,6 +29,7 @@ class TestNote(RestTestCase): """Tests /note/ POST with a poster uuid.""" data = { 'project_id': self.env.project.id, + 'posted': arrow.get().isoformat(), 'title': 'Test', 'content': 'This is a test note.', 'poster_uuid': str(uuid.uuid4())} @@ -54,6 +55,7 @@ class TestNote(RestTestCase): def test_post_no_project_id(self): """Tests /note/ POST with no project id. It should 400.""" data = { + 'posted': arrow.get().isoformat(), 'title': 'Test', 'content': 'This is a test note.', 'user_uuid': str(uuid.uuid4())} @@ -64,6 +66,7 @@ class TestNote(RestTestCase): """Tests /note/ POST with a bad project id. It should 400.""" data = { 'project_id': 1, + 'posted': arrow.get().isoformat(), 'title': 'Test', 'content': 'This is a test note.', 'user_uuid': str(uuid.uuid4())} @@ -74,6 +77,7 @@ class TestNote(RestTestCase): """Tests /note/ POST with no poster uuid or name. It should 400.""" data = { 'project_id': self.env.project.id, + 'posted': arrow.get().isoformat(), 'title': 'Test', 'content': 'This is a test note.'} response = self.post(self.url, data=data) @@ -83,12 +87,23 @@ class TestNote(RestTestCase): """Tests /note/ POST with a bad poster uuid. It should 400.""" data = { 'project_id': self.env.project.id, + 'posted': arrow.get().isoformat(), 'title': 'Test', 'content': 'This is a test note.', 'poster_uuid': 'test'} response = self.post(self.url, data=data) self.assertEqual(response.status_code, 400) + def test_post_no_posted(self): + """Test /note/ POST with no posted datetime. It should 400.""" + data = { + 'project_id': self.env.project.id, + 'title': 'Test', + 'content': 'This is a test note.', + 'post_uuid': str(uuid.uuid4())} + response = self.post(self.url, data=data) + self.assertEqual(response.status_code, 400) + def test_post_bad_posted(self): """Tests /note/ POST with a bad posted datetime. It should 400.""" data = { -- GitLab From 38ed7d4c4774e0af43f92c558ce62c8137aab994 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Tue, 21 Jun 2016 15:05:21 -0400 Subject: [PATCH 113/118] Add missing negative case tests to client. --- app/tests/test_client.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/app/tests/test_client.py b/app/tests/test_client.py index 4195348..08fd6ea 100644 --- a/app/tests/test_client.py +++ b/app/tests/test_client.py @@ -81,8 +81,8 @@ class TestClient(RestTestCase): response = self.delete(self.url + str(model.id)) self.assertEqual(response.status_code, 405) -class TestClient(SalesforceTestCase): - """Tests the /client/ endpoints.""" +class TestClientSalesforce(SalesforceTestCase): + """Tests the /client/ endpoints when hit from saleforce.""" url = '/client/' Model = Client @@ -94,3 +94,26 @@ class TestClient(SalesforceTestCase): 'name': model.name} data.update({'name': 'John Doe'}) self._test_post(data, {'form': 'salesforce'}) + + def test_update_missing_sf_id(self): + """Tests /client/?form=salesforce POST update without sf id. This + should 400.""" + model = self.env.client + data = {'name': model.name} + data.update({'name': 'John Doe'}) + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_update_missing_name(self): + """Tests /client/?form=salesforce POST update without name. This + should 400.""" + model = self.env.client + data = {'sales_force_id':model.sales_force_id} + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) -- GitLab From 55af664bbfd4502c645277fd0e78ad8bd63cef01 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Tue, 21 Jun 2016 15:15:40 -0400 Subject: [PATCH 114/118] Add missing negative test cases for contact. --- app/tests/test_contact.py | 55 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/app/tests/test_contact.py b/app/tests/test_contact.py index aff1a4b..4ec3d14 100644 --- a/app/tests/test_contact.py +++ b/app/tests/test_contact.py @@ -370,7 +370,7 @@ class TestSalesForceContact(SalesforceTestCase): url = '/contact/' Model = Contact - def test_update_embedded_contact_method(self): + def test_update_contact_method(self): """Tests /contact/?form=salesforce POST with existing object.""" model = self.env.contact @@ -383,7 +383,7 @@ class TestSalesForceContact(SalesforceTestCase): } self._test_post(data, {'form': 'salesforce'}) - def test_update(self): + def test_update_name(self): """Tests /contact/?form=salesforce POST with existing object.""" model = self.env.contact @@ -397,6 +397,57 @@ class TestSalesForceContact(SalesforceTestCase): } self._test_post(data, {'form': 'salesforce'}) + def test_update_missing_name(self): + """Tests /contact/?form=salesforce POST missing name. It should 400. + """ + model = self.env.contact + + data = { + 'sales_force_id': model.sales_force_id, + 'contact_methods':[{ + 'method': 'sms', + 'value': '88855555555' + }] + } + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_update_missing_sf_id(self): + """Tests /contact/?form=salesforce POST missing salesforce id. It + should 400.""" + model = self.env.contact + + data = { + 'name': 'John Doe', + 'contact_methods':[{ + 'method': 'sms', + 'value': '88855555555' + }] + } + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_update_missing_contact_methods(self): + """Tests /contact/?form=salesforce POST missing contact methods. It + should 400""" + model = self.env.contact + + data = { + 'sales_force_id': model.sales_force_id, + 'name': 'John Doe' + } + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + class TestSalesForceProjectContact(SalesforceTestCase): """Tests the /contact/project/?form=salesforce POST endpoint.""" -- GitLab From 17168b32f341150d034fd1b0109a2f786078b1c2 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Tue, 21 Jun 2016 15:33:22 -0400 Subject: [PATCH 115/118] Add missing negative test cases for porject post from salesforce. --- app/tests/test_project.py | 210 +++++++++++++++++++++++++------------- 1 file changed, 141 insertions(+), 69 deletions(-) diff --git a/app/tests/test_project.py b/app/tests/test_project.py index d68581e..df3cf3a 100644 --- a/app/tests/test_project.py +++ b/app/tests/test_project.py @@ -231,11 +231,148 @@ class TestSalesForceProject(SalesforceTestCase): Client.sales_force_id == client_data['sales_force_id']))\ .first()) - def test_update_change_client(self): - """Tests /project/?form=salesforce POST with a change in orphaned client. - This happens when a client is no longer associated to an opportunity and - reassociated to another opportunity. + def test_create_missing_sf_id(self): + """Tests /project/?form=salesforce POST missing salesforce id. It + should 400.""" + client_data = {'sales_force_id': 'test', 'name': 'test'} + place_data = {'sales_force_id': 'test', 'name': 'test'} + address_data = { + 'street_address': '111 N Fake St', + 'city': 'Raleigh', + 'county': 'Wake', + 'state': 'NC', + 'country': 'United States of America', + 'postal_code': '27615'} + data = { + 'name': 'test', + 'state': 'pending', + 'client': client_data, + 'place': place_data, + 'address': address_data} + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_create_missing_client(self): + """Tests /project/?form=salesforce POST create with no client. This + should 400.""" + client_data = {'sales_force_id': 'test', 'name': 'test'} + place_data = {'sales_force_id': 'test', 'name': 'test'} + address_data = { + 'street_address': '111 N Fake St', + 'city': 'Raleigh', + 'county': 'Wake', + 'state': 'NC', + 'country': 'United States of America', + 'postal_code': '27615'} + data = { + 'sales_force_id': 'test', + 'name': 'test', + 'state': 'pending', + 'place': place_data, + 'address': address_data} + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_create_missing_place(self): + """Tests /project/?form=salesforce POST create with no place. It + should 400. """ + client_data = {'sales_force_id': 'test', 'name': 'test'} + address_data = { + 'street_address': '111 N Fake St', + 'city': 'Raleigh', + 'county': 'Wake', + 'state': 'NC', + 'country': 'United States of America', + 'postal_code': '27615'} + data = { + 'sales_force_id': 'test', + 'name': 'test', + 'state': 'pending', + 'client': client_data, + 'address': address_data} + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_create_missing_address(self): + """Tests /project/?form=salesforce POST with no address. It should + 400. + """ + client_data = {'sales_force_id': 'test', 'name': 'test'} + place_data = {'sales_force_id': 'test', 'name': 'test'} + data = { + 'sales_force_id': 'test', + 'name': 'test', + 'state': 'pending', + 'client': client_data, + 'place': place_data} + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_create_missing_project_state(self): + """Tests /project/?form=salesforce POST missing project state. It + should 400. """ + client_data = {'sales_force_id': 'test', 'name': 'test'} + place_data = {'sales_force_id': 'test', 'name': 'test'} + address_data = { + 'street_address': '111 N Fake St', + 'city': 'Raleigh', + 'county': 'Wake', + 'state': 'NC', + 'country': 'United States of America', + 'postal_code': '27615'} + data = { + 'sales_force_id': 'test', + 'name': 'test', + 'client': client_data, + 'place': place_data, + 'address': address_data} + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_create_missing_project_name(self): + """Tests /project/?form=salesforce POST missing project state. It + should 400. """ + client_data = {'sales_force_id': 'test', 'name': 'test'} + place_data = {'sales_force_id': 'test', 'name': 'test'} + address_data = { + 'street_address': '111 N Fake St', + 'city': 'Raleigh', + 'county': 'Wake', + 'state': 'NC', + 'country': 'United States of America', + 'postal_code': '27615'} + data = { + 'sales_force_id': 'test', + 'state': 'pending', + 'client': client_data, + 'place': place_data, + 'address': address_data} + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_update_change_client(self): + """Tests /project/?form=salesforce POST with a change in orphaned + client. This happens when a client is no longer associated to an + opportunity and reassociated to another opportunity.""" model = self.env.project client = db.session.query(Client)\ .filter(Client.id == model.client_id)\ @@ -325,23 +462,6 @@ class TestSalesForceProject(SalesforceTestCase): Client.name == new_client_data['name']))\ .first()) - def test_create_no_client(self): - """Tests /project/?form=salesforce POST create with no client. This should - 400. - """ - place = self.env.place - data = { - 'sales_force_id': 'test', - 'name': 'test', - 'state': 'pending', - 'place': place.get_dictionary(), - 'address': place.address.get_dictionary()} - response = self.post( - self.url, - data=data, - query_string={'form': 'salesforce'}) - self.assertEqual(response.status_code, 400) - def test_update_no_client(self): """Tests /project/?form=salesforce POST update with no client. This should 400. @@ -480,30 +600,6 @@ class TestSalesForceProject(SalesforceTestCase): .filter(and_(*filters))\ .first()) - def test_create_no_place(self): - """Tests /project/?form=salesforce POST create with no place. It should 400. - """ - client_data = {'sales_force_id': 'test', 'name': 'test'} - place_data = {'sales_force_id': 'test', 'name': 'test'} - address_data = { - 'street_address': '111 N Fake St', - 'city': 'Raleigh', - 'county': 'Wake', - 'state': 'NC', - 'country': 'United States of America', - 'postal_code': '27615'} - data = { - 'sales_force_id': 'test', - 'name': 'test', - 'state': 'pending', - 'client': client_data, - 'address': address_data} - response = self.post( - self.url, - data=data, - query_string={'form': 'salesforce'}) - self.assertEqual(response.status_code, 400) - def test_update_no_place(self): """Tests /project/?form=salesforce POST update with no place. It should 400. """ @@ -544,30 +640,6 @@ class TestSalesForceProject(SalesforceTestCase): query_string={'form': 'salesforce'}) self.assertEqual(response.status_code, 400) - def test_create_no_address(self): - """Tests /project/?form=salesforce POST with no address. It should 400. - """ - client_data = {'sales_force_id': 'test', 'name': 'test'} - place_data = {'sales_force_id': 'test', 'name': 'test'} - address_data = { - 'street_address': '111 N Fake St', - 'city': 'Raleigh', - 'county': 'Wake', - 'state': 'NC', - 'country': 'United States of America', - 'postal_code': '27615'} - data = { - 'sales_force_id': 'test', - 'name': 'test', - 'state': 'pending', - 'client': client_data, - 'place': place_data} - response = self.post( - self.url, - data=data, - query_string={'form': 'salesforce'}) - self.assertEqual(response.status_code, 400) - def test_update_no_address(self): """Tests /project/?form=salesforce POST with no address. It should 400. """ -- GitLab From a8db75a2fca5200f7be7a18d0245cf06d1a64c99 Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Tue, 21 Jun 2016 15:36:02 -0400 Subject: [PATCH 116/118] Add missing negative cases for place from salesoforce. --- app/tests/test_place.py | 328 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 324 insertions(+), 4 deletions(-) diff --git a/app/tests/test_place.py b/app/tests/test_place.py index 53d9cc1..3b0ebb2 100644 --- a/app/tests/test_place.py +++ b/app/tests/test_place.py @@ -263,9 +263,169 @@ class TestSalesForcePlace(SalesforceTestCase): } self._test_post(data, {'form': 'salesforce'}) - def test_post_sf_new_place_no_address(self): - """ Tests /place/?form=salesforce without an address. It should 400. - """ + def test_create_missing_street_address(self): + """Tests /place/?form=salesforce POST create missing street address. + It should 400.""" + address_data ={ + 'city':'Brooklyn', + 'county':'Kings', + 'state':'NY', + 'country':'United States of America', + 'postal_code':'11210'} + data = { + 'sales_force_id': 'test', + 'name': 'test place', + 'address': address_data + } + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_create_missing_city(self): + """Tests /place/?form=salesforce POST create missing city. It should + 400.""" + address_data ={ + 'street_address':'15 MetroTech Center', + 'county':'Kings', + 'state':'NY', + 'country':'United States of America', + 'postal_code':'11210'} + data = { + 'sales_force_id': 'test', + 'name': 'test place', + 'address': address_data + } + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_create_missing_county(self): + """Tests /place/?form=salesforce POST create missing county. It + should 400.""" + address_data ={ + 'street_address':'15 MetroTech Center', + 'city':'Brooklyn', + 'state':'NY', + 'country':'United States of America', + 'postal_code':'11210'} + data = { + 'sales_force_id': 'test', + 'name': 'test place', + 'address': address_data + } + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_create_missing_state(self): + """Tests /place/?form=salesforce POST create missing state. It should + 400.""" + address_data ={ + 'street_address':'15 MetroTech Center', + 'city':'Brooklyn', + 'county':'Kings', + 'country':'United States of America', + 'postal_code':'11210'} + data = { + 'sales_force_id': 'test', + 'name': 'test place', + 'address': address_data + } + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_create_missing_country(self): + """Tests /place/?form=salesforce POST create missing country. It + should 400.""" + address_data ={ + 'street_address':'15 MetroTech Center', + 'city':'Brooklyn', + 'county':'Kings', + 'state':'NY', + 'postal_code':'11210'} + data = { + 'sales_force_id': 'test', + 'name': 'test place', + 'address': address_data + } + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_create_missing_postal_code(self): + """Tests /place/?form=salesforce POST create missing postal code. It + should 400.""" + address_data ={ + 'street_address':'15 MetroTech Center', + 'city':'Brooklyn', + 'county':'Kings', + 'state':'NY', + 'country':'United States of America'} + data = { + 'sales_force_id': 'test', + 'name': 'test place', + 'address': address_data + } + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_create_missing_sf_id(self): + """Tests /place/?form=salesforce POST create missing salesfore id. It + should 400.""" + address_data ={ + 'street_address':'15 MetroTech Center', + 'city':'Brooklyn', + 'county':'Kings', + 'state':'NY', + 'country':'United States of America', + 'postal_code':'11210'} + data = { + 'name': 'test place', + 'address': address_data + } + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_create_missing_name(self): + """Tests /place/?form=salesforce POST create with missing name. It + should 400.""" + address_data ={ + 'street_address':'15 MetroTech Center', + 'city':'Brooklyn', + 'county':'Kings', + 'state':'NY', + 'country':'United States of America', + 'postal_code':'11210'} + data = { + 'sales_force_id': 'test', + 'address': address_data + } + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_create_missing_address(self): + """ Tests /place/?form=salesforce POST create missing an address. It + should 400.""" data = { 'sales_force_id': 'test', 'name': 'test place', @@ -292,7 +452,167 @@ class TestSalesForcePlace(SalesforceTestCase): 'address': address_data} self._test_post(data, {'form': 'salesforce'}) - def test_post_sf_update_place_no_address(self): + def test_update_missing_street_address(self): + """Tests /place/?form=salesforce POST update missing street + address. It should 400.""" + model = self.env.place + address_data = { + 'city':'Brooklyn', + 'county':'Kings', + 'state':'NY', + 'country':'United States of America', + 'postal_code':'11210'} + data = { + 'sales_force_id': model.sales_force_id, + 'name': model.name, + 'address': address_data} + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_update_missing_city(self): + """Tests /place/?form=salesforce POST update missing city. It should + 400.""" + model = self.env.place + address_data = { + 'street_address':'2 MetroTech Center', + 'county':'Kings', + 'state':'NY', + 'country':'United States of America', + 'postal_code':'11210'} + data = { + 'sales_force_id': model.sales_force_id, + 'name': model.name, + 'address': address_data} + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_update_missing_county(self): + """Tests /place/?form=salesforce POST update missing county + address. It should 400.""" + model = self.env.place + address_data = { + 'street_address':'2 MetroTech Center', + 'city':'Brooklyn', + 'state':'NY', + 'country':'United States of America', + 'postal_code':'11210'} + data = { + 'sales_force_id': model.sales_force_id, + 'name': model.name, + 'address': address_data} + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_update_missing_state(self): + """Tests /place/?form=salesforce POST update missing state. It should + 400.""" + model = self.env.place + address_data = { + 'street_address':'2 MetroTech Center', + 'city':'Brooklyn', + 'county':'Kings', + 'country':'United States of America', + 'postal_code':'11210'} + data = { + 'sales_force_id': model.sales_force_id, + 'name': model.name, + 'address': address_data} + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_update_missing_country(self): + """Tests /place/?form=salesforce POST update missing country. It + should 400.""" + model = self.env.place + address_data = { + 'street_address':'2 MetroTech Center', + 'city':'Brooklyn', + 'county':'Kings', + 'state':'NY', + 'postal_code':'11210'} + data = { + 'sales_force_id': model.sales_force_id, + 'name': model.name, + 'address': address_data} + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_update_missing_postal_code(self): + """Tests /place/?form=salesforce POST update missing postal code + address. It should 400.""" + model = self.env.place + address_data = { + 'street_address':'2 MetroTech Center', + 'city':'Brooklyn', + 'county':'Kings', + 'state':'NY', + 'country':'United States of America'} + data = { + 'sales_force_id': model.sales_force_id, + 'name': model.name, + 'address': address_data} + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_update_missing_sf_id(self): + """Tests /place/?form=salesforce POST update missing salesfoce id. It + should 400.""" + model = self.env.place + address_data = { + 'street_address':'2 MetroTech Center', + 'city':'Brooklyn', + 'county':'Kings', + 'state':'NY', + 'country':'United States of America', + 'postal_code':'11210'} + data = { + 'name': model.name, + 'address': address_data} + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_update_missing_name(self): + """Tests /place/?form=salesforce POST update missing name. It should + 400.""" + model = self.env.place + address_data = { + 'street_address':'2 MetroTech Center', + 'city':'Brooklyn', + 'county':'Kings', + 'state':'NY', + 'country':'United States of America', + 'postal_code':'11210'} + data = { + 'sales_force_id': model.sales_force_id, + 'address': address_data} + response = self.post( + self.url, + data=data, + query_string={'form': 'salesforce'}) + self.assertEqual(response.status_code, 400) + + def test_update_missing_address(self): """ Tests /place/?form=salesforce without an address. It should 400. """ model = self.env.place -- GitLab From fdb2fc1a6e2ab17d6110483ca35a167c05ed742b Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Tue, 21 Jun 2016 15:39:40 -0400 Subject: [PATCH 117/118] Remove extra lines. --- app/tests/test_project.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/app/tests/test_project.py b/app/tests/test_project.py index df3cf3a..434b7b0 100644 --- a/app/tests/test_project.py +++ b/app/tests/test_project.py @@ -281,8 +281,7 @@ class TestSalesForceProject(SalesforceTestCase): def test_create_missing_place(self): """Tests /project/?form=salesforce POST create with no place. It - should 400. - """ + should 400.""" client_data = {'sales_force_id': 'test', 'name': 'test'} address_data = { 'street_address': '111 N Fake St', @@ -305,8 +304,7 @@ class TestSalesForceProject(SalesforceTestCase): def test_create_missing_address(self): """Tests /project/?form=salesforce POST with no address. It should - 400. - """ + 400.""" client_data = {'sales_force_id': 'test', 'name': 'test'} place_data = {'sales_force_id': 'test', 'name': 'test'} data = { @@ -323,7 +321,7 @@ class TestSalesForceProject(SalesforceTestCase): def test_create_missing_project_state(self): """Tests /project/?form=salesforce POST missing project state. It - should 400. """ + should 400.""" client_data = {'sales_force_id': 'test', 'name': 'test'} place_data = {'sales_force_id': 'test', 'name': 'test'} address_data = { @@ -347,7 +345,7 @@ class TestSalesForceProject(SalesforceTestCase): def test_create_missing_project_name(self): """Tests /project/?form=salesforce POST missing project state. It - should 400. """ + should 400.""" client_data = {'sales_force_id': 'test', 'name': 'test'} place_data = {'sales_force_id': 'test', 'name': 'test'} address_data = { -- GitLab From 810b7735c05e8d2e6554a5b6f096fd9bbfa5f09f Mon Sep 17 00:00:00 2001 From: Jose Contreras Date: Tue, 21 Jun 2016 15:41:49 -0400 Subject: [PATCH 118/118] Remove tests requiring county. --- app/tests/test_place.py | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/app/tests/test_place.py b/app/tests/test_place.py index 3b0ebb2..f068957 100644 --- a/app/tests/test_place.py +++ b/app/tests/test_place.py @@ -303,26 +303,6 @@ class TestSalesForcePlace(SalesforceTestCase): query_string={'form': 'salesforce'}) self.assertEqual(response.status_code, 400) - def test_create_missing_county(self): - """Tests /place/?form=salesforce POST create missing county. It - should 400.""" - address_data ={ - 'street_address':'15 MetroTech Center', - 'city':'Brooklyn', - 'state':'NY', - 'country':'United States of America', - 'postal_code':'11210'} - data = { - 'sales_force_id': 'test', - 'name': 'test place', - 'address': address_data - } - response = self.post( - self.url, - data=data, - query_string={'form': 'salesforce'}) - self.assertEqual(response.status_code, 400) - def test_create_missing_state(self): """Tests /place/?form=salesforce POST create missing state. It should 400.""" @@ -492,26 +472,6 @@ class TestSalesForcePlace(SalesforceTestCase): query_string={'form': 'salesforce'}) self.assertEqual(response.status_code, 400) - def test_update_missing_county(self): - """Tests /place/?form=salesforce POST update missing county - address. It should 400.""" - model = self.env.place - address_data = { - 'street_address':'2 MetroTech Center', - 'city':'Brooklyn', - 'state':'NY', - 'country':'United States of America', - 'postal_code':'11210'} - data = { - 'sales_force_id': model.sales_force_id, - 'name': model.name, - 'address': address_data} - response = self.post( - self.url, - data=data, - query_string={'form': 'salesforce'}) - self.assertEqual(response.status_code, 400) - def test_update_missing_state(self): """Tests /place/?form=salesforce POST update missing state. It should 400.""" -- GitLab