Step 5: Migrating the relationship records of legacy custom objects
This article describes how to migrate the relationship records of your legacy objects to the new custom objects experience. Relationship records define the relationships between existing records.
Topics covered in this article:
- Understanding how relationship records map to object records in the new custom objects experience
- Migrating the record relationships of legacy custom objects
- Next steps
This is the fifth and last article in a series on migrating legacy custom objects:
- Step 1: Migrating the object types of legacy custom objects
- Step 2: Migrating the schemas of legacy custom objects
- Step 3: Migrating the relationship types of legacy custom objects
- Step 4: Migrating the records of legacy custom objects
- Step 5: Migrating the relationship records of legacy custom objects
Understanding how relationship records map to object records in the new custom objects experience
In the legacy custom objects experience, a relationship between two records is established by a relationship record. Example:
{
"data": {
"id": "1a034055-6485-11ea-a338-a7395da450f1",
"relationship_type": "dogs_assigned_to_walker",
"source": "zen:user:623577345673",
"target": "fc7925e3-6484-11ea-a338-19eb2b9d27d1"
}
}
In the new custom objects experience, a relationship between two records is established when the value of a lookup relationship field on a record is updated. For example, to create a "dogs_assigned_to_walker" relationship between records, an agent might update an "Assigned walker" lookup field on a dog record with the id of the Zendesk user. After the update, the dog record might look as follows:
{
"custom_object_record": {
"custom_object_fields": {
"breed": "Corgi",
"training": false,
"assigned_walker": 3462224532
},
"name": "Miso",
"id": "01GDXYEY1FQYN066VHF49YHJ21"
}
}
The lookup field is always on the records of the "many" object in a "many-to-one" relationship. This corresponds to the target
object of the associated legacy relationship type. As a result, you always update the record specified by the target
record of the legacy relationship record. This record is either from a Zendesk standard object or from a migrated custom object.
The JSON format of a Zendesk standard record is different from the format of a custom object record like the one shown in the example above. For example, suppose Zendesk user records have a lookup relationship field. The field might looks as follows in a user record:
{
"user": {
"id": 623577345673,
"custom_fields": [
{
"id": 23618791821075,
"value": "01HGK5DPHSAWGVE492CAWZSE7A"
},
...
]
...
}
}
Lookup fields and other custom fields are contained in the custom_fields
array of Zendesk standard records. The fields are identified only by their 'id'.
You must identify and update a specific lookup relationship field in the array. The value
of a lookup relationship field is a record id.
Even if the custom fields in a Zendesk standard record are contained in an array, you don't need to retrieve the full array to update a specific field. You can update a specific field in the array. The API will merge the updated field with the existing fields. The other fields won't be overwritten.
Use the endpoint to update the specific Zendesk record to update the lookup field:
- for ticket records, use Update Ticket
- for user records, use Update User
- for organization records, use Update Organization
Migrating the record relationships of legacy custom objects
Migrating a relationship record consists of assigning the relationship record's source
record id to the lookup field on the relationship's target
record.
The record ids of standard Zendesk objects are the same in both custom objects experiences. You can assign these to lookup fields as is.
However, the record ids of legacy records are different from their associated migrated records. When you migrated the records in the previous step, you created a map of record ids that associated each legacy record id to a new record id. Use this map the assign the correct record ids to the lookup fields of records.
You can retrieve relationships records by relationship type. After retrieving the relationship records, you can use them to update the lookup fields of records in the new experience.
Example main function logic
The following example main()
function shows the general logic of migrating legacy relationship records to the new custom objects experience. The called functions are explained in the follow-up sections.
def main():
record_map = read_json_file('record_map.json')
lookup_field_map = read_json_file('lookup_field_map.json')
relationship_types = get_relationship_types()
for relationship_type in relationship_types:
relationship_records = get_relationship_records(relationship_type)
for relationship_record in relationship_records:
if relationship_record['target'][:4] == 'zen:':
update_zen_object_target_record(relationship_record, record_map, lookup_field_map)
else:
update_custom_object_target_record(relationship_record, record_map, relationship_type)
# utility function
def read_json_file(file_path):
with open(file_path, mode='r', encoding='utf-8') as f:
content = json.load(f)
return content
Example function for getting relationship records
You can retrieve relationship records by relationship type:
GET /api/sunshine/relationships/records?type={relationship_type}
See List Legacy Relationship Records by Type.
The following example Python functions gets the relationship records for a specific relationship type.
def get_relationship_records(relationship_type):
relationship_records = []
endpoint = f'api/sunshine/relationships/records'
url = f'https://{ZENDESK_SUBDOMAIN}.zendesk.com/{endpoint}'
params = {'type': relationship_type["key"], 'per_page': 1000}
auth = f'{ZENDESK_USER_EMAIL}/token', ZENDESK_API_TOKEN
response = requests.get(url, params=params, auth=auth)
# paginate
while url:
if response.status_code != 200:
print(f'Failed to retrieve relationship records: {response.status_code}: {response.text}')
return []
records = response.json()['data']
if records:
relationship_records.extend(records)
links = response.json()['links']
if links['next']:
url = f'https://{ZENDESK_SUBDOMAIN}.zendesk.com{links["next"]}'
response = requests.get(url, auth=auth)
else:
url = ''
return relationship_records
Example function for updating Zendesk object records
The following example Python function updates a lookup field in a Zendesk object record.
def update_zen_object_target_record(rel_record, record_map, lookup_field_map):
# replace legacy record ids
rel_record = update_legacy_record_ids(rel_record, record_map) # see below
if not rel_record:
return {}
# gather the request parameters
source_record_id = rel_record['source']
if source_record_id[:4] == 'zen:':
source_record_id = source_record_id.split(':')[2]
target_record_id = rel_record['target'].split(':')[2]
zen_object = rel_record['target'].split(':')[1] # example, user in zen:user:373545462053
lookup_field_id = lookup_field_map[rel_record['relationship_type']]
# create the request body
custom_fields = [{'id': lookup_field_id, 'value': source_record_id}]
data = {zen_object: {'custom_fields': custom_fields}}
# make the request
endpoint = f'api/v2/{zen_object}s/{target_record_id}'
url = f'https://{ZENDESK_SUBDOMAIN}.zendesk.com/{endpoint}'
response = requests.put(url, json=data, auth=AUTH)
if response.status_code != 200:
print(f'{response.status_code}: {response.text}')
return {}
return response.json()
# utility function
def update_legacy_record_ids(rel_record, record_map) -> dict:
for record_type in ['source', 'target']:
record_id = rel_record[record_type]
if record_id[:4] == 'zen:':
continue
if record_id not in record_map:
print(f'- Error: Record {record_id} not found in record map')
return {}
rel_record[record_type] = record_map[record_id]
return rel_record
Example function for updating custom object records
The following example Python function updates a lookup field in a custom object record.
def update_custom_object_target_record(rel_record, record_map, rel_type):
# replace legacy record ids
rel_record = update_legacy_record_ids(rel_record, record_map)
if not rel_record:
return {}
# gather the request parameters
target_record_id = rel_record['target']
source_record_id = rel_record['source']
if source_record_id[:4] == 'zen:':
source_record_id = source_record_id.split(':')[2]
lookup_field_name = rel_record['relationship_type'] # assumes same key for migrated lookup field
# create the request body
data = {
'custom_object_record': {
'custom_object_fields': {
f'{lookup_field_name}': f'{source_record_id}',
}
}
}
# make the request
target_object_key = rel_type['target'][0] # assumes same key for migrated custom object
endpoint = f'api/v2/custom_objects/{target_object_key}/records/{target_record_id}'
url = f'https://{ZENDESK_SUBDOMAIN}.zendesk.com/{endpoint}'
response = requests.patch(url, json=data, auth=AUTH)
if response.status_code != 200:
print(f'{response.status_code}: {response.text}')
return {}
return response.json()
Next steps
This article is the last in this five-part series on migrating legacy custom objects.