Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,4 @@ All rights reserved.
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
limitations under the License.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# plugin-data-setup-transfer

[![NPM](https://img.shields.io/npm/v/plugin-data-setup-transfer.svg?label=plugin-data-setup-transfer)](https://www.npmjs.com/package/plugin-data-setup-transfer) [![Downloads/week](https://img.shields.io/npm/dw/plugin-data-setup-transfer.svg)](https://npmjs.org/package/plugin-data-setup-transfer) [![License](https://img.shields.io/badge/License-BSD%203--Clause-brightgreen.svg)](https://raw.githubusercontent.com/salesforcecli/plugin-data-setup-transfer/main/LICENSE.txt)
[![NPM](https://img.shields.io/npm/v/plugin-data-setup-transfer.svg?label=plugin-data-setup-transfer)](https://www.npmjs.com/package/plugin-data-setup-transfer) [![Downloads/week](https://img.shields.io/npm/dw/plugin-data-setup-transfer.svg)](https://npmjs.org/package/plugin-data-setup-transfer) [![License](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](https://opensource.org/license/apache-2-0)


This plugin is bundled with the [Salesforce CLI](https://developer.salesforce.com/tools/sfdxcli). For more information on the CLI, read the [getting started guide](https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_intro.htm).
Expand Down
20 changes: 20 additions & 0 deletions command-snapshot.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[
{
"alias": [],
"command": "data:setup:transfer",
"flagAliases": [],
"flagChars": ["e", "i", "o", "s", "v", "x"],
"flags": [
"api-version",
"definition-identifier",
"extended-definition-file",
"filter-value",
"flags-dir",
"json",
"source-org",
"target-org",
"version"
],
"plugin": "plugin-data-setup-transfer"
}
]
31 changes: 17 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,10 @@
"@oclif/plugin-command-snapshot": "^5.3.22",
"@salesforce/cli-plugins-testkit": "^5.3.58",
"@salesforce/dev-scripts": "^11.0.4",
"@types/mocha": "^10.0.10",
"@types/node": "^25.9.1",
"eslint-plugin-sf-plugin": "^1.20.33",
"husky": "^9.1.7",
"oclif": "^4.23.8",
"ts-node": "^10.9.2",
"typescript": "^6.0.3",
"wireit": "^0.14.12"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iowillhoit wireit is removed here, but still referenced in other places in package.json. So shall we remove all references of wireit?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of dev-deps are automatically pulled in via dev-scripts, wireit is one of them

"typescript": "^5.9.3"
},
"engines": {
"node": ">=18.0.0"
Expand All @@ -38,17 +34,24 @@
"sfdx",
"sfdx-plugin"
],
"license": "BSD-3-Clause",
"license": "Apache-2.0",
"oclif": {
"commands": "./lib/commands",
"bin": "sf",
"topicSeparator": " ",
"devPlugins": [
"@oclif/plugin-help"
"@oclif/plugin-help",
"@oclif/plugin-command-snapshot"
],
"topics": {
"hello": {
"description": "Commands to say hello."
"data": {
"description": "Manage records in your org.",
"subtopics": {
"setup": {
"description": "Retrieve setup data from one org and deploy it to another."
}
},
"external": true
}
},
"flexibleTaxonomy": true
Expand All @@ -59,14 +62,15 @@
"clean-all": "sf-clean all",
"compile": "wireit",
"docs": "sf-docs",
"fix-license": "eslint src test --fix --rule \"header/header: [2]\"",
"format": "wireit",
"link-check": "wireit",
"lint": "wireit",
"postinstall": "yarn husky install",
"postpack": "sf-clean --ignore-signing-artifacts",
"prepack": "sf-prepack",
"prepare": "sf-install",
"test": "wireit",
"test:nuts": "nyc mocha \"**/*.nut.ts\" --slow 4500 --timeout 600000 --parallel",
"test:nuts": "echo 'No NUT tests'",
"test:only": "wireit",
"version": "oclif readme"
},
Expand Down Expand Up @@ -133,8 +137,7 @@
"test:only": {
"command": "nyc mocha \"test/**/*.test.ts\"",
"env": {
"FORCE_COLOR": "2",
"TS_NODE_PROJECT": "test/tsconfig.json"
"FORCE_COLOR": "2"
},
"files": [
"test/**/*.ts",
Expand Down Expand Up @@ -176,7 +179,7 @@
"output": []
},
"link-check": {
"command": "node -e \"process.exit(process.env.CI ? 0 : 1)\" || linkinator \"**/*.md\" --skip \"CHANGELOG.md|node_modules|test/|confluence.internal.salesforce.com|my.salesforce.com|%s\" --markdown --retry --directory-listing --verbosity error",
"command": "node -e \"process.exit(process.env.CI ? 0 : 1)\" || linkinator \"**/*.md\" --skip \"CHANGELOG.md|node_modules|test/|confluence.internal.salesforce.com|my.salesforce.com|localhost|%s\" --markdown --retry --directory-listing --verbosity error",
"files": [
"./*.md",
"./!(CHANGELOG).md",
Expand Down
18 changes: 18 additions & 0 deletions schemas/data-setup-transfer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/SetupTransferResult",
"definitions": {
"SetupTransferResult": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
},
"exportResponse": {},
"importResponse": {}
},
"required": ["success", "exportResponse", "importResponse"],
"additionalProperties": false
}
}
}
32 changes: 6 additions & 26 deletions src/commands/data/setup/transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,39 +269,19 @@ export default class SetupTransfer extends SfCommand<SetupTransferResult> {
this.spinner.status = messages.getMessage('info.callingExportApi');
const sourceApiVersion = sourceConnection.version;
const exportApiPath = `/services/data/v${sourceApiVersion}/connect/industries/setup/dataset/actions/export`;
const instanceUrl = sourceConnection.instanceUrl ?? '';
const fullUrl = `${instanceUrl}${exportApiPath}`;

const exportAbort = new AbortController();
const exportTimeoutId = setTimeout(() => exportAbort.abort(), SetupTransfer.HTTP_TIMEOUT_MS);
let httpResponse: Response;
try {
httpResponse = await fetch(fullUrl, {
const exportResponse = await sourceConnection.request<ExportResponse>(
{
method: 'POST',
url: exportApiPath,
body: JSON.stringify(exportPayload),
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${sourceConnection.accessToken ?? ''}`,
},
body: JSON.stringify(exportPayload),
signal: exportAbort.signal,
});
} finally {
clearTimeout(exportTimeoutId);
}

const rawBody = await httpResponse.text();

if (!httpResponse.ok) {
throw new Error(`Export API returned ${httpResponse.status}: ${rawBody}`);
}

// eslint-disable-next-line no-control-regex
const sanitizedBody = rawBody.replace(/[\x00-\x1F\x7F]/g, (ch) =>
ch === '\n' || ch === '\r' || ch === '\t' ? ch : ''
},
{ timeout: SetupTransfer.HTTP_TIMEOUT_MS }
);

const exportResponse = JSON.parse(sanitizedBody) as ExportResponse;

const errors = exportResponse.errors as Array<{ message?: string }> | undefined;
if (exportResponse.isSuccess === false) {
const errorDetails = errors?.map((e) => e.message ?? JSON.stringify(e)).join('\n') ?? 'Unknown error';
Expand Down
57 changes: 21 additions & 36 deletions test/commands/data/setup/transfer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,12 @@ describe('data setup transfer', () => {
sfCommandStubs = stubSfCommandUx($$.SANDBOX);
await $$.stubAuths(mockSourceOrg, mockTargetOrg);

// Stub fetch for export API
$$.SANDBOX.stub(global, 'fetch').resolves({
ok: true,
status: 200,
text: async () => JSON.stringify(mockExportResponse),
} as Response);

// Stub connection.request for import API
const targetConnection = await mockTargetOrg.getConnection();
targetConnection.request = $$.SANDBOX.stub().resolves(mockImportResponse);
$$.fakeConnectionRequest = (request) => {
if (typeof request === 'string' || (request as { url: string }).url.includes('/export')) {
return Promise.resolve(mockExportResponse);
}
return Promise.resolve(mockImportResponse);
};
});

afterEach(() => {
Expand Down Expand Up @@ -171,21 +167,15 @@ describe('data setup transfer', () => {

describe('error handling', () => {
it('throws error when export API returns error', async () => {
$$.SANDBOX.restore();
await $$.stubAuths(mockSourceOrg, mockTargetOrg);

$$.SANDBOX.stub(global, 'fetch').resolves({
ok: true,
status: 200,
text: async () =>
JSON.stringify({
$$.fakeConnectionRequest = (request) => {
if (typeof request === 'string' || (request as { url: string }).url.includes('/export')) {
return Promise.resolve({
isSuccess: false,
errors: [{ message: 'Invalid definition identifier' }],
}),
} as Response);

const targetConnection = await mockTargetOrg.getConnection();
targetConnection.request = $$.SANDBOX.stub().resolves(mockImportResponse);
});
}
return Promise.resolve(mockImportResponse);
};

try {
await SetupTransfer.run([
Expand All @@ -205,18 +195,13 @@ describe('data setup transfer', () => {
}
});

it('throws error when export API returns non-200 status', async () => {
$$.SANDBOX.restore();
await $$.stubAuths(mockSourceOrg, mockTargetOrg);

$$.SANDBOX.stub(global, 'fetch').resolves({
ok: false,
status: 400,
text: async () => 'Bad Request',
} as Response);

const targetConnection = await mockTargetOrg.getConnection();
targetConnection.request = $$.SANDBOX.stub().resolves(mockImportResponse);
it('throws error when export API request fails', async () => {
$$.fakeConnectionRequest = (request) => {
if (typeof request === 'string' || (request as { url: string }).url.includes('/export')) {
return Promise.reject(new Error('REQUEST_FAILED: Bad Request'));
}
return Promise.resolve(mockImportResponse);
};

try {
await SetupTransfer.run([
Expand All @@ -231,7 +216,7 @@ describe('data setup transfer', () => {
]);
expect.fail('Should have thrown an error');
} catch (error) {
expect((error as Error).message).to.include('Export API returned 400');
expect((error as Error).message).to.include('REQUEST_FAILED');
}
});
});
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"extends": "@salesforce/dev-config/tsconfig-strict-esm",
"compilerOptions": {
"outDir": "lib",
"rootDir": "src"
"rootDir": "src",
"skipLibCheck": true
},
"include": ["./src/**/*.ts"]
}
Loading
Loading