def testSalesforceAdapterOnSuccessFor1ToNObjects(self): """Ensure that 1:N -- well actually 2 here :) -- Salesforce Adapter mapped objects find their way into the appropriate Salesforce.com instance. """ # create multiple action adapters self.ff1.invokeFactory('SalesforcePFGAdapter', 'contact_adapter') self.ff1.invokeFactory('SalesforcePFGAdapter', 'account_adapter') # disable mailer adapter self.ff1.setActionAdapter(('contact_adapter','account_adapter',)) # configure our contact_adapter to create a contact on submission # last name is the lone required field self.ff1.contact_adapter.setTitle('Salesforce Contact Action Adapter') self.ff1.contact_adapter.setSFObjectType('Contact') self.ff1.contact_adapter.setFieldMap(( {'field_path': 'replyto', 'form_field': 'Your E-Mail Address', 'sf_field': 'Email'}, {'field_path': 'comments', 'form_field': 'Comments', 'sf_field': 'LastName'})) # configure our account_adapter to create a contact on submission # last name is the lone required field self.ff1.account_adapter.setTitle('Salesforce Account Action Adapter') self.ff1.account_adapter.setSFObjectType('Account') self.ff1.account_adapter.setFieldMap(( {'field_path':'comments', 'form_field': 'Comments', 'sf_field': 'Name'},)) # build the request and submit the form for both adapters fields = self.ff1._getFieldObjects() request = base.FakeRequest(replyto = '*****@*****.**', # mapped to Email (see above) comments='PloneTestCase1ToN') # mapped to LastName (see above) request.SESSION = {} # we only call onSuccess for our last SF adapter in the form # which calculates the need order and executes all SF adapters in # the appropriate sequence self.ff1.account_adapter.onSuccess(fields, request) # direct query of Salesforce to get the id of the newly created contact contact_res = self.salesforce.query( "SELECT Id FROM %s WHERE Email='%s' AND LastName='%s'" % ( self.ff1.contact_adapter.getSFObjectType(), '*****@*****.**', 'PloneTestCase1ToN') ) # in case we fail, stock up our to delete list for tear down self._todelete.append(contact_res['records'][0]['Id']) # direct query of Salesforce to get the id of the newly created account account_res = self.salesforce.query( "SELECT Id FROM %s WHERE Name='%s'" % ( self.ff1.account_adapter.getSFObjectType(), 'PloneTestCase1ToN') ) # in case we fail, stock up our to delete list for tear down self._todelete.append(account_res['records'][0]['Id']) # assert that our newly created Contact was found self.assertEqual(1, contact_res['size']) self.assertEqual(1, account_res['size'])
def testEmptyFormDateFieldIsntPushedUpstreamAsInvalidXSDDateTime(self): """Assuming we survive a test for issue (i.e. empty string isn't cast to DateTime causing a syntax error): http://plone.org/products/salesforcepfgadapter/issues/5 We also want to make sure this isn't added to sObject and therefore failing upon an attempt to create the object in Salesforce due to a SoapFaultError of invalid value for the type xsd:dateTime. See: http://plone.org/products/salesforcepfgadapter/issues/6 """ # create a attachmetn action adapter self.ff1.invokeFactory('SalesforcePFGAdapter', 'contact_adapter') # disable mailer adapter self.ff1.setActionAdapter(('contact_adapter', )) # configure our contact_adapter to create an Attachment on submission self.ff1.contact_adapter.setTitle('Salesforce Contact Action Adapter') self.ff1.contact_adapter.setSFObjectType('Contact') # bogus mapping to meet Contact creation reqs, # we optionally ask for the Birtdate in the form self.ff1.contact_adapter.setFieldMap(( { 'field_path': 'comments', 'form_field': 'Comments', 'sf_field': 'LastName' }, { 'field_path': 'date', 'form_field': 'date', 'sf_field': 'Birthdate' }, )) # build the request and submit the form for both adapters fields = self.ff1._getFieldObjects() # assuming there was a FormDateField that was not filled out, it would # look like the following in the request: request = base.FakeRequest(comments='PloneTestCaseEmptyDateField', date='') request.SESSION = {} # call onSuccess self.ff1.contact_adapter.onSuccess(fields, request) # query for our attachment contact_res = self.salesforce.query( "SELECt Id, Birthdate FROM Contact WHERE LastName='PloneTestCaseEmptyDateField'" ) # in case we fail, stock up our to delete list for tear down self._todelete.append(contact_res['records'][0]['Id']) # make our assertions self.failIf(contact_res['records'][0]['Birthdate'])
def testFieldsetContainedFieldsCorrectlyPushedToSalesforceObject(self): """As a follow-up to ensuring our fieldset fields can be mapped, we ensure their values are appropriately pushed to Salesforce.com See: http://plone.org/products/salesforcepfgadapter/issues/2 """ # create a new fieldset within the form self.ff1.invokeFactory('FieldsetFolder', 'subform') self.ff1.subform.setTitle("Subform Fieldset") # create a field local to the fieldset self.ff1.subform.invokeFactory('FormStringField', 'subformfield') self.ff1.subform.subformfield.setTitle("Subform Field") # create a contact action adapters self.ff1.invokeFactory('SalesforcePFGAdapter', 'contact_adapter') # disable mailer adapter self.ff1.setActionAdapter(('contact_adapter',)) # configure our contact_adapter to create a contact on submission # last name is the lone required field, but we map a few others # and our fieldset field ... self.ff1.contact_adapter.setTitle('Salesforce Contact Action Adapter') self.ff1.contact_adapter.setSFObjectType('Contact') self.ff1.contact_adapter.setFieldMap(( {'field_path': 'replyto', 'form_field': 'Your E-Mail Address', 'sf_field': 'Email'}, {'field_path': 'comments', 'form_field': 'Comments', 'sf_field': 'LastName'}, {'field_path': 'subform,subformfield', 'form_field': 'Subform Fieldset --> Subform Field', 'sf_field': 'FirstName'}, )) # build the request and submit the form for both adapters fields = self.ff1._getFieldObjects() request = base.FakeRequest(replyto = '*****@*****.**', # mapped to Email (see above) comments='PloneTestCaseFieldsetFields', # mapped to LastName (see above) subformfield='PloneTestCaseFieldsetSubField',) # mapped to FirstName (see above) request.SESSION = {} # we only call onSuccess for our last SF adapter in the form # which calculates the need order and executes all SF adapters in # the appropriate sequence self.ff1.contact_adapter.onSuccess(fields, request) # direct query of Salesforce to get the id of the newly created contact contact_res = self.salesforce.query( "SELECT Id, FirstName FROM %s WHERE LastName='%s'" % ( self.ff1.contact_adapter.getSFObjectType(), 'PloneTestCaseFieldsetFields') ) # in case we fail, stock up our to delete list for tear down self._todelete.append(contact_res['records'][0]['Id']) # assert that our newly created Contact was found self.assertEqual(request.form['subformfield'], contact_res['records'][0]['FirstName'])
def testEmptyIntegerFieldIsntPushedUpstreamAsInvalidXSDIntDouble(self): """A correctly configured form may ask for an optional "on a scale of 1-5" type question that is left blank. The build object for creation code shouldn't assume too much about this and turn it into something it's not, like a string. We use lead creation and and the NumberOfEmployees field as an example below. See: http://plone.org/products/salesforcepfgadapter/issues/8 """ # create a attachmetn action adapter self.ff1.invokeFactory('SalesforcePFGAdapter', 'lead_adapter') # disable mailer adapter self.ff1.setActionAdapter(('lead_adapter',)) # configure our contact_adapter to create an Attachment on submission self.ff1.lead_adapter.setTitle('Salesforce Lead Action Adapter') self.ff1.lead_adapter.setSFObjectType('Lead') self.ff1.invokeFactory('FormIntegerField', 'num') self.ff1.num.setTitle('num') # bogus mapping to meet Contact creation reqs, # we optionally ask for the Birtdate in the form self.ff1.lead_adapter.setFieldMap(( {'field_path': 'comments', 'form_field': 'Comments', 'sf_field': 'LastName'}, {'field_path': 'topic', 'form_field': 'Subject', 'sf_field': 'Company'}, {'field_path': 'num', 'form_field': 'num', 'sf_field': 'NumberOfEmployees'}, )) # build the request and submit the form for both adapters fields = self.ff1._getFieldObjects() # assuming there was a FormIntegerField that was not filled out, it would # look like the following in the request: request = base.FakeRequest(comments = 'PloneTestCaseEmptyIntegerField', topic = 'PloneTestCaseEmptyIntegerFieldCompany') request.SESSION = {} # call onSuccess self.ff1.lead_adapter.onSuccess(fields, request) # query for our attachment lead_res = self.salesforce.query( "SELECT Id, NumberOfEmployees FROM Lead WHERE LastName='%s'" % ( 'PloneTestCaseEmptyIntegerField') ) # in case we fail, stock up our to delete list for tear down self._todelete.append(lead_res['records'][0]['Id']) # make our assertions self.failIf(lead_res['records'][0]['NumberOfEmployees'])
def _createTestContact(self): # first create a new contact - build the request and submit the form self.ff1.contact_adapter.setCreationMode('create') fields = self.ff1._getFieldObjects() request = base.FakeRequest(replyto = '*****@*****.**', # mapped to Email (see above) comments='PloneTestCase') # mapped to LastName (see above) request.SESSION = {} self.ff1.contact_adapter.onSuccess(fields, request) # direct query of Salesforce to get the id of the newly created contact res = self.salesforce.query(['Id',],self.ff1.contact_adapter.getSFObjectType(), "Email='*****@*****.**' and LastName='PloneTestCase'") self._todelete.append(res['records'][0]['Id']) # assert that our newly created Contact was found self.assertEqual(1, res['size']) self.ff1.contact_adapter.setCreationMode('update')
def testEmptyFormDateFieldDoesntFailDateTimeConversion(self): """We don't want to try and cast an empty FormDateField value to an appropriately formatted DateTime. See: http://plone.org/products/salesforcepfgadapter/issues/5 """ # assuming there was a FormDateField that was not filled out, it would # look like the following in the request: request = base.FakeRequest(topic='test subject', replyto='*****@*****.**', date='') fields = [self.ff1.date] # attempt to build the object which would trigger # SyntaxError: Unable to parse (' GMT+0',), {} # if the date came through as '' and we tried to coerce to DateTime sObject = self.ff1.salesforce._buildSObjectFromForm(fields, REQUEST=request) self.failIf(sObject.has_key('date'))
def testSalesforceAdapterOnSuccess(self): """Ensure that our Salesforce Adapter mapped objects find their way into the appropriate Salesforce.com instance. """ # create our action adapter self.ff1.invokeFactory('SalesforcePFGAdapter', 'contact_adapter') # disable mailer adapter self.ff1.setActionAdapter(('contact_adapter',)) # configure our action adapter to create a contact on submission # last name is the lone required field self.ff1.contact_adapter.setTitle('Salesforce Action Adapter') self.ff1.contact_adapter.setSFObjectType('Contact') self.ff1.contact_adapter.setFieldMap(( {'field_path': 'replyto', 'form_field': 'Your E-Mail Address', 'sf_field': 'Email'}, )) self.ff1.contact_adapter.setPresetValueMap(( {'value': 'PloneTestCase', 'sf_field': 'LastName'}, )) # build the request and submit the form fields = self.ff1._getFieldObjects() request = base.FakeRequest(replyto = '*****@*****.**') # mapped to Email (see above) request.SESSION = {} self.ff1.contact_adapter.onSuccess(fields, request) # direct query of Salesforce to get the id of the newly created contact res = self.salesforce.query( "SELECT Id FROM %s WHERE Email='%s' AND LastName='%s'" % ( self.ff1.contact_adapter.getSFObjectType(), '*****@*****.**', 'PloneTestCase') ) self._todelete.append(res['records'][0]['Id']) # assert that our newly created Contact was found self.assertEqual(1, res['size'])
def testDateFieldConvertedToSalesforceFormat(self): """ Prove that DateField values get converted to the format expected by Salesforce (mm/dd/yyyy). """ from DateTime import DateTime now = DateTime() now_plone = now.strftime('%m-%d-%Y %H:%M') request = base.FakeRequest(topic='test subject', replyto='*****@*****.**', date=now_plone) fields = [self.ff1.date] sObject = self.ff1.salesforce._buildSObjectFromForm(fields, REQUEST=request) from time import strptime try: strptime(sObject['date'], '%Y-%m-%dT%H:%M:%SZ') except ValueError: self.fail( "Doesn't look like the date was converted to Salesforce format properly." )
def testPresetValueMapExpressions(self): """Ensure that our Salesforce Adapter mapped objects find their way into the appropriate Salesforce.com instance. """ # create our action adapter self.ff1.invokeFactory('SalesforcePFGAdapter', 'contact_adapter') # disable mailer adapter self.ff1.setActionAdapter(('contact_adapter',)) # configure our action adapter to create a contact on submission, # exercising various expression formats self.ff1.contact_adapter.setTitle('Salesforce Action Adapter') self.ff1.contact_adapter.setSFObjectType('Contact') self.ff1.contact_adapter.setPresetValueMap(( {'value': 'PloneTestCase', 'sf_field': 'LastName'}, {'value': 'path:object/portal_url', 'sf_field': 'Description'}, {'value': 'python:now', 'sf_field': 'Birthdate'}, )) # build the request and submit the form fields = self.ff1._getFieldObjects() request = base.FakeRequest() request.SESSION = {} self.ff1.contact_adapter.onSuccess(fields, request) # direct query of Salesforce to get the id of the newly created contact res = self.salesforce.query( "SELECT Id, LastName, Description, Birthdate FROM %s WHERE LastName='%s'" % ( self.ff1.contact_adapter.getSFObjectType(), 'PloneTestCase') ) self._todelete.append(res['records'][0]['Id']) # check the stored values self.assertEqual(1, res['size']) self.assertEqual('PloneTestCase', res[0].LastName) self.failUnless(res[0].Description.startswith('http://')) self.failUnless(isinstance(res[0].Birthdate, datetime.date))
def testChainedDependenciesInsertCorrectly(self): # create multiple action adapters self.ff1.invokeFactory('SalesforcePFGAdapter', 'contact_adapter') self.ff1.invokeFactory('SalesforcePFGAdapter', 'account_adapter') # disable mailer adapter self.ff1.setActionAdapter(( 'contact_adapter', 'account_adapter', )) # configure our contact_adapter to create a contact on submission # last name is the lone required field self.ff1.contact_adapter.setTitle('Salesforce Contact Action Adapter') self.ff1.contact_adapter.setSFObjectType('Contact') self.ff1.contact_adapter.setFieldMap(({ 'field_path': 'replyto', 'form_field': 'Your E-Mail Address', 'sf_field': 'Email' }, { 'field_path': 'comments', 'form_field': 'Comments', 'sf_field': 'LastName' })) # configure our account_adapter to create a contact on submission # last name is the lone required field self.ff1.account_adapter.setTitle('Salesforce Account Action Adapter') self.ff1.account_adapter.setSFObjectType('Account') self.ff1.account_adapter.setFieldMap(({ 'field_path': 'topic', 'form_field': 'Subject', 'sf_field': 'Name' }, )) # set up dependencies self.ff1.contact_adapter.setDependencyMap(({ 'adapter_id': 'account_adapter', 'adapter_name': 'Salesforce Account Action Adapter', 'sf_field': 'AccountId' }, )) request = base.FakeRequest( topic="testChainedDependenciesInsertCorrectly", replyto='*****@*****.**', comments='testChainedDependenciesInsertCorrectly') request.SESSION = {} # call onSuccess on last SF adapter in form fields = self.ff1._getFieldObjects() self.ff1.account_adapter.onSuccess(fields, request) # salesforce queries and cleanup contact_res = self.salesforce.query( "SELECT Id, AccountId FROM Contact WHERE LastName='%s' AND Email='%s'" % ('testChainedDependenciesInsertCorrectly', '*****@*****.**')) self._todelete.append(contact_res['records'][0]['Id']) # for clean-up account_res = self.salesforce.query( "SELECT Id FROM Account WHERE Name = 'testChainedDependenciesInsertCorrectly'" ) account_id = account_res['records'][0]['Id'] self._todelete.append(account_id) # for clean-up # assertions self.assertEqual(1, contact_res['size']) self.assertEqual(1, account_res['size']) self.assertEqual(account_id, contact_res['records'][0]['AccountId'])
def testChainedAdaptersAccountsForNonexecutingAdapters(self): """We delegate the creation of all Salesforce objects for each adapter to the final adapter. This final adapter, however, must be an adapter that's enabled (i.e. present in the getActionAdapter list). Here we ensure that all active adapters are successfully run (this would be done by the final active adapter -- but that is an unimportant implementation detail.) """ # create multiple action adapters self.ff1.invokeFactory('SalesforcePFGAdapter', 'contact_adapter') self.ff1.invokeFactory('SalesforcePFGAdapter', 'account_adapter') # disable mailer adapter self.ff1.setActionAdapter(( 'contact_adapter', 'account_adapter', )) # configure our contact_adapter to create a contact on submission # last name is the lone required field self.ff1.contact_adapter.setTitle('Salesforce Contact Action Adapter') self.ff1.contact_adapter.setSFObjectType('Contact') # ... but configure a totally bogus execution # condition that could never be true self.ff1.contact_adapter.setFieldMap(({ 'field_path': 'replyto', 'form_field': 'Your E-Mail Address', 'sf_field': 'Email' }, { 'field_path': 'comments', 'form_field': 'Comments', 'sf_field': 'LastName' })) # configure our account_adapter to create a contact on submission # last name is the lone required field self.ff1.account_adapter.setTitle('Salesforce Account Action Adapter') self.ff1.account_adapter.setSFObjectType('Account') self.ff1.account_adapter.setExecCondition('python:1 == 0') self.ff1.account_adapter.setFieldMap(({ 'field_path': 'topic', 'form_field': 'Subject', 'sf_field': 'Name' }, )) # set up dependencies self.ff1.contact_adapter.setDependencyMap(({ 'adapter_id': 'account_adapter', 'adapter_name': 'Salesforce Account Action Adapter', 'sf_field': 'AccountId' }, )) request = base.FakeRequest( topic="testChainedRespectNonexecutableFinalAdapters", replyto='*****@*****.**', comments='testChainedRespectNonexecutableFinalAdapters') request.SESSION = {} fields = self.ff1._getFieldObjects() # call onSuccess on last *executable* SF adapter in form self.ff1.contact_adapter.onSuccess(fields, request) # salesforce queries and cleanup contact_res = self.salesforce.query( "SELECT Id, AccountId FROM Contact WHERE LastName='%s' AND Email='%s'" % ('testChainedRespectNonexecutableFinalAdapters', '*****@*****.**')) self._todelete.append(contact_res['records'][0]['Id']) # for clean-up account_res = self.salesforce.query( "SELECT Id FROM Account WHERE Name = 'testChainedRespectNonexecutableFinalAdapters'" ) # assertions self.assertEqual(1, contact_res['size']) self.assertEqual(0, account_res['size']) self.failIf(contact_res['records'][0]['AccountId'])
def testFileFieldsSavedToSalesforce(self): """There may be other use cases, but the Attachment type in Salesforce can be associated with any other type and is where binary data, as stored on the Body field, is typically associated with a record. Here we confirm that a binary file can be mapped and is succesfully posted to Salesforce.com via PloneFormGen's FormFileField type. """ def _createBinaryFile(): from cgi import FieldStorage from ZPublisher.HTTPRequest import FileUpload from tempfile import TemporaryFile fp = TemporaryFile('w+b') fp.write('\x00' + 'x' * (1 << 19)) fp.seek(0) env = {'REQUEST_METHOD':'PUT'} headers = {'content-type':'application/msword', 'content-length': 1 << 19, 'content-disposition':'attachment; filename=test.doc'} fs = FieldStorage(fp=fp, environ=env, headers=headers) return FileUpload(fs) # add a file field to our standard form self.ff1.invokeFactory('FormFileField','filefield') self.ff1.filefield.setTitle("File") # directly create a contact for association, since we need # a valid parent id and don't care about related objects here sObject = dict(type='Contact') sObject['LastName'] = 'testFileFieldsSavedToSalesforce' contact_create_res = self.salesforce.create(sObject) # get ready to cleanup regardless of test case success self._todelete.append(contact_create_res[0]['id']) # create a attachment action adapter self.ff1.invokeFactory('SalesforcePFGAdapter', 'attachment_adapter') # disable mailer adapter self.ff1.setActionAdapter(('attachment_adapter',)) # configure our attachment_adapter to create an Attachment on submission self.ff1.attachment_adapter.setTitle('Salesforce Attachment Action Adapter') self.ff1.attachment_adapter.setSFObjectType('Attachment') # bogus mapping to meet Attachment reqs self.ff1.attachment_adapter.setFieldMap(( {'field_path': 'replyto', 'form_field': 'Your E-Mail Address', 'sf_field': 'ParentId'}, {'field_path': 'filefield', 'form_field': 'File', 'sf_field': 'Body'}, {'field_path': 'filefield,mimetype', 'form_field': 'File Mimetype', 'sf_field': 'ContentType'}, {'field_path': 'filefield,filename', 'form_field': 'File Filename', 'sf_field': 'Name'}, )) # build the request and submit the form for both adapters fields = self.ff1._getFieldObjects() request = base.FakeRequest(replyto = contact_create_res[0]['id'], # mapped to ParentId (see above) filefield_file=_createBinaryFile(),) # mapped to FirstName (see above) request.SESSION = {} # call onSuccess self.ff1.attachment_adapter.onSuccess(fields, request) # query for our attachment attach_res = self.salesforce.query( "SELECT Id, Name, ContentType, BodyLength FROM Attachment WHERE ParentId='%s'" % ( contact_create_res[0]['id']) ) # in case we fail, stock up our to delete list for tear down self._todelete.append(attach_res['records'][0]['Id']) # make our assertions self.assertEqual('test.doc', attach_res['records'][0]['Name']) self.assertEqual('application/msword', attach_res['records'][0]['ContentType']) self.failUnless(attach_res['records'][0]['BodyLength'] > 0)