W-13656292 feat: add import bulk/resume commands#1091
Conversation
`data import bulk` will cache the API used when it created the job
| isState: true, | ||
| filename: BulkImportRequestCache.getFileName(), | ||
| stateFolder: Global.SF_STATE_FOLDER, | ||
| ttl: Duration.days(7), |
There was a problem hiding this comment.
bulk ingest/query job results are available for 7 days after being created:
https://developer.salesforce.com/docs/atlas.en-us.252.0.salesforce_app_limits_cheatsheet.meta/salesforce_app_limits_cheatsheet/salesforce_app_limits_platform_bulkapi.htm
| jobId: string; | ||
| processedRecords?: number; | ||
| successfulRecords?: number; | ||
| failedRecords?: number; |
There was a problem hiding this comment.
JSON output:
async: jobID
sync: jobID and num of processed/successful/failed records
| }); | ||
|
|
||
| try { | ||
| await job.poll(5000, timeout.milliseconds); |
There was a problem hiding this comment.
5s for the polling interval, we could add a --poll-interval flag if users want to customize this later.
| jobId: jobInfo.id, | ||
| processedRecords: jobInfo.numberRecordsProcessed, | ||
| successfulRecords: jobInfo.numberRecordsProcessed - (jobInfo.numberRecordsFailed ?? 0), | ||
| failedRecords: jobInfo.numberRecordsFailed, |
There was a problem hiding this comment.
The API only gives us the total of records processed (that includes success/failure) and failed ones so we need calculate successful qty.
https://developer.salesforce.com/docs/atlas.en-us.api_asynch.meta/api_asynch/get_job_info.htm
| ms.stop('failed'); | ||
| throw messages.createError( | ||
| 'error.jobFailed', | ||
| [jobInfo.errorMessage, conn.getUsername(), job.id], |
There was a problem hiding this comment.
jobInfo.errorMessage is guaranteed to be present on state=Failed:
https://github.com/jsforce/jsforce/blob/160426335c3d6f8efd1c3244eacb0454e755c988/src/api/bulk2.ts#L802
https://developer.salesforce.com/docs/atlas.en-us.api_asynch.meta/api_asynch/get_job_info.htm
| if (jobInfo.state === 'Aborted') { | ||
| ms.stop('failed'); | ||
| // TODO: replace this msg to point to `sf data bulk results` when it's added (W-12408034) | ||
| throw messages.createError('error.jobAborted', [conn.getUsername(), job.id], [], err as Error); |
There was a problem hiding this comment.
aborted jobs don't include an error msg, we log the sf org open .. msg so they can view more details in the org.
| char: 'w', | ||
| unit: 'minutes', | ||
| summary: messages.getMessage('flags.wait.summary'), | ||
| defaultValue: 5, |
There was a problem hiding this comment.
data import resume has a default wait time of 5 minutes.
This is on purpose so that if you don't specify --wait you still have a good chance the job finishes on time.
data upsert resume and data delete resume have --wait with 0 as a default, so on a first run without --wait you have to re-run them again with a bigger timeout if the job is still running.
| char: 'i', | ||
| length: 18, | ||
| startsWith: '750', | ||
| exactlyOne: ['use-most-recent'], |
There was a problem hiding this comment.
following @VivekMChawla's comments about specificity:
https://salesforce-internal.slack.com/archives/G02K6C90RBJ/p1722277847033949
sf data import resume is invalid, you have to pass either --job-id or --use-most-recent
src/commands/data/import/resume.ts
Outdated
| flags['job-id'], | ||
| flags['use-most-recent'], | ||
| undefined, | ||
| undefined |
There was a problem hiding this comment.
the 4th arg is supposed to be api-version but I didn't add that flag here because the API version used when creating the job is cached by data import bulk so we don't need to pass it here.
src/commands/data/import/resume.ts
Outdated
| const numberRecordsFailed = data?.numberRecordsFailed ?? 0; | ||
|
|
||
| if (data?.numberRecordsProcessed) { | ||
| return (data.numberRecordsProcessed - numberRecordsFailed).toString(); |
There was a problem hiding this comment.
we default to 0 on L84 for failed records if no API info is available yet (this state update happens while job is being processed) but only render the Successful records block if the API returns numberRecordsProcessed.
This makes oclif/mso render a spinner instead of 0 when there's no processed data (first seconds of a job run or if it failed).
src/commands/data/import/resume.ts
Outdated
| const jobInfo = await job.check(); | ||
|
|
||
| // send last data update so job status/num. of records processed/failed represent the last update | ||
| ms.goto('Processing the job', jobInfo); |
There was a problem hiding this comment.
if job.poll on L138 throws then the state rendered is from the previous poll update so we do one last update with fresh data from the org (job.check()) so the last state rendered is accurate (job state, record counter).
|
QA 🔴 bulk import with --wait It works as expected but 🟡 bulk import Question: is it supposed to be async by default? 🟢 bulk import with --async 🟢 resume an async import with --job-id 🟢 resume an async import with --use-most-recent 🟡 use invalid id for resume I would have expected an error about the id not being valid but instead got this. Not sure if that's right or not 🔴 links in terminal that doesn't support links you need to provide your own fallback so that terminal-link doesn't insert non-visible whitespace characters, which is what I think causes this issue. See https://github.com/salesforcecli/plugin-deploy-retrieve/blob/main/src/utils/deployStages.ts#L78 🟢 async insert and resume with large csv 🟢 handles failures |
should be fixed now (
yes, same as
fixed by writing a separate cache resolver for
added the fallback |
|
@mdonnalley this is ready for review/qa again, thanks! also, please squash-merge the PR when it's good to go (bunch of commits in my branch) EDIT: |
`job.poll` is in a try/catch, when a failure happens it throws but also emits `error`. We avoid stopping MSO on `error` because we want to send a last update in the `catch` block
|
🟢 bulk import with --wait 🟢 use invalid id for resume 🟢 links in terminal that doesn't support links |
import bulk/resume commandsimport bulk/resume commands

What does this PR do?
Adds 2 new commands:
data import bulktake a CSV with fields of an object and bulk insert them into the org:
happy path
Screen.Recording.2024-10-16.at.10.57.01.AM.mov
failed to import all records
Screen.Recording.2024-10-16.at.11.07.10.AM.mov
job failure
job aborted
Screen.Recording.2024-10-16.at.11.12.26.AM.mov
data import resumeresume an async/timed out import
Screen.Recording.2024-10-16.at.11.17.31.AM.mov
testing instructions
checkout PR locally and
sf plugins linkit, you can use the following CSV files available in plugin-data for testing:test/test-files/data-project/data/bulkUpsertLarge.csv(big)test/test-files/data-project/data/bulkUpsert.csv(smol)What issues does this PR fix or reference?
@W-13656292@