1

I am trying to write a bit of python code to permanently delete some records using the Salesforce.com Bulk API v2. Using curl, I have success. I:

  1. create an Bulk API 2 hardDelete job
  2. PUT the file to .../jobs/ingest/7501E00000BlHU6QAN/batches
  3. set the job status to UploadComplete
  4. check the job at .../jobs/ingest/7501E00000BlHU6QAN

Commands:

curl -X POST -H 'Authorization: Bearer XXXXX' -H 'Content-Type: application/json; charset=UTF-8' -H 'Accept: application/json' -d '{"object" : "SBQQ__Quote__c", "contentType" : "CSV", "operation" : "hardDelete", "lineEnding" : "CRLF"}' https://instance.my.salesforce.com/services/data/v45.0/jobs/ingest/ | python -m json.tool
{
    "id": "7501E00000BlHU6QAN",
    "operation": "hardDelete",
    "object": "MyObject",
    "createdById": "MyID",
    "createdDate": "2019-04-05T05:51:10.000+0000",
    "systemModstamp": "2019-04-05T05:51:10.000+0000",
    "state": "Open",
    "concurrencyMode": "Parallel",
    "contentType": "CSV",
    "apiVersion": 45.0,
    "contentUrl": "services/data/v45.0/jobs/ingest/7501E00000BlHU6QAN/batches",
    "lineEnding": "CRLF",
    "columnDelimiter": "COMMA"
}

curl -X PUT -H 'Authorization: Bearer XXXXX' -H 'Content-Type: text/csv' -H 'Accept: application/json' --upload-file /cygdrive/c/Users/kurtb/Desktop/RecordsToDelete.csv https://instance.my.salesforce.com/services/data/v45.0/jobs/ingest/7501E00000BlHU6QAN/batches

curl -X PATCH -H 'Authorization: Bearer XXXXX' -H 'Content-Type: application/json; charset=UTF-8' -H 'Accept: application/json' -d '{"state" : "UploadComplete" }' https://instance.my.salesforce.com/services/data/v45.0/jobs/ingest/7501E00000BlHU6QAN | python -m json.tool
{
    "id": "7501E00000BlHU6QAN",
    "operation": "hardDelete",
    "object": "MyObject",
    "createdById": "MyID",
    "createdDate": "2019-04-05T05:51:10.000+0000",
    "systemModstamp": "2019-04-05T05:51:10.000+0000",
    "state": "UploadComplete",
    "concurrencyMode": "Parallel",
    "contentType": "CSV",
    "apiVersion": 45.0
}

curl -X GET -H 'Authorization: Bearer XXXXX' -H 'Accept: application/json' https://instance.my.salesforce.com/services/data/v45.0/jobs/ingest/7501E00000BlHU6QAN | python -m json.tool
{
    "id": "7501E00000BlHU6QAN",
    "operation": "hardDelete",
    "object": "MyObject",
    "createdById": "MyID",
    "createdDate": "2019-04-05T05:51:10.000+0000",
    "systemModstamp": "2019-04-05T06:01:04.000+0000",
    "state": "JobComplete",
    "concurrencyMode": "Parallel",
    "contentType": "CSV",
    "apiVersion": 45.0,
    "jobType": "V2Ingest",
    "lineEnding": "CRLF",
    "columnDelimiter": "COMMA",
    "numberRecordsProcessed": 42,
    "numberRecordsFailed": 0,
    "retries": 0,
    "totalProcessingTime": 1272,
    "apiActiveProcessingTime": 1201,
    "apexProcessingTime": 302
}

SUCCESS!!!!!

In python, if I attempt to send the same file referenced above using:

Salesforce_API_Call('/%s' % Dictionary_AddedSalesforceBulkAPI2HardDeleteJob['contentUrl'], method = "PUT", files={'file': open('c:/users/Kurtb/Desktop/ItemsToDelete.csv', 'rb')})

Debug: headers: {'Content-type': 'text/csv', 'Accept': 'application/json', 'Authorization': 'Bearer XXXXX'}

Debug: API PUT call: https://instance.my.salesforce.com/services/data/v45.0/jobs/ingest/7501E00000BlHNjQAN/batches

and subsequently close the job manually:

curl -X PATCH -H 'Authorization: Bearer XXXXX' -H 'Content-Type: application/json; charset=UTF-8' -H 'Accept: application/json' -d '{"state" : "UploadComplete" }' https://instance.my.salesforce.com/services/data/v45.0/jobs/ingest/7501E00000BlHNjQAN | python -m json.tool

    "id": "7501E00000BlHNjQAN",
    "operation": "hardDelete",
    "object": "MyObject",
    "createdById": "MyID",
    "createdDate": "2019-04-05T05:09:57.000+0000",
    "systemModstamp": "2019-04-05T05:09:57.000+0000",
    "state": "UploadComplete",
    "concurrencyMode": "Parallel",
    "contentType": "CSV",
    "apiVersion": 45.0
}

and subsequently query the job to inquire on its disposition:

curl -X GET -H 'Authorization: Bearer XXXXX' -H 'Accept: application/json' https://instance.my.salesforce.com/services/data/v45.0/jobs/ingest/7501E00000BlHNjQAN | python -m json.tool
{
    "id": "7501E00000BlHNjQAN",
    "operation": "hardDelete",
    "object": "MyObject",
    "createdById": "MyID",
    "createdDate": "2019-04-05T05:09:57.000+0000",
    "systemModstamp": "2019-04-05T05:10:32.000+0000",
    "state": "Failed",
    "concurrencyMode": "Parallel",
    "contentType": "CSV",
    "apiVersion": 45.0,
    "jobType": "V2Ingest",
    "lineEnding": "CRLF",
    "columnDelimiter": "COMMA",
    "numberRecordsProcessed": 0,
    "numberRecordsFailed": 0,
    "retries": 0,
    "totalProcessingTime": 0,
    "apiActiveProcessingTime": 0,
    "apexProcessingTime": 0,
    "errorMessage": "InvalidBatch : InvalidBatch : The 'delete' batch must contain only ids"
}

FAILED!!!!

Here are first few lines of the RecordsToDelete.csv file: Id a3A1E000001AWTdUAO a3A1E000001AWTcUAO

Each line, including the last line, of the RecordsToDelete.csv file is terminated with CRLF.

enter image description here

Any thoughts as to why the same file PUT with curl works, but yet using python, it does not? Any thoughts as to where to look and/or how to configure Salesforce to debug what's happening when I'm using the python version?

  • try passing the only "ids" as the error message suggests – Sagar Hinsu Apr 5 at 8:41
  • What is the content of the file you are posting? What Python connector library are you using? – David Reed Apr 5 at 11:41
  • @SagarHinsu - Thank you for your suggestion. While it was not stated in the above post, I have tried the data structure both with and without a "header" row, and in either case, the job fails. Further, I would reinforce that using curl to dry-run the Bulk API v2 calls is successful with the same file generated. Thanks, again, for making time to suggest! – Kurt Buckardt Apr 5 at 17:17
  • @DavidReed - Thank you for making the time to reply to my question! I am using the python requests library (import requests). I'd swear that I put a snip of the file content in to the question, but upon review I don't see it. That's what I get for posting when I should have been sleeping :) Here's the first few lines: Id a3A1E000001AWTdUAO a3A1E000001AWTcUAO – Kurt Buckardt Apr 5 at 17:24
  • 1
    @DavidReed - Thank you, again for your thoughts. Hex editing the file shows 0x0D 0X0A at the end of each line (including the last line). Reconciling this to my trusty OLD ASCII chart, shows 0x0D resolves to Carriage Return & 0x0A resolves to "New Line Feed, New Line." As a guy that used to feed green bar into a band printer, I feel like I may be one of the few humans left on Earth that actually understands the significance of these hex values :) – Kurt Buckardt Apr 6 at 17:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.