diff --git a/command-snapshot.json b/command-snapshot.json index e99d7f20..e1776817 100644 --- a/command-snapshot.json +++ b/command-snapshot.json @@ -35,6 +35,7 @@ "async", "file", "flags-dir", + "hard-delete", "json", "loglevel", "sobject", diff --git a/messages/bulk.operation.command.md b/messages/bulk.operation.command.md index f25b06c0..95553561 100644 --- a/messages/bulk.operation.command.md +++ b/messages/bulk.operation.command.md @@ -17,3 +17,7 @@ Run the command asynchronously. # flags.verbose.summary Print verbose output of failed records if result is available. + +# hard-delete-permission-error + +You must have the "Bulk API Hard Delete" system permission to use the --hard-delete flag. This permission is disabled by default and can be enabled only by a system administrator. diff --git a/messages/bulkv2.delete.md b/messages/bulkv2.delete.md index 03ef9619..3c6c0326 100644 --- a/messages/bulkv2.delete.md +++ b/messages/bulkv2.delete.md @@ -17,3 +17,11 @@ When you execute this command, it starts a job, displays the ID, and then immedi - Bulk delete records from a custom object in an org with alias my-scratch and wait 5 minutes for the command to complete: <%= config.bin %> <%= command.id %> --sobject MyObject__c --file files/delete.csv --wait 5 --target-org my-scratch + +# flags.hard-delete.summary + +Mark the records as immediately eligible for deletion by your org. If you don't specify this flag, the deleted records go into the Recycle Bin. + +# flags.hard-delete.description + +You must have the "Bulk API Hard Delete" system permission to use this flag. The permission is disabled by default and can be enabled only by a system administrator. diff --git a/src/BulkBaseCommand.ts b/src/BulkBaseCommand.ts index c3502ad2..31f821c8 100644 --- a/src/BulkBaseCommand.ts +++ b/src/BulkBaseCommand.ts @@ -6,11 +6,11 @@ */ import { SfCommand, Spinner } from '@salesforce/sf-plugins-core'; -import { IngestJobV2, JobInfoV2 } from '@jsforce/jsforce-node/lib/api/bulk2.js'; +import type { IngestJobV2, JobInfoV2 } from '@jsforce/jsforce-node/lib/api/bulk2.js'; import { Duration } from '@salesforce/kit'; import { capitalCase } from 'change-case'; import { Messages } from '@salesforce/core'; -import { Schema } from '@jsforce/jsforce-node'; +import type { Schema } from '@jsforce/jsforce-node'; import { getResultMessage } from './reporters/reporters.js'; import { BulkDataRequestCache } from './bulkDataRequestCache.js'; @@ -70,9 +70,11 @@ export const displayBulkV2Result = ({ cmd: SfCommand; username?: string; }): void => { + // if we just read from jobInfo.operation it may suggest running the nonexistent `sf data hardDelete resume` command + const operation = jobInfo.operation === 'hardDelete' || jobInfo.operation === 'delete' ? 'delete' : jobInfo.operation; if (isAsync && jobInfo.state !== 'JobComplete' && jobInfo.state !== 'Failed') { - cmd.logSuccess(messages.getMessage('success', [jobInfo.operation, jobInfo.id])); - cmd.info(messages.getMessage('checkStatus', [jobInfo.operation, jobInfo.id, username])); + cmd.logSuccess(messages.getMessage('success', [operation, jobInfo.id])); + cmd.info(messages.getMessage('checkStatus', [operation, jobInfo.id, username])); } else { cmd.log(); cmd.info(getResultMessage(jobInfo)); @@ -81,7 +83,7 @@ export const displayBulkV2Result = ({ process.exitCode = 1; } if (jobInfo.state === 'InProgress' || jobInfo.state === 'Open') { - cmd.info(messages.getMessage('checkStatus', [jobInfo.operation, jobInfo.id, username])); + cmd.info(messages.getMessage('checkStatus', [operation, jobInfo.id, username])); } if (jobInfo.state === 'Failed') { throw messages.createError('bulkJobFailed', [jobInfo.id]).setData(jobInfo); diff --git a/src/api/data/tree/exportApi.ts b/src/api/data/tree/exportApi.ts index 08ff6948..b145dc15 100644 --- a/src/api/data/tree/exportApi.ts +++ b/src/api/data/tree/exportApi.ts @@ -9,7 +9,7 @@ import path from 'node:path'; import fs from 'node:fs'; import { Logger, Messages, Org, SfError, Lifecycle } from '@salesforce/core'; -import { DescribeSObjectResult, QueryResult } from '@jsforce/jsforce-node'; +import type { DescribeSObjectResult, QueryResult } from '@jsforce/jsforce-node'; import { Ux } from '@salesforce/sf-plugins-core'; import { BasicRecord, diff --git a/src/api/file/fileToContentVersion.ts b/src/api/file/fileToContentVersion.ts index 87d83618..2b403f80 100644 --- a/src/api/file/fileToContentVersion.ts +++ b/src/api/file/fileToContentVersion.ts @@ -8,7 +8,7 @@ import { readFile } from 'node:fs/promises'; import { basename } from 'node:path'; import { Connection } from '@salesforce/core'; -import { Record, SaveResult } from '@jsforce/jsforce-node'; +import type { Record, SaveResult } from '@jsforce/jsforce-node'; import FormData from 'form-data'; export type ContentVersion = { diff --git a/src/batcher.ts b/src/batcher.ts index d25c9494..6c561248 100644 --- a/src/batcher.ts +++ b/src/batcher.ts @@ -8,10 +8,10 @@ import { ReadStream } from 'node:fs'; import { Connection, Messages, SfError } from '@salesforce/core'; import { Ux } from '@salesforce/sf-plugins-core'; -import { Schema } from '@jsforce/jsforce-node'; +import type { Schema } from '@jsforce/jsforce-node'; import { stringify } from 'csv-stringify/sync'; import { parse } from 'csv-parse'; -import { +import type { Batch, BatchInfo, BulkIngestBatchResult, diff --git a/src/bulkOperationCommand.ts b/src/bulkOperationCommand.ts index 7a6e8e0c..cc7bd4e8 100644 --- a/src/bulkOperationCommand.ts +++ b/src/bulkOperationCommand.ts @@ -7,12 +7,11 @@ import fs from 'node:fs'; import { ReadStream } from 'node:fs'; import os from 'node:os'; - import { Flags, SfCommand } from '@salesforce/sf-plugins-core'; import { Duration } from '@salesforce/kit'; import { Connection, Messages } from '@salesforce/core'; import { Ux } from '@salesforce/sf-plugins-core/Ux'; -import { Schema } from '@jsforce/jsforce-node'; +import type { Schema } from '@jsforce/jsforce-node'; import { BulkV2, IngestJobV2, @@ -77,7 +76,7 @@ export const baseFlags = { }), }; -type SupportedOperations = Extract; +type SupportedOperations = Extract; export const runBulkOperation = async ({ sobject, @@ -147,6 +146,10 @@ export const runBulkOperation = async ({ } return result; } catch (err) { + if (err instanceof Error && err.name === 'FEATURENOTENABLED' && operation === 'hardDelete') { + // add info specific to hardDelete permission + err.message = messages.getMessage('hard-delete-permission-error'); + } cmd.spinner.stop(); throw err; } @@ -156,6 +159,7 @@ export const runBulkOperation = async ({ }; const getCache = async (operation: SupportedOperations): Promise => { switch (operation) { + case 'hardDelete': case 'delete': return BulkDeleteRequestCache.create(); case 'upsert': @@ -168,8 +172,7 @@ const getCache = async (operation: SupportedOperations): Promise( job: IngestJobV2, diff --git a/src/bulkUtils.ts b/src/bulkUtils.ts index 068e7fab..792572bf 100644 --- a/src/bulkUtils.ts +++ b/src/bulkUtils.ts @@ -13,7 +13,7 @@ import type { IngestJobV2UnprocessedRecords, } from '@jsforce/jsforce-node/lib/api/bulk2.js'; -import { Schema } from '@jsforce/jsforce-node'; +import type { Schema } from '@jsforce/jsforce-node'; import { Connection, Messages } from '@salesforce/core'; import { BulkProcessedRecordV2, BulkRecordsV2 } from './types.js'; diff --git a/src/commands/data/create/record.ts b/src/commands/data/create/record.ts index 9a51df52..e205bc84 100644 --- a/src/commands/data/create/record.ts +++ b/src/commands/data/create/record.ts @@ -6,7 +6,7 @@ */ import { Messages } from '@salesforce/core'; -import { SaveResult } from '@jsforce/jsforce-node'; +import type { SaveResult } from '@jsforce/jsforce-node'; import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; import { orgFlags, perflogFlag } from '../../../flags.js'; import { stringToDictionary, collectErrorMessages } from '../../../dataCommand.js'; diff --git a/src/commands/data/delete/bulk.ts b/src/commands/data/delete/bulk.ts index 73236cde..139224cd 100644 --- a/src/commands/data/delete/bulk.ts +++ b/src/commands/data/delete/bulk.ts @@ -7,7 +7,7 @@ import { Messages } from '@salesforce/core'; import { Duration } from '@salesforce/kit'; -import { SfCommand } from '@salesforce/sf-plugins-core'; +import { Flags, SfCommand } from '@salesforce/sf-plugins-core'; import { baseFlags, runBulkOperation } from '../../../bulkOperationCommand.js'; import { BulkResultV2 } from '../../../types.js'; @@ -19,7 +19,14 @@ export default class Delete extends SfCommand { public static readonly summary = messages.getMessage('summary'); public static readonly description = messages.getMessage('description'); - public static readonly flags = baseFlags; + public static readonly flags = { + ...baseFlags, + 'hard-delete': Flags.boolean({ + summary: messages.getMessage('flags.hard-delete.summary'), + description: messages.getMessage('flags.hard-delete.description'), + default: false, + }), + }; public async run(): Promise { const { flags } = await this.parse(Delete); @@ -30,7 +37,7 @@ export default class Delete extends SfCommand { connection: flags['target-org'].getConnection(flags['api-version']), wait: flags.async ? Duration.minutes(0) : flags.wait, verbose: flags.verbose, - operation: 'delete', + operation: flags['hard-delete'] ? 'hardDelete' : 'delete', }); } } diff --git a/src/commands/data/delete/record.ts b/src/commands/data/delete/record.ts index e9610d15..cd4b131a 100644 --- a/src/commands/data/delete/record.ts +++ b/src/commands/data/delete/record.ts @@ -6,7 +6,7 @@ */ import { Messages, SfError } from '@salesforce/core'; -import { SaveResult } from '@jsforce/jsforce-node'; +import type { SaveResult } from '@jsforce/jsforce-node'; import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; import { orgFlags, perflogFlag } from '../../../flags.js'; import { collectErrorMessages, query } from '../../../dataCommand.js'; diff --git a/src/commands/data/get/record.ts b/src/commands/data/get/record.ts index 5f806170..0ed6157c 100644 --- a/src/commands/data/get/record.ts +++ b/src/commands/data/get/record.ts @@ -6,7 +6,7 @@ */ import { Messages, SfError } from '@salesforce/core'; -import { Record } from '@jsforce/jsforce-node'; +import type { Record } from '@jsforce/jsforce-node'; import { toAnyJson } from '@salesforce/ts-types'; import { SfCommand, Flags, Ux } from '@salesforce/sf-plugins-core'; import { orgFlags, perflogFlag } from '../../../flags.js'; diff --git a/src/commands/data/query.ts b/src/commands/data/query.ts index 9bb45679..ed096474 100644 --- a/src/commands/data/query.ts +++ b/src/commands/data/query.ts @@ -8,7 +8,7 @@ import fs from 'node:fs'; import { Connection, Logger, Messages, SfError } from '@salesforce/core'; -import { Record as jsforceRecord } from '@jsforce/jsforce-node'; +import type { Record as jsforceRecord } from '@jsforce/jsforce-node'; import { AnyJson, ensureJsonArray, diff --git a/src/commands/data/query/resume.ts b/src/commands/data/query/resume.ts index b1eb4bb7..2d0bbcfe 100644 --- a/src/commands/data/query/resume.ts +++ b/src/commands/data/query/resume.ts @@ -7,7 +7,7 @@ import { Messages } from '@salesforce/core'; import { QueryJobV2 } from '@jsforce/jsforce-node/lib/api/bulk2.js'; -import { Record as jsforceRecord } from '@jsforce/jsforce-node'; +import type { Record as jsforceRecord } from '@jsforce/jsforce-node'; import { Flags, loglevel, diff --git a/src/commands/data/update/record.ts b/src/commands/data/update/record.ts index 41d0637a..c78d041a 100644 --- a/src/commands/data/update/record.ts +++ b/src/commands/data/update/record.ts @@ -6,7 +6,7 @@ */ import { Messages, SfError } from '@salesforce/core'; -import { SaveError, SaveResult } from '@jsforce/jsforce-node'; +import type { SaveError, SaveResult } from '@jsforce/jsforce-node'; import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; import { orgFlags } from '../../../flags.js'; import { collectErrorMessages, query, stringToDictionary } from '../../../dataCommand.js'; diff --git a/src/dataCommand.ts b/src/dataCommand.ts index 26b4faa7..45a9356f 100644 --- a/src/dataCommand.ts +++ b/src/dataCommand.ts @@ -6,7 +6,7 @@ */ import { Connection, Messages, SfError } from '@salesforce/core'; -import { Record as jsforceRecord, SaveResult } from '@jsforce/jsforce-node'; +import type { Record as jsforceRecord, SaveResult } from '@jsforce/jsforce-node'; import { Ux } from '@salesforce/sf-plugins-core'; import { GenericObject } from './types.js'; diff --git a/src/dataSoqlQueryTypes.ts b/src/dataSoqlQueryTypes.ts index eedb02e3..70dc9fc7 100644 --- a/src/dataSoqlQueryTypes.ts +++ b/src/dataSoqlQueryTypes.ts @@ -5,7 +5,7 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ -import { QueryResult, Record } from '@jsforce/jsforce-node'; +import type { QueryResult, Record } from '@jsforce/jsforce-node'; import { Optional } from '@salesforce/ts-types'; import { GenericEntry } from './types.js'; diff --git a/src/queryUtils.ts b/src/queryUtils.ts index acce8e6a..c9683318 100644 --- a/src/queryUtils.ts +++ b/src/queryUtils.ts @@ -4,7 +4,7 @@ * Licensed under the BSD 3-Clause license. * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ -import { Record } from '@jsforce/jsforce-node'; +import type { Record } from '@jsforce/jsforce-node'; import { Field, FieldType, SoqlQueryResult } from './dataSoqlQueryTypes.js'; import { FormatTypes, JsonReporter } from './reporters/reporters.js'; import { CsvReporter } from './reporters/csvReporter.js'; diff --git a/src/reporters/csvReporter.ts b/src/reporters/csvReporter.ts index ecfa5044..a6d50622 100644 --- a/src/reporters/csvReporter.ts +++ b/src/reporters/csvReporter.ts @@ -7,7 +7,7 @@ import { EOL } from 'node:os'; import { Ux } from '@salesforce/sf-plugins-core'; import { get, getNumber, isString } from '@salesforce/ts-types'; -import { Record as jsforceRecord } from '@jsforce/jsforce-node'; +import type { Record as jsforceRecord } from '@jsforce/jsforce-node'; import { Field, SoqlQueryResult } from '../dataSoqlQueryTypes.js'; import { getAggregateAliasOrName, maybeMassageAggregates } from './reporters.js'; import { QueryReporter, logFields, isSubquery, isAggregate } from './reporters.js'; diff --git a/src/reporters/humanReporter.ts b/src/reporters/humanReporter.ts index f0f46db2..4ca20b50 100644 --- a/src/reporters/humanReporter.ts +++ b/src/reporters/humanReporter.ts @@ -8,7 +8,7 @@ import { Ux } from '@salesforce/sf-plugins-core'; import ansis from 'ansis'; import { get, getArray, isPlainObject, isString, Optional } from '@salesforce/ts-types'; import { Messages } from '@salesforce/core'; -import { Record as jsforceRecord } from '@jsforce/jsforce-node'; +import type { Record as jsforceRecord } from '@jsforce/jsforce-node'; import { GenericEntry, GenericObject } from '../types.js'; import { Field, FieldType, SoqlQueryResult } from '../dataSoqlQueryTypes.js'; import { QueryReporter, logFields, isSubquery, isAggregate, getAggregateAliasOrName } from './reporters.js'; diff --git a/test/api/data/tree/export.test.ts b/test/api/data/tree/export.test.ts index 41e237ee..890055d0 100644 --- a/test/api/data/tree/export.test.ts +++ b/test/api/data/tree/export.test.ts @@ -6,7 +6,7 @@ */ import { expect, config } from 'chai'; -import { DescribeSObjectResult } from '@jsforce/jsforce-node'; +import type { DescribeSObjectResult } from '@jsforce/jsforce-node'; import { RefFromIdByType, buildRefMap, diff --git a/test/commands/data/create/file.nut.ts b/test/commands/data/create/file.nut.ts index 2dedb268..a30a5f5b 100644 --- a/test/commands/data/create/file.nut.ts +++ b/test/commands/data/create/file.nut.ts @@ -8,7 +8,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { execCmd, TestSession } from '@salesforce/cli-plugins-testkit'; import { expect } from 'chai'; -import { SaveResult } from '@jsforce/jsforce-node'; +import type { SaveResult } from '@jsforce/jsforce-node'; import { SoqlQueryResult } from '../../../../src/dataSoqlQueryTypes.js'; import { ContentVersion } from '../../../../src/api/file/fileToContentVersion.js'; diff --git a/test/commands/data/create/record.test.ts b/test/commands/data/create/record.test.ts index 983fd4a9..5970b738 100644 --- a/test/commands/data/create/record.test.ts +++ b/test/commands/data/create/record.test.ts @@ -11,7 +11,7 @@ import { Config } from '@oclif/core/config'; import { expect } from 'chai'; import { TestContext, MockTestOrgData } from '@salesforce/core/testSetup'; import { AnyJson, ensureJsonMap, ensureString } from '@salesforce/ts-types'; -import { SaveResult } from '@jsforce/jsforce-node'; +import type { SaveResult } from '@jsforce/jsforce-node'; import Create from '../../../../src/commands/data/create/record.js'; const sObjectId = '0011100001zhhyUAAQ'; diff --git a/test/commands/data/dataBulk.nut.ts b/test/commands/data/dataBulk.nut.ts index ba9c87e1..72711a8e 100644 --- a/test/commands/data/dataBulk.nut.ts +++ b/test/commands/data/dataBulk.nut.ts @@ -12,7 +12,7 @@ import { expect, config as chaiConfig } from 'chai'; import { execCmd, TestSession } from '@salesforce/cli-plugins-testkit'; import { sleep } from '@salesforce/kit'; import { ensurePlainObject } from '@salesforce/ts-types'; -import { SaveResult } from '@jsforce/jsforce-node'; +import type { SaveResult } from '@jsforce/jsforce-node'; import { BulkResultV2 } from '../../../src/types.js'; import { QueryResult } from './dataSoqlQuery.nut.js'; diff --git a/test/commands/data/delete/bulk.test.ts b/test/commands/data/delete/bulk.test.ts new file mode 100644 index 00000000..e2c9f0e5 --- /dev/null +++ b/test/commands/data/delete/bulk.test.ts @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2020, salesforce.com, inc. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ + +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import fs from 'node:fs'; +import { TestContext, MockTestOrgData, shouldThrow } from '@salesforce/core/testSetup'; +import { Config } from '@oclif/core/config'; +import { assert, expect } from 'chai'; +import { IngestJobV2, JobInfoV2 } from '@jsforce/jsforce-node/lib/api/bulk2.js'; +import Bulk from '../../../../src/commands/data/delete/bulk.js'; + +describe('data:delete:bulk', () => { + const $$ = new TestContext(); + const testOrg = new MockTestOrgData(); + let config: Config; + + before(async () => { + config = new Config({ root: resolve(dirname(fileURLToPath(import.meta.url)), '../../../..') }); + await config.load(); + }); + + beforeEach(async () => { + await $$.stubAuths(testOrg); + $$.SANDBOX.stub(fs, 'existsSync').returns(true); + $$.SANDBOX.stub(fs, 'createReadStream').resolves(); + // @ts-expect-error only stubbing a very small part + $$.SANDBOX.stub(fs.promises, 'stat').resolves({ isFile: () => true, isDirectory: () => true }); + $$.SANDBOX.stub(IngestJobV2.prototype, 'open').resolves(); + $$.SANDBOX.stub(IngestJobV2.prototype, 'uploadData').resolves(); + $$.SANDBOX.stub(IngestJobV2.prototype, 'close').resolves(); + $$.SANDBOX.stub(IngestJobV2.prototype, 'poll').resolves(); + }); + + afterEach(async () => { + $$.SANDBOX.restore(); + }); + + it('should pass the hardDelete option to the api', async () => { + const options: JobInfoV2 = { + operation: 'hardDelete', + id: '123', + object: 'Account', + apiActiveProcessingTime: 0, + assignmentRuleId: '90', + contentUrl: '389', + errorMessage: undefined, + externalIdFieldName: '123', + jobType: 'V2Ingest', + state: 'JobComplete', + apiVersion: 44.0, + concurrencyMode: 'Parallel', + retries: 0, + totalProcessingTime: 1, + apexProcessingTime: 1, + columnDelimiter: 'BACKQUOTE', + numberRecordsProcessed: 10, + contentType: 'CSV', + numberRecordsFailed: 0, + createdById: '234', + createdDate: '', + systemModstamp: '', + lineEnding: 'LF', + }; + + // we can't spy on ESM modules... :( + $$.SANDBOX.stub(IngestJobV2.prototype, 'check').resolves(options); + + const result = await Bulk.run([ + '--target-org', + 'test@org.com', + '--hard-delete', + '--file', + '../../oss/plugin-data/test/test-files/data-project/data/bulkUpsertLarge.csv', + '--sobject', + 'Account', + ]); + expect(result).to.deep.equal({ + jobInfo: options, + }); + }); + + it('should handle user without permission error', async () => { + const e = new Error('FEATURENOTENABLED'); + e.name = 'FEATURENOTENABLED'; + $$.SANDBOX.stub(IngestJobV2.prototype, 'check').throws(e); + + const bulk = new Bulk( + [ + '--target-org', + 'test@org.com', + '--hard-delete', + '--file', + '../../oss/plugin-data/test/test-files/data-project/data/bulkUpsertLarge.csv', + '--sobject', + 'Account', + ], + config + ); + try { + await shouldThrow(bulk.run()); + } catch (err) { + assert(err instanceof Error); + expect(err.message).to.equal( + 'You must have the "Bulk API Hard Delete" system permission to use the --hard-delete flag. This permission is disabled by default and can be enabled only by a system administrator.' + ); + } + }); + it('should not change error when not using --hard-delete', async () => { + const e = new Error('some other server-side error, but not permissions'); + $$.SANDBOX.stub(IngestJobV2.prototype, 'check').throws(e); + + const bulk = new Bulk( + [ + '--target-org', + 'test@org.com', + '--file', + '../../oss/plugin-data/test/test-files/data-project/data/bulkUpsertLarge.csv', + '--sobject', + 'Account', + ], + config + ); + try { + await shouldThrow(bulk.run()); + } catch (err) { + assert(err instanceof Error); + expect(err.message).to.equal('some other server-side error, but not permissions'); + } + }); + + it('should succeed', async () => { + const options: JobInfoV2 = { + operation: 'delete', + id: '123', + object: 'Account', + apiActiveProcessingTime: 0, + assignmentRuleId: '90', + contentUrl: '389', + errorMessage: undefined, + externalIdFieldName: '123', + jobType: 'V2Ingest', + state: 'JobComplete', + apiVersion: 44.0, + concurrencyMode: 'Parallel', + retries: 0, + totalProcessingTime: 1, + apexProcessingTime: 1, + columnDelimiter: 'BACKQUOTE', + numberRecordsProcessed: 10, + contentType: 'CSV', + numberRecordsFailed: 0, + createdById: '234', + createdDate: '', + systemModstamp: '', + lineEnding: 'LF', + }; + $$.SANDBOX.stub(IngestJobV2.prototype, 'check').resolves(options); + + const result = await Bulk.run([ + '--target-org', + 'test@org.com', + '--file', + '../../oss/plugin-data/test/test-files/data-project/data/bulkUpsertLarge.csv', + '--sobject', + 'Account', + ]); + expect(result).to.deep.equal({ + jobInfo: options, + }); + }); +}); diff --git a/test/commands/data/delete/record.test.ts b/test/commands/data/delete/record.test.ts index be4c8b2c..608f6389 100644 --- a/test/commands/data/delete/record.test.ts +++ b/test/commands/data/delete/record.test.ts @@ -13,7 +13,7 @@ import { ensureJsonMap, ensureString, AnyJson } from '@salesforce/ts-types'; import { expect } from 'chai'; import { Config } from '@oclif/core/config'; -import { SaveResult } from '@jsforce/jsforce-node'; +import type { SaveResult } from '@jsforce/jsforce-node'; import Delete from '../../../../src/commands/data/delete/record.js'; const sObjectId = '0011100001zhhyUAAQ'; diff --git a/test/commands/data/query/resume.nut.ts b/test/commands/data/query/resume.nut.ts index 0bc4a0b5..c57e86c5 100644 --- a/test/commands/data/query/resume.nut.ts +++ b/test/commands/data/query/resume.nut.ts @@ -7,7 +7,7 @@ import path from 'node:path'; import { expect, config } from 'chai'; import { execCmd, TestSession } from '@salesforce/cli-plugins-testkit'; -import { QueryResult, Record } from '@jsforce/jsforce-node'; +import type { QueryResult, Record } from '@jsforce/jsforce-node'; import { sleep } from '@salesforce/kit'; import { JobInfoV2 } from '@jsforce/jsforce-node/lib/api/bulk2.js'; config.truncateThreshold = 0; diff --git a/test/commands/data/record/dataRecord.nut.ts b/test/commands/data/record/dataRecord.nut.ts index 0f0ed868..84b5e41a 100644 --- a/test/commands/data/record/dataRecord.nut.ts +++ b/test/commands/data/record/dataRecord.nut.ts @@ -9,7 +9,7 @@ import { strict as assert } from 'node:assert/strict'; import { expect } from 'chai'; import { execCmd, genUniqueString, TestSession } from '@salesforce/cli-plugins-testkit'; import { Dictionary } from '@salesforce/ts-types'; -import { SaveResult } from '@jsforce/jsforce-node'; +import type { SaveResult } from '@jsforce/jsforce-node'; type AccountRecord = { Id: string; diff --git a/test/commands/data/update/record.test.ts b/test/commands/data/update/record.test.ts index 28068287..af6d7caf 100644 --- a/test/commands/data/update/record.test.ts +++ b/test/commands/data/update/record.test.ts @@ -13,7 +13,7 @@ import { expect } from 'chai'; import { TestContext, MockTestOrgData, shouldThrow } from '@salesforce/core/testSetup'; import { Config } from '@oclif/core/config'; import { SfError } from '@salesforce/core/sfError'; -import { SaveResult } from '@jsforce/jsforce-node'; +import type { SaveResult } from '@jsforce/jsforce-node'; import Update from '../../../../src/commands/data/update/record.js'; const sObjectId = '0011100001zhhyUAAQ'; diff --git a/test/soqlQuery.test.ts b/test/soqlQuery.test.ts deleted file mode 100644 index da986873..00000000 --- a/test/soqlQuery.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -/* - * Copyright (c) 2023, salesforce.com, inc. - * All rights reserved. - * Licensed under the BSD 3-Clause license. - * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause - */ diff --git a/test/testUtil.ts b/test/testUtil.ts index e00feee2..32f1b23c 100644 --- a/test/testUtil.ts +++ b/test/testUtil.ts @@ -6,7 +6,7 @@ */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { QueryResult, SaveResult, UpsertResult, UserInfo } from '@jsforce/jsforce-node'; +import type { QueryResult, SaveResult, UpsertResult, UserInfo } from '@jsforce/jsforce-node'; import { Connection } from '@salesforce/core'; import EventEmitter = NodeJS.EventEmitter;