#!/usr/bin/env python3
import json, difflib
postman_json_text = '''{
"TemplateDocument": {
"Href": "https://apiuatna11.springcm.com/v2/bccae332-c7db-4892-ab85-257df0f70fea/documents/a0cbc0e6-d87d-459e-8d63-66baa47878f3" },
"DataXML": "AC-0000102/04/2026123 Main St, Denver, CO 802021Missing comparable sale adjustment detail.Added adjustment rationale and supporting calculations.2Neighborhood trend explanation insufficient.Expanded market trend narrative with MLS evidence.3Photo date stamps were not included.Re-uploaded photos with date metadata and captions.3",
"DestinationDocumentName": "Review_AC-00001.docx",
"DestinationFolder": {
"Href": "https://apiuatna11.springcm.com/v2/bccae332-c7db-4892-ab85-257df0f70fea/folders/12220442-b12e-f111-84fc-88e9a4bd0d9c"
}
}
'''
# Parse the Postman JSON
postman = json.loads(postman_json_text)
# Reconstruct the Apex payload (simulate AppraiserCasePayloadBuilder output)
payload = {
'AppraiserCaseNumber': 'AC-00001',
'AppraiserFieldReviewDate': '02/04/2026',
'PropertyAddress': '123 Main St, Denver, CO 80202',
'DeficiencyList': [
{'deficiencyNumber': '1', 'description': 'Missing comparable sale adjustment detail.', 'resolution': 'Added adjustment rationale and supporting calculations.'},
{'deficiencyNumber': '2', 'description': 'Neighborhood trend explanation insufficient.', 'resolution': 'Expanded market trend narrative with MLS evidence.'},
{'deficiencyNumber': '3', 'description': 'Photo date stamps were not included.', 'resolution': 'Re-uploaded photos with date metadata and captions.'}
]
}
# Function to mimic buildDataXml from Apex implementation
def escape_xml(s):
if s is None:
return ''
return s.replace('&','&').replace('<','<').replace('>','>').replace('"','"').replace("'","'")
def build_data_xml(payload):
xml = ''
# Emit flat fields first (Apex iterates payload.keySet() which is not ordered in Python dict, but we'll follow this order)
# We'll follow the same ordering as in Postman: AppraiserCaseNumber, AppraiserFieldReviewDate, PropertyAddress
for key in ['AppraiserCaseNumber','AppraiserFieldReviewDate','PropertyAddress']:
v = payload.get(key)
xml += f'<{key}>' + escape_xml(str(v) if v is not None else '') + f'{key}>'
# Emit DeficiencyList nested structure
deficiencies = payload.get('DeficiencyList')
if deficiencies:
xml += ''
for d in deficiencies:
xml += ''
xml += '' + escape_xml(str(d.get('deficiencyNumber'))) + ''
xml += '' + escape_xml(str(d.get('description'))) + ''
xml += '' + escape_xml(str(d.get('resolution'))) + ''
xml += ''
xml += ''
xml += '' + str(len(deficiencies)) + ''
xml += ''
return xml
apex_data_xml = build_data_xml(payload)
apex_request = {
'TemplateDocument': {'Href': postman['TemplateDocument']['Href']},
'DataXML': apex_data_xml,
'DestinationDocumentName': postman['DestinationDocumentName'],
'DestinationFolder': {'Href': postman['DestinationFolder']['Href']}
}
# Compare postman vs apex
print('Comparing top-level fields:')
for key in ['TemplateDocument','DestinationDocumentName','DestinationFolder']:
p = postman.get(key)
a = apex_request.get(key)
equal = (p == a)
print(f' - {key}:', 'MATCH' if equal else 'DIFFER')
# Compare DataXML
postman_xml = postman['DataXML']
apex_xml = apex_request['DataXML']
if postman_xml == apex_xml:
print('\nDataXML: EXACT MATCH')
else:
print('\nDataXML: DIFFER')
# show diff
pd = postman_xml.split('><')
ad = apex_xml.split('><')
diff = difflib.unified_diff(pd, ad, fromfile='postman_xml', tofile='apex_xml', lineterm='')
print('\n'.join(diff))
# also show small head/tail
print('\n-- Postman head 200 chars --\n', postman_xml[:200])
print('\n-- Apex head 200 chars --\n', apex_xml[:200])
# Additionally print whether other top-level objects match (TemplateDocument Href and Folder Href)
print('\nTemplateDocument.Href match:', postman['TemplateDocument']['Href'] == apex_request['TemplateDocument']['Href'])
print('DestinationFolder.Href match:', postman['DestinationFolder']['Href'] == apex_request['DestinationFolder']['Href'])
# Print apex_data_xml for inspection
print('\n--- Apex DataXML ---\n')
print(apex_data_xml)