Create and query Salesforce records
A REST channel that accepts JSON, maps it to a Campaign, creates the record and reads it back.
Overview
This is the canonical accept-and-transform flow: an external application sends JSON to a REST channel, a Python service maps that input to the format Salesforce expects, creates the record and returns its new ID. A second service reads a record back by its ID.
The example uses the Campaign object, but every sObject - Account, Contact, Lead, or your own custom objects - works the same way, because the REST API address space is uniform: /sobjects/{Type}/ to create, /sobjects/{Type}/{id} to read.
It assumes a connection named "My Salesforce Connection" already exists - the getting started guide shows how to create one.
Creating a record
The service defines its input and output with models, maps the input to a plain dict with the field names Salesforce expects - custom fields carry the __c suffix - and posts it:
# -*- coding: utf-8 -*-
# stdlib
from dataclasses import dataclass
# Zato
from zato.server.service import Model, Service
# ################################################################################################################################
@dataclass(init=False)
class CreateCampaignRequest(Model):
name: str
segment: str
@dataclass(init=False)
class CreateCampaignResponse(Model):
campaign_id: str
# ################################################################################################################################
class CreateCampaign(Service):
name = 'crm.create-campaign'
input = CreateCampaignRequest
output = CreateCampaignResponse
def handle(self):
# Map our input to the field names Salesforce expects ..
request = {
'Name': self.request.input.name,
'Segment__c': self.request.input.segment,
}
# .. get a connection to Salesforce ..
conn = self.salesforce['My Salesforce Connection']
# .. create the record now ..
sf_response = conn.post('/sobjects/Campaign/', request)
# .. and return the new record's ID to our caller.
response = CreateCampaignResponse()
response.campaign_id = sf_response['id']
self.response.payload = response
A successful create answers with the new record's ID and a success flag:
Reading a record back
A GET on the record's path returns all of its fields:
# -*- coding: utf-8 -*-
# Zato
from zato.server.service import Service
class GetCampaign(Service):
name = 'crm.get-campaign'
input = 'campaign_id'
def handle(self):
campaign_id = self.request.input.campaign_id
conn = self.salesforce['My Salesforce Connection']
# Read the record by its ID ..
campaign = conn.get(f'/sobjects/Campaign/{campaign_id}')
# .. and pass it on as is.
self.response.payload = campaign
To find records by their business attributes rather than by ID, use SOQL - the SOQL guide covers queries, relationship traversal and pagination.
Exposing the services over REST
Create a REST channel for each service - for instance, /api/campaign/create pointing to crm.create-campaign with the POST method, secured with Basic Auth. Your API clients authenticate with credentials they already understand while Zato deals with how to authenticate against Salesforce.
Testing
$ curl http://api:password@localhost:17010/api/campaign/create \
-d '{"name":"Summer promotion", "segment":"Enterprise customers"}'
{"campaign_id":"701RO00000BpkfMYAR"}
$
And reading it back:
$ curl http://api:password@localhost:17010/api/campaign/get \
-d '{"campaign_id":"701RO00000BpkfMYAR"}'
{"Id":"701RO00000BpkfMYAR", "Name":"Summer promotion", "Segment__c":"Enterprise customers"}
$
What to do when a create fails - a missing required field, an invalid picklist value - is covered in the error handling guide.