@salesforce/packaging 1.4.16 → 1.4.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/interfaces/packagingInterfacesAndType.d.ts +37 -33
- package/lib/package/package.d.ts +3 -3
- package/lib/package/package.js +14 -8
- package/lib/package/packageAncestry.d.ts +24 -25
- package/lib/package/packageAncestry.js +159 -147
- package/lib/package/packageConvert.d.ts +1 -1
- package/lib/package/packageConvert.js +15 -21
- package/lib/package/packageCreate.d.ts +1 -1
- package/lib/package/packageCreate.js +14 -25
- package/lib/package/packageDelete.js +4 -1
- package/lib/package/packageInstall.d.ts +5 -4
- package/lib/package/packageInstall.js +10 -9
- package/lib/package/packageProfileApi.d.ts +3 -6
- package/lib/package/packageProfileApi.js +153 -145
- package/lib/package/packageUninstall.js +15 -9
- package/lib/package/packageVersion.d.ts +6 -5
- package/lib/package/packageVersion.js +76 -46
- package/lib/package/packageVersionCreate.d.ts +2 -2
- package/lib/package/packageVersionCreate.js +81 -59
- package/lib/package/packageVersionCreateRequest.d.ts +2 -2
- package/lib/package/packageVersionCreateRequest.js +8 -11
- package/lib/package/packageVersionCreateRequestReport.js +4 -1
- package/lib/package/packageVersionList.d.ts +5 -4
- package/lib/package/packageVersionList.js +18 -18
- package/lib/package/packageVersionReport.js +6 -4
- package/lib/package/subscriberPackageVersion.d.ts +4 -4
- package/lib/package/subscriberPackageVersion.js +23 -10
- package/lib/package/versionNumber.d.ts +8 -3
- package/lib/package/versionNumber.js +13 -4
- package/lib/package1/package1Version.js +18 -13
- package/lib/utils/packageUtils.d.ts +11 -7
- package/lib/utils/packageUtils.js +25 -12
- package/messages/package.md +4 -0
- package/messages/package1Version.md +4 -0
- package/messages/package_version.md +8 -0
- package/messages/package_version_create.md +32 -4
- package/messages/pkg_utils.md +1 -1
- package/package.json +13 -12
|
@@ -8,13 +8,10 @@
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.createPackage = exports.createPackageDirEntry = exports.createPackageRequestFromContext = void 0;
|
|
10
10
|
const core_1 = require("@salesforce/core");
|
|
11
|
-
const ts_types_1 = require("@salesforce/ts-types");
|
|
12
11
|
const pkgUtils = require("../utils/packageUtils");
|
|
13
12
|
const packageUtils_1 = require("../utils/packageUtils");
|
|
14
|
-
core_1.Messages.importMessagesDirectory(__dirname);
|
|
15
|
-
const messages = core_1.Messages.loadMessages('@salesforce/packaging', 'package_create');
|
|
16
13
|
function createPackageRequestFromContext(project, options) {
|
|
17
|
-
const namespace = options.noNamespace ? '' : project.getSfProjectJson().getContents().namespace
|
|
14
|
+
const namespace = options.noNamespace ? '' : project.getSfProjectJson().getContents().namespace ?? '';
|
|
18
15
|
return {
|
|
19
16
|
Name: options.name,
|
|
20
17
|
Description: options.description,
|
|
@@ -29,22 +26,21 @@ exports.createPackageRequestFromContext = createPackageRequestFromContext;
|
|
|
29
26
|
* Create packageDirectory json entry for this package that can be written to sfdx-project.json
|
|
30
27
|
*
|
|
31
28
|
* @param project
|
|
32
|
-
* @param
|
|
29
|
+
* @param options - package create options
|
|
33
30
|
* @private
|
|
34
31
|
*/
|
|
35
32
|
function createPackageDirEntry(project, options) {
|
|
36
|
-
|
|
33
|
+
const packageDirs = project.getSfProjectJson().getContents().packageDirectories ?? [];
|
|
37
34
|
let isNew = false;
|
|
38
|
-
if (!packageDirs) {
|
|
39
|
-
packageDirs = [];
|
|
40
|
-
}
|
|
41
35
|
// see if package exists (exists means it has an id or package)
|
|
42
|
-
let packageDir = packageDirs
|
|
36
|
+
let packageDir = packageDirs
|
|
37
|
+
.map((pd) => pd)
|
|
38
|
+
.find((pd) => pd.path === options.path && !pd.id && !pd.package);
|
|
43
39
|
if (!packageDir) {
|
|
44
40
|
// no match - create a new one
|
|
45
41
|
isNew = true;
|
|
46
42
|
packageDir = pkgUtils.DEFAULT_PACKAGE_DIR;
|
|
47
|
-
packageDir.path = packageDir.path
|
|
43
|
+
packageDir.path = (0, packageUtils_1.replaceIfEmpty)(packageDir.path, options.path);
|
|
48
44
|
}
|
|
49
45
|
if (packageDirs.length === 0) {
|
|
50
46
|
packageDir.default = true;
|
|
@@ -52,10 +48,10 @@ function createPackageDirEntry(project, options) {
|
|
|
52
48
|
else if (isNew) {
|
|
53
49
|
packageDir.default = !packageDirs.find((pd) => pd.default);
|
|
54
50
|
}
|
|
55
|
-
packageDir.package = packageDir.package
|
|
56
|
-
packageDir.versionName = packageDir.versionName
|
|
57
|
-
packageDir.versionNumber = packageDir.versionNumber
|
|
58
|
-
packageDir.versionDescription = packageDir.versionDescription
|
|
51
|
+
packageDir.package = (0, packageUtils_1.replaceIfEmpty)(packageDir.package, options.name);
|
|
52
|
+
packageDir.versionName = (0, packageUtils_1.replaceIfEmpty)(packageDir.versionName, pkgUtils.DEFAULT_PACKAGE_DIR.versionName);
|
|
53
|
+
packageDir.versionNumber = (0, packageUtils_1.replaceIfEmpty)(packageDir.versionNumber, pkgUtils.DEFAULT_PACKAGE_DIR.versionNumber);
|
|
54
|
+
packageDir.versionDescription = (0, packageUtils_1.replaceIfEmpty)(packageDir.versionDescription, options.description);
|
|
59
55
|
return packageDir;
|
|
60
56
|
}
|
|
61
57
|
exports.createPackageDirEntry = createPackageDirEntry;
|
|
@@ -63,30 +59,23 @@ async function createPackage(connection, project, options) {
|
|
|
63
59
|
// strip trailing slash from path param
|
|
64
60
|
options.path = options.path.replace(/\/$/, '');
|
|
65
61
|
const request = createPackageRequestFromContext(project, options);
|
|
66
|
-
let packageId = null;
|
|
67
62
|
const createResult = await connection.tooling
|
|
68
63
|
.sobject('Package2')
|
|
69
64
|
.create(request)
|
|
70
65
|
.catch((err) => {
|
|
71
|
-
const error =
|
|
66
|
+
const error = err instanceof Error ? err : new Error(typeof err === 'string' ? err : 'Unknown error');
|
|
72
67
|
throw core_1.SfError.wrap((0, packageUtils_1.applyErrorAction)((0, packageUtils_1.massageErrorMessage)(error)));
|
|
73
68
|
});
|
|
74
69
|
if (!createResult.success) {
|
|
75
70
|
throw pkgUtils.combineSaveErrors('Package2', 'create', createResult.errors);
|
|
76
71
|
}
|
|
77
|
-
packageId = createResult.id;
|
|
78
|
-
const queryResult = await connection.tooling.query(`SELECT Id FROM Package2 WHERE Id='${packageId}'`);
|
|
79
|
-
if (!queryResult?.records[0]) {
|
|
80
|
-
throw messages.createError('unableToFindPackageWithId', [packageId]);
|
|
81
|
-
}
|
|
82
|
-
const record = queryResult.records[0];
|
|
83
72
|
if (!process.env.SFDX_PROJECT_AUTOUPDATE_DISABLE_FOR_PACKAGE_CREATE) {
|
|
84
73
|
const packageDirectory = createPackageDirEntry(project, options);
|
|
85
74
|
project.getSfProjectJson().addPackageDirectory(packageDirectory);
|
|
86
|
-
project.getSfProjectJson().addPackageAlias(options.name,
|
|
75
|
+
project.getSfProjectJson().addPackageAlias(options.name, createResult.id);
|
|
87
76
|
await project.getSfProjectJson().write();
|
|
88
77
|
}
|
|
89
|
-
return { Id:
|
|
78
|
+
return { Id: createResult.id };
|
|
90
79
|
}
|
|
91
80
|
exports.createPackage = createPackage;
|
|
92
81
|
//# sourceMappingURL=packageCreate.js.map
|
|
@@ -16,7 +16,10 @@ async function deletePackage(idOrAlias, project, connection, undelete) {
|
|
|
16
16
|
const isUndelete = undelete;
|
|
17
17
|
request.IsDeprecated = !isUndelete;
|
|
18
18
|
const updateResult = await connection.tooling.update('Package2', request).catch((err) => {
|
|
19
|
-
|
|
19
|
+
if (err instanceof Error) {
|
|
20
|
+
throw (0, packageUtils_1.applyErrorAction)((0, packageUtils_1.massageErrorMessage)(err));
|
|
21
|
+
}
|
|
22
|
+
throw err;
|
|
20
23
|
});
|
|
21
24
|
if (!updateResult.success) {
|
|
22
25
|
throw (0, packageUtils_1.combineSaveErrors)('Package2', 'update', updateResult.errors);
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { Connection } from '@salesforce/core';
|
|
2
|
+
import { Nullable } from '@salesforce/ts-types';
|
|
2
3
|
import { QueryResult } from 'jsforce';
|
|
3
4
|
import { Duration } from '@salesforce/kit';
|
|
4
|
-
import {
|
|
5
|
+
import { PackageInstallCreateRequest, PackageInstallOptions, PackageType, PackagingSObjects } from '../interfaces';
|
|
5
6
|
import PackageInstallRequest = PackagingSObjects.PackageInstallRequest;
|
|
6
7
|
export declare function createPackageInstallRequest(connection: Connection, pkgInstallCreateRequest: PackageInstallCreateRequest, packageType: PackageType): Promise<PackagingSObjects.PackageInstallRequest>;
|
|
7
8
|
export declare function getStatus(connection: Connection, packageInstallRequestId: string): Promise<PackageInstallRequest>;
|
|
8
9
|
export declare function isErrorFromSPVQueryRestriction(err: Error): boolean;
|
|
9
10
|
export declare function isErrorPackageNotAvailable(err: Error): boolean;
|
|
10
|
-
export declare function getInstallationStatus(subscriberPackageVersionId: string, installationKey: string
|
|
11
|
-
export declare function waitForPublish(connection: Connection, subscriberPackageVersionId: string, frequency
|
|
12
|
-
export declare function pollStatus(connection: Connection, installRequestId: string, options
|
|
11
|
+
export declare function getInstallationStatus(subscriberPackageVersionId: string, installationKey: Nullable<string>, connection: Connection): Promise<QueryResult<PackagingSObjects.SubscriberPackageVersion> | undefined>;
|
|
12
|
+
export declare function waitForPublish(connection: Connection, subscriberPackageVersionId: string, frequency?: number | Duration, timeout?: number | Duration, installationKey?: string | Nullable<string>): Promise<void>;
|
|
13
|
+
export declare function pollStatus(connection: Connection, installRequestId: string, options?: PackageInstallOptions): Promise<PackageInstallRequest>;
|
|
@@ -60,7 +60,8 @@ async function createPackageInstallRequest(connection, pkgInstallCreateRequest,
|
|
|
60
60
|
}
|
|
61
61
|
exports.createPackageInstallRequest = createPackageInstallRequest;
|
|
62
62
|
async function getStatus(connection, packageInstallRequestId) {
|
|
63
|
-
|
|
63
|
+
const results = (await connection.tooling.retrieve('PackageInstallRequest', packageInstallRequestId));
|
|
64
|
+
return results;
|
|
64
65
|
}
|
|
65
66
|
exports.getStatus = getStatus;
|
|
66
67
|
// determines if error is from malformed SubscriberPackageVersion query
|
|
@@ -94,14 +95,14 @@ async function getInstallationStatus(subscriberPackageVersionId, installationKey
|
|
|
94
95
|
}
|
|
95
96
|
exports.getInstallationStatus = getInstallationStatus;
|
|
96
97
|
async function waitForPublish(connection, subscriberPackageVersionId, frequency, timeout, installationKey) {
|
|
97
|
-
const pollingTimeout = (0, packageUtils_1.numberToDuration)(timeout
|
|
98
|
+
const pollingTimeout = (0, packageUtils_1.numberToDuration)(timeout);
|
|
98
99
|
if (pollingTimeout.milliseconds <= 0) {
|
|
99
100
|
return;
|
|
100
101
|
}
|
|
101
102
|
let queryResult;
|
|
102
103
|
let installValidationStatus = 'PACKAGE_UNAVAILABLE';
|
|
103
104
|
const pollingOptions = {
|
|
104
|
-
frequency: (0, packageUtils_1.numberToDuration)(frequency
|
|
105
|
+
frequency: (0, packageUtils_1.numberToDuration)(frequency),
|
|
105
106
|
timeout: pollingTimeout,
|
|
106
107
|
poll: async () => {
|
|
107
108
|
queryResult = await getInstallationStatus(subscriberPackageVersionId, installationKey, connection);
|
|
@@ -130,7 +131,7 @@ async function waitForPublish(connection, subscriberPackageVersionId, frequency,
|
|
|
130
131
|
// if polling timed out
|
|
131
132
|
const error = installMsgs.createError('subscriberPackageVersionNotPublished');
|
|
132
133
|
error.setData(queryResult?.records[0]);
|
|
133
|
-
if (error.stack && e.stack) {
|
|
134
|
+
if (error.stack && e instanceof Error && e.stack) {
|
|
134
135
|
getLogger().debug(`Error during waitForPublish polling:\n${e.stack}`);
|
|
135
136
|
// append the original stack to this new error
|
|
136
137
|
error.stack += `\nDUE TO:\n${e.stack}`;
|
|
@@ -139,11 +140,11 @@ async function waitForPublish(connection, subscriberPackageVersionId, frequency,
|
|
|
139
140
|
}
|
|
140
141
|
}
|
|
141
142
|
exports.waitForPublish = waitForPublish;
|
|
142
|
-
async function pollStatus(connection, installRequestId, options) {
|
|
143
|
-
let packageInstallRequest;
|
|
143
|
+
async function pollStatus(connection, installRequestId, options = { pollingFrequency: 5000, pollingTimeout: 300000 }) {
|
|
144
|
+
let packageInstallRequest = await getStatus(connection, installRequestId);
|
|
144
145
|
const { pollingFrequency, pollingTimeout } = options;
|
|
145
|
-
const frequency = (0, packageUtils_1.numberToDuration)(pollingFrequency
|
|
146
|
-
const timeout = (0, packageUtils_1.numberToDuration)(pollingTimeout
|
|
146
|
+
const frequency = (0, packageUtils_1.numberToDuration)(pollingFrequency);
|
|
147
|
+
const timeout = (0, packageUtils_1.numberToDuration)(pollingTimeout);
|
|
147
148
|
const pollingOptions = {
|
|
148
149
|
frequency,
|
|
149
150
|
timeout,
|
|
@@ -169,7 +170,7 @@ async function pollStatus(connection, installRequestId, options) {
|
|
|
169
170
|
const errMsg = e instanceof Error ? e.message : (0, ts_types_1.isString)(e) ? e : 'polling timed out';
|
|
170
171
|
const error = new core_1.SfError(errMsg, 'PackageInstallTimeout');
|
|
171
172
|
error.setData(packageInstallRequest);
|
|
172
|
-
if (error.stack && e.stack) {
|
|
173
|
+
if (error.stack && e instanceof Error && e.stack) {
|
|
173
174
|
// add the original stack to this new error
|
|
174
175
|
error.stack += `\nDUE TO:\n${e.stack}`;
|
|
175
176
|
}
|
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SfProject } from '@salesforce/core';
|
|
2
2
|
import { AsyncCreatable } from '@salesforce/kit';
|
|
3
3
|
import { ProfileApiOptions } from '../interfaces';
|
|
4
4
|
export declare class PackageProfileApi extends AsyncCreatable<ProfileApiOptions> {
|
|
5
|
-
private options;
|
|
6
5
|
readonly profiles: ProfileInformation[];
|
|
7
|
-
apiVersion: string;
|
|
8
6
|
nodeEntities: {
|
|
9
7
|
name: string[];
|
|
10
|
-
childElement: string[];
|
|
11
8
|
parentElement: string[];
|
|
9
|
+
childElement: string[];
|
|
12
10
|
};
|
|
13
11
|
otherProfileSettings: {
|
|
14
12
|
name: string[];
|
|
15
|
-
childElement: string[];
|
|
16
13
|
parentElement: string[];
|
|
14
|
+
childElement: string[];
|
|
17
15
|
};
|
|
18
|
-
config: ConfigAggregator;
|
|
19
16
|
project: SfProject;
|
|
20
17
|
includeUserLicenses: boolean;
|
|
21
18
|
generateProfileInformation: boolean;
|
|
@@ -15,6 +15,63 @@ const core_1 = require("@salesforce/core");
|
|
|
15
15
|
const kit_1 = require("@salesforce/kit");
|
|
16
16
|
core_1.Messages.importMessagesDirectory(__dirname);
|
|
17
17
|
const profileApiMessages = core_1.Messages.loadMessages('@salesforce/packaging', 'profile_api');
|
|
18
|
+
// nodeEntities is used to determine which elements in the profile are relevant to the source being packaged.
|
|
19
|
+
// name refers to the entity type name in source that the element pertains to. As an example, a profile may
|
|
20
|
+
// have an entry like the example below, which should only be added to the packaged profile if the related
|
|
21
|
+
// CustomObject is in the source being packaged:
|
|
22
|
+
// <objectPermissions>
|
|
23
|
+
// <allowCreate>true</allowCreate>
|
|
24
|
+
// ...
|
|
25
|
+
// <object>MyCustomObject__c</object>
|
|
26
|
+
// ...
|
|
27
|
+
// </objectPermissions>
|
|
28
|
+
//
|
|
29
|
+
// For this example: nodeEntities.parentElement = objectPermissions and nodeEntities.childElement = object
|
|
30
|
+
const NODE_ENTITIES = {
|
|
31
|
+
name: [
|
|
32
|
+
'CustomObject',
|
|
33
|
+
'CustomField',
|
|
34
|
+
'Layout',
|
|
35
|
+
'CustomTab',
|
|
36
|
+
'CustomApplication',
|
|
37
|
+
'ApexClass',
|
|
38
|
+
'CustomPermission',
|
|
39
|
+
'ApexPage',
|
|
40
|
+
'ExternalDataSource',
|
|
41
|
+
'RecordType',
|
|
42
|
+
],
|
|
43
|
+
parentElement: [
|
|
44
|
+
'objectPermissions',
|
|
45
|
+
'fieldPermissions',
|
|
46
|
+
'layoutAssignments',
|
|
47
|
+
'tabVisibilities',
|
|
48
|
+
'applicationVisibilities',
|
|
49
|
+
'classAccesses',
|
|
50
|
+
'customPermissions',
|
|
51
|
+
'pageAccesses',
|
|
52
|
+
'externalDataSourceAccesses',
|
|
53
|
+
'recordTypeVisibilities',
|
|
54
|
+
],
|
|
55
|
+
childElement: [
|
|
56
|
+
'object',
|
|
57
|
+
'field',
|
|
58
|
+
'layout',
|
|
59
|
+
'tab',
|
|
60
|
+
'application',
|
|
61
|
+
'apexClass',
|
|
62
|
+
'name',
|
|
63
|
+
'apexPage',
|
|
64
|
+
'externalDataSource',
|
|
65
|
+
'recordType',
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
// There are some profile settings that are allowed to be packaged that may not necessarily map to a specific metadata
|
|
69
|
+
// object. We should still handle these accordingly, but a little differently than the above mentioned types.
|
|
70
|
+
const OTHER_PROFILE_SETTINGS = {
|
|
71
|
+
name: ['CustomSettings', 'CustomMetadataTypeAccess'],
|
|
72
|
+
parentElement: ['customSettingAccesses', 'customMetadataTypeAccesses'],
|
|
73
|
+
childElement: ['name', 'name'],
|
|
74
|
+
};
|
|
18
75
|
/*
|
|
19
76
|
* This class provides functions used to re-write .profiles in the workspace when creating a package2 version.
|
|
20
77
|
* All profiles found in the workspaces are extracted out and then re-written to only include metadata in the profile
|
|
@@ -23,74 +80,17 @@ const profileApiMessages = core_1.Messages.loadMessages('@salesforce/packaging',
|
|
|
23
80
|
class PackageProfileApi extends kit_1.AsyncCreatable {
|
|
24
81
|
constructor(options) {
|
|
25
82
|
super(options);
|
|
26
|
-
this.options = options;
|
|
27
83
|
this.profiles = [];
|
|
84
|
+
this.nodeEntities = NODE_ENTITIES;
|
|
85
|
+
this.otherProfileSettings = OTHER_PROFILE_SETTINGS;
|
|
86
|
+
this.includeUserLicenses = false;
|
|
28
87
|
this.generateProfileInformation = false;
|
|
88
|
+
this.project = options.project;
|
|
89
|
+
this.includeUserLicenses = options.includeUserLicenses ?? false;
|
|
90
|
+
this.generateProfileInformation = options.generateProfileInformation ?? false;
|
|
29
91
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
this.includeUserLicenses = this.options.includeUserLicenses;
|
|
33
|
-
this.generateProfileInformation = this.options.generateProfileInformation;
|
|
34
|
-
this.config = await core_1.ConfigAggregator.create();
|
|
35
|
-
this.apiVersion = this.config.getPropertyValue('apiVersion');
|
|
36
|
-
// nodeEntities is used to determine which elements in the profile are relevant to the source being packaged.
|
|
37
|
-
// name refers to the entity type name in source that the element pertains to. As an example, a profile may
|
|
38
|
-
// have an entry like the example below, which should only be added to the packaged profile if the related
|
|
39
|
-
// CustomObject is in the source being packaged:
|
|
40
|
-
// <objectPermissions>
|
|
41
|
-
// <allowCreate>true</allowCreate>
|
|
42
|
-
// ...
|
|
43
|
-
// <object>MyCustomObject__c</object>
|
|
44
|
-
// ...
|
|
45
|
-
// </objectPermissions>
|
|
46
|
-
//
|
|
47
|
-
// For this example: nodeEntities.parentElement = objectPermissions and nodeEntities.childElement = object
|
|
48
|
-
this.nodeEntities = {
|
|
49
|
-
name: [
|
|
50
|
-
'CustomObject',
|
|
51
|
-
'CustomField',
|
|
52
|
-
'Layout',
|
|
53
|
-
'CustomTab',
|
|
54
|
-
'CustomApplication',
|
|
55
|
-
'ApexClass',
|
|
56
|
-
'CustomPermission',
|
|
57
|
-
'ApexPage',
|
|
58
|
-
'ExternalDataSource',
|
|
59
|
-
'RecordType',
|
|
60
|
-
],
|
|
61
|
-
parentElement: [
|
|
62
|
-
'objectPermissions',
|
|
63
|
-
'fieldPermissions',
|
|
64
|
-
'layoutAssignments',
|
|
65
|
-
'tabVisibilities',
|
|
66
|
-
'applicationVisibilities',
|
|
67
|
-
'classAccesses',
|
|
68
|
-
'customPermissions',
|
|
69
|
-
'pageAccesses',
|
|
70
|
-
'externalDataSourceAccesses',
|
|
71
|
-
'recordTypeVisibilities',
|
|
72
|
-
],
|
|
73
|
-
childElement: [
|
|
74
|
-
'object',
|
|
75
|
-
'field',
|
|
76
|
-
'layout',
|
|
77
|
-
'tab',
|
|
78
|
-
'application',
|
|
79
|
-
'apexClass',
|
|
80
|
-
'name',
|
|
81
|
-
'apexPage',
|
|
82
|
-
'externalDataSource',
|
|
83
|
-
'recordType',
|
|
84
|
-
],
|
|
85
|
-
};
|
|
86
|
-
// There are some profile settings that are allowed to be packaged that may not necessarily map to a specific metadata
|
|
87
|
-
// object. We should still handle these accordingly, but a little differently than the above mentioned types.
|
|
88
|
-
this.otherProfileSettings = {
|
|
89
|
-
name: ['CustomSettings', 'CustomMetadataTypeAccess'],
|
|
90
|
-
parentElement: ['customSettingAccesses', 'customMetadataTypeAccesses'],
|
|
91
|
-
childElement: ['name', 'name'],
|
|
92
|
-
};
|
|
93
|
-
}
|
|
92
|
+
// eslint-disable-next-line class-methods-use-this,@typescript-eslint/no-empty-function
|
|
93
|
+
async init() { }
|
|
94
94
|
/**
|
|
95
95
|
* For any profile present in the workspace, this function generates a subset of data that only contains references
|
|
96
96
|
* to items in the manifest.
|
|
@@ -107,71 +107,74 @@ class PackageProfileApi extends kit_1.AsyncCreatable {
|
|
|
107
107
|
}
|
|
108
108
|
profilePaths.forEach((profilePath) => {
|
|
109
109
|
// profile metadata can present in any directory in the package structure
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
// Go through each of the other profile settings we might want to include. We pass in "all" the members since these
|
|
129
|
-
// will reference anything that could be packaged. The "copyNodes" function will only include the type if it
|
|
130
|
-
// exists in the profile itself
|
|
131
|
-
this.otherProfileSettings.name.forEach((element) => {
|
|
132
|
-
const idx = this.otherProfileSettings.name.indexOf(element);
|
|
133
|
-
if (idx > -1) {
|
|
134
|
-
hasNodes =
|
|
135
|
-
this.copyNodes(profileDom, this.otherProfileSettings.parentElement[idx], this.otherProfileSettings.childElement[idx], allMembers, profileNode, profileName) || hasNodes;
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
// add userLicenses to the profile
|
|
139
|
-
if (this.includeUserLicenses === true) {
|
|
140
|
-
const userLicenses = profileDom.getElementsByTagName('userLicense');
|
|
141
|
-
if (userLicenses) {
|
|
142
|
-
hasNodes = true;
|
|
143
|
-
for (const userLicense of Array.from(userLicenses)) {
|
|
144
|
-
profileNode.appendChild(userLicense.cloneNode(true));
|
|
110
|
+
const profileNameMatch = profilePath.match(/([^/]+)\.profile-meta.xml/);
|
|
111
|
+
const profileName = profileNameMatch ? profileNameMatch[1] : null;
|
|
112
|
+
if (profileName) {
|
|
113
|
+
const profileDom = new xmldom_1.DOMParser().parseFromString(fs.readFileSync(profilePath, 'utf-8'));
|
|
114
|
+
const newDom = new xmldom_1.DOMParser().parseFromString('<?xml version="1.0" encoding="UTF-8"?><Profile xmlns="http://soap.sforce.com/2006/04/metadata"></Profile>');
|
|
115
|
+
const profileNode = newDom.getElementsByTagName('Profile')[0];
|
|
116
|
+
let hasNodes = false;
|
|
117
|
+
// We need to keep track of all the members for when we package up the "OtherProfileSettings"
|
|
118
|
+
let allMembers = [];
|
|
119
|
+
manifest.Package.forEach((element) => {
|
|
120
|
+
const name = element.name;
|
|
121
|
+
const members = element.members;
|
|
122
|
+
allMembers = allMembers.concat(members);
|
|
123
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
124
|
+
const idx = this.nodeEntities.name.indexOf(name[0]);
|
|
125
|
+
if (idx > -1) {
|
|
126
|
+
hasNodes =
|
|
127
|
+
this.copyNodes(profileDom, this.nodeEntities.parentElement[idx], this.nodeEntities.childElement[idx], members, profileNode, profileName) ?? hasNodes;
|
|
145
128
|
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
129
|
+
});
|
|
130
|
+
// Go through each of the other profile settings we might want to include. We pass in "all" the members since these
|
|
131
|
+
// will reference anything that could be packaged. The "copyNodes" function will only include the type if it
|
|
132
|
+
// exists in the profile itself
|
|
133
|
+
this.otherProfileSettings.name.forEach((element) => {
|
|
134
|
+
const idx = this.otherProfileSettings.name.indexOf(element);
|
|
135
|
+
if (idx > -1) {
|
|
136
|
+
hasNodes =
|
|
137
|
+
this.copyNodes(profileDom, this.otherProfileSettings.parentElement[idx], this.otherProfileSettings.childElement[idx], allMembers, profileNode, profileName) ?? hasNodes;
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
// add userLicenses to the profile
|
|
141
|
+
if (this.includeUserLicenses) {
|
|
142
|
+
const userLicenses = profileDom.getElementsByTagName('userLicense');
|
|
143
|
+
if (userLicenses) {
|
|
144
|
+
hasNodes = true;
|
|
145
|
+
for (const userLicense of Array.from(userLicenses)) {
|
|
146
|
+
profileNode.appendChild(userLicense.cloneNode(true));
|
|
147
|
+
}
|
|
165
148
|
}
|
|
166
149
|
}
|
|
167
|
-
|
|
168
|
-
|
|
150
|
+
const xmlSrcFile = path.basename(profilePath);
|
|
151
|
+
const xmlFile = xmlSrcFile.replace(/(.*)(-meta.xml)/, '$1');
|
|
152
|
+
const destFilePath = path.join(destPath, xmlFile);
|
|
153
|
+
if (hasNodes) {
|
|
154
|
+
const serializer = new xmldom_1.XMLSerializer();
|
|
155
|
+
serializer.serializeToString(newDom);
|
|
156
|
+
fs.writeFileSync(destFilePath, serializer.serializeToString(newDom), 'utf-8');
|
|
169
157
|
}
|
|
170
|
-
|
|
171
|
-
//
|
|
172
|
-
// eslint-disable-next-line @typescript-eslint/no-
|
|
173
|
-
|
|
174
|
-
|
|
158
|
+
else {
|
|
159
|
+
// remove from manifest
|
|
160
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
161
|
+
const profileName = xmlFile.replace(/(.*)(\.profile)/, '$1');
|
|
162
|
+
excludedProfiles.push(profileName);
|
|
163
|
+
if (this.generateProfileInformation) {
|
|
164
|
+
const profile = this.profiles.find(({ ProfileName }) => ProfileName === profileName);
|
|
165
|
+
if (profile) {
|
|
166
|
+
profile.setIsPackaged(false);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
fs.unlinkSync(destFilePath);
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
// It is normal for the file to not exist if the profile is in the workspace but not in the directory being packaged.
|
|
174
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
175
|
+
if (err instanceof Error && 'code' in err && err.code !== 'ENOENT') {
|
|
176
|
+
throw err;
|
|
177
|
+
}
|
|
175
178
|
}
|
|
176
179
|
}
|
|
177
180
|
}
|
|
@@ -187,22 +190,25 @@ class PackageProfileApi extends kit_1.AsyncCreatable {
|
|
|
187
190
|
filterAndGenerateProfilesForManifest(typesArr, excludedDirectories = []) {
|
|
188
191
|
const profilePaths = this.findAllProfiles(excludedDirectories);
|
|
189
192
|
// Filter all profiles
|
|
190
|
-
|
|
193
|
+
const filteredTypesArr = (typesArr ?? []).filter((kvp) => kvp.name[0] !== 'Profile');
|
|
191
194
|
if (profilePaths) {
|
|
192
195
|
const members = [];
|
|
193
196
|
profilePaths.forEach((profilePath) => {
|
|
194
197
|
// profile metadata can present in any directory in the package structure
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
if (
|
|
198
|
-
|
|
198
|
+
const profileNameMatch = profilePath.match(/([^/]+)\.profile-meta.xml/);
|
|
199
|
+
const profileName = profileNameMatch ? profileNameMatch[1] : null;
|
|
200
|
+
if (profileName) {
|
|
201
|
+
members.push(profileName);
|
|
202
|
+
if (this.generateProfileInformation) {
|
|
203
|
+
this.profiles.push(new ProfileInformation(profileName, profilePath, true, []));
|
|
204
|
+
}
|
|
199
205
|
}
|
|
200
206
|
});
|
|
201
207
|
if (members.length > 0) {
|
|
202
|
-
|
|
208
|
+
filteredTypesArr.push({ name: ['Profile'], members });
|
|
203
209
|
}
|
|
204
210
|
}
|
|
205
|
-
return
|
|
211
|
+
return filteredTypesArr;
|
|
206
212
|
}
|
|
207
213
|
getProfileInformation() {
|
|
208
214
|
return this.profiles;
|
|
@@ -216,18 +222,20 @@ class PackageProfileApi extends kit_1.AsyncCreatable {
|
|
|
216
222
|
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
|
217
223
|
for (let i = 0; i < nodes.length; i++) {
|
|
218
224
|
const name = nodes[i].getElementsByTagName(childElement)[0].childNodes[0].nodeValue;
|
|
219
|
-
if (
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
profile
|
|
225
|
+
if (name) {
|
|
226
|
+
if (members.includes(name)) {
|
|
227
|
+
// appendChild will take the passed in node (newNode) and find the parent if it exists and then remove
|
|
228
|
+
// the newNode from the parent. This causes issues with the way this is copying the nodes, so pass in a clone instead.
|
|
229
|
+
const currentNode = nodes[i].cloneNode(true);
|
|
230
|
+
appendToNode.appendChild(currentNode);
|
|
231
|
+
nodesAdded = true;
|
|
232
|
+
}
|
|
233
|
+
else if (this.generateProfileInformation) {
|
|
234
|
+
// Tell the user which profile setting has been removed from the package
|
|
235
|
+
const profile = this.profiles.find(({ ProfileName }) => ProfileName === profileName);
|
|
236
|
+
if (profile) {
|
|
237
|
+
profile.appendRemovedSetting(name);
|
|
238
|
+
}
|
|
231
239
|
}
|
|
232
240
|
}
|
|
233
241
|
}
|
|
@@ -17,7 +17,7 @@ const messages = core_1.Messages.loadMessages('@salesforce/packaging', 'package_
|
|
|
17
17
|
const pkgMessages = core_1.Messages.loadMessages('@salesforce/packaging', 'package');
|
|
18
18
|
async function getUninstallErrors(conn, id) {
|
|
19
19
|
const errorQueryResult = await conn.tooling.query(`"SELECT Message FROM PackageVersionUninstallRequestError WHERE ParentRequest.Id = '${id}' ORDER BY Message"`);
|
|
20
|
-
return errorQueryResult?.records
|
|
20
|
+
return errorQueryResult?.records ?? [];
|
|
21
21
|
}
|
|
22
22
|
exports.getUninstallErrors = getUninstallErrors;
|
|
23
23
|
async function pollUninstall(uninstallRequestId, conn, frequency, wait) {
|
|
@@ -58,17 +58,23 @@ async function uninstallPackage(id, conn, frequency = kit_1.Duration.seconds(0),
|
|
|
58
58
|
const uninstallRequest = await conn.tooling.sobject('SubscriberPackageVersionUninstallRequest').create({
|
|
59
59
|
SubscriberPackageVersionId: id,
|
|
60
60
|
});
|
|
61
|
-
if (
|
|
62
|
-
|
|
63
|
-
.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
if (uninstallRequest.success) {
|
|
62
|
+
if (wait.seconds === 0) {
|
|
63
|
+
return (await conn.tooling
|
|
64
|
+
.sobject('SubscriberPackageVersionUninstallRequest')
|
|
65
|
+
.retrieve(uninstallRequest.id));
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
return await pollUninstall(uninstallRequest.id, conn, frequency, wait);
|
|
69
|
+
}
|
|
68
70
|
}
|
|
71
|
+
throw (0, packageUtils_1.combineSaveErrors)('SubscriberPackageVersionUninstallRequest', 'create', uninstallRequest.errors);
|
|
69
72
|
}
|
|
70
73
|
catch (err) {
|
|
71
|
-
|
|
74
|
+
if (err instanceof Error) {
|
|
75
|
+
throw (0, packageUtils_1.applyErrorAction)((0, packageUtils_1.massageErrorMessage)(err));
|
|
76
|
+
}
|
|
77
|
+
throw err;
|
|
72
78
|
}
|
|
73
79
|
}
|
|
74
80
|
exports.uninstallPackage = uninstallPackage;
|