@salesforce/packaging 2.2.9-profiles.1 → 2.3.0
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 +4 -2
- package/lib/package/packageProfileApi.d.ts +42 -6
- package/lib/package/packageProfileApi.js +234 -58
- package/lib/package/packageVersionCreate.d.ts +1 -6
- package/lib/package/packageVersionCreate.js +36 -63
- package/lib/package/packageVersionCreateRequest.js +20 -1
- package/lib/package/packageVersionList.js +4 -1
- package/lib/package/packageVersionReport.js +1 -1
- package/messages/package_version_create.md +12 -0
- package/package.json +16 -9
- package/lib/package/profileRewriter.d.ts +0 -37
- package/lib/package/profileRewriter.js +0 -108
|
@@ -4,7 +4,6 @@ import { SaveResult } from 'jsforce';
|
|
|
4
4
|
import { Attributes } from 'graphology-types';
|
|
5
5
|
import { Optional } from '@salesforce/ts-types';
|
|
6
6
|
import { ConvertResult } from '@salesforce/source-deploy-retrieve';
|
|
7
|
-
import { Package } from 'jsforce/lib/api/metadata';
|
|
8
7
|
import { PackageProfileApi } from '../package/packageProfileApi';
|
|
9
8
|
import { PackageAncestryNode } from '../package/packageAncestry';
|
|
10
9
|
import { PackagingSObjects } from './packagingSObjects';
|
|
@@ -49,6 +48,7 @@ export type PackageVersionCreateRequestResult = {
|
|
|
49
48
|
CreatedDate: string;
|
|
50
49
|
HasMetadataRemoved: boolean | null;
|
|
51
50
|
CreatedBy: string;
|
|
51
|
+
ConvertedFromVersionId: string | null;
|
|
52
52
|
};
|
|
53
53
|
export declare const PackageVersionCreateRequestResultInProgressStatuses: PackagingSObjects.Package2VersionStatus[];
|
|
54
54
|
export type PackageVersionCreateRequestError = {
|
|
@@ -155,6 +155,7 @@ export type PackageVersionListOptions = {
|
|
|
155
155
|
verbose?: boolean;
|
|
156
156
|
concise?: boolean;
|
|
157
157
|
isReleased?: boolean;
|
|
158
|
+
showConversionsOnly?: boolean;
|
|
158
159
|
};
|
|
159
160
|
export type PackageVersionUpdateOptions = {
|
|
160
161
|
InstallKey?: string;
|
|
@@ -266,10 +267,12 @@ export type PackageVersionCreateRequestQueryOptions = {
|
|
|
266
267
|
createdlastdays?: number;
|
|
267
268
|
status?: 'Queued' | 'InProgress' | 'Success' | 'Error';
|
|
268
269
|
id?: string;
|
|
270
|
+
showConversionsOnly?: boolean;
|
|
269
271
|
};
|
|
270
272
|
export type ProfileApiOptions = {
|
|
271
273
|
project: SfProject;
|
|
272
274
|
includeUserLicenses: boolean;
|
|
275
|
+
generateProfileInformation: boolean;
|
|
273
276
|
};
|
|
274
277
|
export type PackageVersionReportResult = Partial<Omit<PackagingSObjects.Package2Version, 'AncestorId' | 'HasPassedCodeCoverageCheck' | 'HasMetadataRemoved'>> & {
|
|
275
278
|
Package2: Partial<Omit<PackagingSObjects.Package2, 'IsOrgDependent'>> & {
|
|
@@ -370,4 +373,3 @@ export declare const Package1VersionEvents: {
|
|
|
370
373
|
progress: string;
|
|
371
374
|
};
|
|
372
375
|
};
|
|
373
|
-
export type PackageXml = Pick<Package, 'types' | 'version'>;
|
|
@@ -1,27 +1,63 @@
|
|
|
1
1
|
import { SfProject } from '@salesforce/core';
|
|
2
2
|
import { AsyncCreatable } from '@salesforce/kit';
|
|
3
|
-
import {
|
|
3
|
+
import { ProfileApiOptions } from '../interfaces';
|
|
4
4
|
export declare class PackageProfileApi extends AsyncCreatable<ProfileApiOptions> {
|
|
5
|
+
readonly profiles: ProfileInformation[];
|
|
6
|
+
nodeEntities: {
|
|
7
|
+
name: string[];
|
|
8
|
+
parentElement: string[];
|
|
9
|
+
childElement: string[];
|
|
10
|
+
};
|
|
11
|
+
otherProfileSettings: {
|
|
12
|
+
name: string[];
|
|
13
|
+
parentElement: string[];
|
|
14
|
+
childElement: string[];
|
|
15
|
+
};
|
|
5
16
|
project: SfProject;
|
|
6
17
|
includeUserLicenses: boolean;
|
|
18
|
+
generateProfileInformation: boolean;
|
|
7
19
|
constructor(options: ProfileApiOptions);
|
|
8
20
|
init(): Promise<void>;
|
|
9
21
|
/**
|
|
10
22
|
* For any profile present in the workspace, this function generates a subset of data that only contains references
|
|
11
23
|
* to items in the manifest.
|
|
12
24
|
*
|
|
13
|
-
* return a list of profile file locations that need to be removed from the package because they are empty
|
|
14
|
-
*
|
|
15
25
|
* @param destPath location of new profiles
|
|
16
|
-
* @param
|
|
26
|
+
* @param manifest
|
|
17
27
|
* @param excludedDirectories Directories to not include profiles from
|
|
18
28
|
*/
|
|
19
|
-
generateProfiles(destPath: string,
|
|
29
|
+
generateProfiles(destPath: string, manifest: {
|
|
30
|
+
Package: Array<{
|
|
31
|
+
name: string[];
|
|
32
|
+
members: string[];
|
|
33
|
+
}>;
|
|
34
|
+
}, excludedDirectories?: string[]): string[];
|
|
20
35
|
/**
|
|
21
36
|
* Filter out all profiles in the manifest and if any profiles exists in the workspace, add them to the manifest.
|
|
22
37
|
*
|
|
23
38
|
* @param typesArr array of objects { name[], members[] } that represent package types JSON.
|
|
24
39
|
* @param excludedDirectories Direcotires not to generate profiles for
|
|
25
40
|
*/
|
|
26
|
-
filterAndGenerateProfilesForManifest(typesArr:
|
|
41
|
+
filterAndGenerateProfilesForManifest(typesArr: Array<{
|
|
42
|
+
name: string[];
|
|
43
|
+
members: string[];
|
|
44
|
+
}>, excludedDirectories?: string[]): Array<{
|
|
45
|
+
name: string[];
|
|
46
|
+
members: string[];
|
|
47
|
+
}>;
|
|
48
|
+
getProfileInformation(): ProfileInformation[];
|
|
49
|
+
private copyNodes;
|
|
50
|
+
private findAllProfiles;
|
|
51
|
+
}
|
|
52
|
+
declare class ProfileInformation {
|
|
53
|
+
ProfileName: string;
|
|
54
|
+
ProfilePath: string;
|
|
55
|
+
IsPackaged: boolean;
|
|
56
|
+
settingsRemoved: string[];
|
|
57
|
+
constructor(ProfileName: string, ProfilePath: string, IsPackaged: boolean, settingsRemoved: string[]);
|
|
58
|
+
setIsPackaged(IsPackaged: boolean): void;
|
|
59
|
+
appendRemovedSetting(setting: string): void;
|
|
60
|
+
logDebug(): string;
|
|
61
|
+
logInfo(): string;
|
|
27
62
|
}
|
|
63
|
+
export {};
|
|
@@ -1,20 +1,77 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PackageProfileApi = void 0;
|
|
4
2
|
/*
|
|
5
3
|
* Copyright (c) 2022, salesforce.com, inc.
|
|
6
4
|
* All rights reserved.
|
|
7
5
|
* Licensed under the BSD 3-Clause license.
|
|
8
6
|
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
9
7
|
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.PackageProfileApi = void 0;
|
|
10
10
|
const path = require("path");
|
|
11
11
|
const fs = require("fs");
|
|
12
12
|
const glob = require("glob");
|
|
13
|
+
const xmldom_1 = require("@xmldom/xmldom");
|
|
13
14
|
const core_1 = require("@salesforce/core");
|
|
14
15
|
const kit_1 = require("@salesforce/kit");
|
|
15
|
-
const profileRewriter_1 = require("./profileRewriter");
|
|
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,9 +80,14 @@ const profileApiMessages = core_1.Messages.loadMessages('@salesforce/packaging',
|
|
|
23
80
|
class PackageProfileApi extends kit_1.AsyncCreatable {
|
|
24
81
|
constructor(options) {
|
|
25
82
|
super(options);
|
|
83
|
+
this.profiles = [];
|
|
84
|
+
this.nodeEntities = NODE_ENTITIES;
|
|
85
|
+
this.otherProfileSettings = OTHER_PROFILE_SETTINGS;
|
|
26
86
|
this.includeUserLicenses = false;
|
|
87
|
+
this.generateProfileInformation = false;
|
|
27
88
|
this.project = options.project;
|
|
28
89
|
this.includeUserLicenses = options.includeUserLicenses ?? false;
|
|
90
|
+
this.generateProfileInformation = options.generateProfileInformation ?? false;
|
|
29
91
|
}
|
|
30
92
|
// eslint-disable-next-line class-methods-use-this,@typescript-eslint/no-empty-function
|
|
31
93
|
async init() { }
|
|
@@ -33,45 +95,91 @@ class PackageProfileApi extends kit_1.AsyncCreatable {
|
|
|
33
95
|
* For any profile present in the workspace, this function generates a subset of data that only contains references
|
|
34
96
|
* to items in the manifest.
|
|
35
97
|
*
|
|
36
|
-
* return a list of profile file locations that need to be removed from the package because they are empty
|
|
37
|
-
*
|
|
38
98
|
* @param destPath location of new profiles
|
|
39
|
-
* @param
|
|
99
|
+
* @param manifest
|
|
40
100
|
* @param excludedDirectories Directories to not include profiles from
|
|
41
101
|
*/
|
|
42
|
-
generateProfiles(destPath,
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
profilePath,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
102
|
+
generateProfiles(destPath, manifest, excludedDirectories = []) {
|
|
103
|
+
const excludedProfiles = [];
|
|
104
|
+
const profilePaths = this.findAllProfiles(excludedDirectories);
|
|
105
|
+
if (!profilePaths) {
|
|
106
|
+
return excludedProfiles;
|
|
107
|
+
}
|
|
108
|
+
profilePaths.forEach((profilePath) => {
|
|
109
|
+
// profile metadata can present in any directory in the package structure
|
|
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;
|
|
128
|
+
}
|
|
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
|
+
}
|
|
70
139
|
});
|
|
71
|
-
|
|
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
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
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');
|
|
157
|
+
}
|
|
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
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
72
180
|
}
|
|
73
|
-
})
|
|
74
|
-
|
|
181
|
+
});
|
|
182
|
+
return excludedProfiles;
|
|
75
183
|
}
|
|
76
184
|
/**
|
|
77
185
|
* Filter out all profiles in the manifest and if any profiles exists in the workspace, add them to the manifest.
|
|
@@ -80,29 +188,97 @@ class PackageProfileApi extends kit_1.AsyncCreatable {
|
|
|
80
188
|
* @param excludedDirectories Direcotires not to generate profiles for
|
|
81
189
|
*/
|
|
82
190
|
filterAndGenerateProfilesForManifest(typesArr, excludedDirectories = []) {
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
191
|
+
const profilePaths = this.findAllProfiles(excludedDirectories);
|
|
192
|
+
// Filter all profiles
|
|
193
|
+
const filteredTypesArr = (typesArr ?? []).filter((kvp) => kvp.name[0] !== 'Profile');
|
|
194
|
+
if (profilePaths) {
|
|
195
|
+
const members = [];
|
|
196
|
+
profilePaths.forEach((profilePath) => {
|
|
197
|
+
// profile metadata can present in any directory in the package structure
|
|
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
|
+
}
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
if (members.length > 0) {
|
|
208
|
+
filteredTypesArr.push({ name: ['Profile'], members });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return filteredTypesArr;
|
|
212
|
+
}
|
|
213
|
+
getProfileInformation() {
|
|
214
|
+
return this.profiles;
|
|
215
|
+
}
|
|
216
|
+
copyNodes(originalDom, parentElement, childElement, members, appendToNode, profileName) {
|
|
217
|
+
let nodesAdded = false;
|
|
218
|
+
const nodes = originalDom.getElementsByTagName(parentElement);
|
|
219
|
+
if (!nodes) {
|
|
220
|
+
return nodesAdded;
|
|
221
|
+
}
|
|
222
|
+
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
|
223
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
224
|
+
const name = nodes[i].getElementsByTagName(childElement)[0].childNodes[0].nodeValue;
|
|
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
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return nodesAdded;
|
|
243
|
+
}
|
|
244
|
+
findAllProfiles(excludedDirectories = []) {
|
|
245
|
+
return glob.sync(path.join(this.project.getPath(), '**', '*.profile-meta.xml'), {
|
|
246
|
+
ignore: excludedDirectories.map((dir) => `**/${dir}/**`),
|
|
86
247
|
});
|
|
87
|
-
// Filter all profiles, and add back the ones we found names for
|
|
88
|
-
return typesArr
|
|
89
|
-
.filter((kvp) => kvp.name !== 'Profile')
|
|
90
|
-
.concat([{ name: 'Profile', members: profilePathsWithNames.map((i) => i.name) }]);
|
|
91
248
|
}
|
|
92
249
|
}
|
|
93
250
|
exports.PackageProfileApi = PackageProfileApi;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
251
|
+
class ProfileInformation {
|
|
252
|
+
constructor(ProfileName, ProfilePath, IsPackaged, settingsRemoved) {
|
|
253
|
+
this.ProfileName = ProfileName;
|
|
254
|
+
this.ProfilePath = ProfilePath;
|
|
255
|
+
this.IsPackaged = IsPackaged;
|
|
256
|
+
this.settingsRemoved = settingsRemoved;
|
|
257
|
+
}
|
|
258
|
+
setIsPackaged(IsPackaged) {
|
|
259
|
+
this.IsPackaged = IsPackaged;
|
|
260
|
+
}
|
|
261
|
+
appendRemovedSetting(setting) {
|
|
262
|
+
this.settingsRemoved.push(setting);
|
|
263
|
+
}
|
|
264
|
+
logDebug() {
|
|
265
|
+
let info = profileApiMessages.getMessage('addProfileToPackage', [this.ProfileName, this.ProfilePath]);
|
|
266
|
+
this.settingsRemoved.forEach((setting) => {
|
|
267
|
+
info += '\n\t' + profileApiMessages.getMessage('removeProfileSetting', [setting, this.ProfileName]);
|
|
268
|
+
});
|
|
269
|
+
if (!this.IsPackaged) {
|
|
270
|
+
info += '\n\t' + profileApiMessages.getMessage('removeProfile', [this.ProfileName]);
|
|
271
|
+
}
|
|
272
|
+
info += '\n';
|
|
273
|
+
return info;
|
|
274
|
+
}
|
|
275
|
+
logInfo() {
|
|
276
|
+
if (this.IsPackaged) {
|
|
277
|
+
return profileApiMessages.getMessage('addProfileToPackage', [this.ProfileName, this.ProfilePath]);
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
return profileApiMessages.getMessage('profileNotIncluded', [this.ProfileName]);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
108
284
|
//# sourceMappingURL=packageProfileApi.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ConvertResult } from '@salesforce/source-deploy-retrieve';
|
|
2
|
-
import { MDFolderForArtifactOptions, PackageVersionCreateOptions, PackageVersionCreateRequestResult
|
|
2
|
+
import { MDFolderForArtifactOptions, PackageVersionCreateOptions, PackageVersionCreateRequestResult } from '../interfaces';
|
|
3
3
|
export declare class PackageVersionCreate {
|
|
4
4
|
private options;
|
|
5
5
|
private apiVersionFromPackageXml;
|
|
@@ -66,8 +66,3 @@ export declare class MetadataResolver {
|
|
|
66
66
|
*/
|
|
67
67
|
private convertMetadata;
|
|
68
68
|
}
|
|
69
|
-
export declare const packageXmlStringToPackageXmlJson: (rawXml: string) => PackageXml;
|
|
70
|
-
/**
|
|
71
|
-
* Converts PackageXmlJson to a string representing the Xml
|
|
72
|
-
* */
|
|
73
|
-
export declare const packageXmlJsonToXmlString: (packageXmlJson: PackageXml) => string;
|
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.
|
|
9
|
+
exports.MetadataResolver = exports.PackageVersionCreate = void 0;
|
|
10
10
|
const path = require("path");
|
|
11
11
|
const os = require("os");
|
|
12
12
|
const fs = require("fs");
|
|
13
13
|
const core_1 = require("@salesforce/core");
|
|
14
14
|
const source_deploy_retrieve_1 = require("@salesforce/source-deploy-retrieve");
|
|
15
15
|
const scratchOrgSettingsGenerator_1 = require("@salesforce/core/lib/org/scratchOrgSettingsGenerator");
|
|
16
|
+
const xml2js = require("xml2js");
|
|
16
17
|
const kit_1 = require("@salesforce/kit");
|
|
17
|
-
const fast_xml_parser_1 = require("fast-xml-parser");
|
|
18
18
|
const pkgUtils = require("../utils/packageUtils");
|
|
19
19
|
const packageUtils_1 = require("../utils/packageUtils");
|
|
20
20
|
const interfaces_1 = require("../interfaces");
|
|
@@ -240,6 +240,8 @@ class PackageVersionCreate {
|
|
|
240
240
|
sourceDir: sourceBaseDir,
|
|
241
241
|
sourceApiVersion: this.project?.getSfProjectJson()?.get('sourceApiVersion') ?? undefined,
|
|
242
242
|
};
|
|
243
|
+
// Stores any additional client side info that might be needed later on in the process
|
|
244
|
+
const clientSideInfo = new Map();
|
|
243
245
|
await fs.promises.mkdir(packageVersBlobDirectory, { recursive: true });
|
|
244
246
|
const settingsGenerator = new scratchOrgSettingsGenerator_1.default({ asDirectory: true });
|
|
245
247
|
let packageDescriptorJson = (0, kit_1.cloneJson)(this.packageObject);
|
|
@@ -303,19 +305,7 @@ class PackageVersionCreate {
|
|
|
303
305
|
}
|
|
304
306
|
packageDescriptorJson.ancestorId = ancestorId;
|
|
305
307
|
await fs.promises.writeFile(path.join(packageVersBlobDirectory, DESCRIPTOR_FILE), JSON.stringify(packageDescriptorJson), 'utf-8');
|
|
306
|
-
await this.cleanGeneratedPackage(
|
|
307
|
-
packageVersMetadataFolder,
|
|
308
|
-
packageVersProfileFolder,
|
|
309
|
-
unpackagedMetadataFolder,
|
|
310
|
-
seedMetadataFolder,
|
|
311
|
-
metadataZipFile,
|
|
312
|
-
settingsZipFile,
|
|
313
|
-
packageVersBlobDirectory,
|
|
314
|
-
packageVersBlobZipFile,
|
|
315
|
-
unpackagedMetadataZipFile,
|
|
316
|
-
seedMetadataZipFile,
|
|
317
|
-
settingsGenerator,
|
|
318
|
-
});
|
|
308
|
+
await this.cleanGeneratedPackage(packageVersMetadataFolder, packageVersProfileFolder, unpackagedMetadataFolder, seedMetadataFolder, metadataZipFile, settingsZipFile, packageVersBlobDirectory, packageVersBlobZipFile, unpackagedMetadataZipFile, seedMetadataZipFile, clientSideInfo, settingsGenerator);
|
|
319
309
|
return this.createRequestObject(preserveFiles, packageVersTmpRoot, packageVersBlobZipFile);
|
|
320
310
|
}
|
|
321
311
|
verifyHasSource(componentSet) {
|
|
@@ -323,22 +313,22 @@ class PackageVersionCreate {
|
|
|
323
313
|
throw messages.createError('noSourceInRootDirectory', [this.packageObject.path ?? '<unknown>']);
|
|
324
314
|
}
|
|
325
315
|
}
|
|
326
|
-
async cleanGeneratedPackage(
|
|
316
|
+
async cleanGeneratedPackage(packageVersMetadataFolder, packageVersProfileFolder, unpackagedMetadataFolder, seedMetadataFolder, metadataZipFile, settingsZipFile, packageVersBlobDirectory, packageVersBlobZipFile, unpackagedMetadataZipFile, seedMetadataZipFile, clientSideInfo, settingsGenerator) {
|
|
327
317
|
// As part of the source convert process, the package.xml has been written into the tmp metadata directory.
|
|
328
318
|
// The package.xml may need to be manipulated due to processing profiles in the workspace or additional
|
|
329
319
|
// metadata exclusions. If necessary, read the existing package.xml and then re-write it.
|
|
330
320
|
const currentPackageXml = await fs.promises.readFile(path.join(packageVersMetadataFolder, 'package.xml'), 'utf8');
|
|
331
321
|
// convert to json
|
|
332
|
-
const
|
|
333
|
-
if (!
|
|
322
|
+
const packageJson = (await xml2js.parseStringPromise(currentPackageXml));
|
|
323
|
+
if (!packageJson?.Package) {
|
|
334
324
|
throw messages.createError('packageXmlDoesNotContainPackage');
|
|
335
325
|
}
|
|
336
|
-
if (!
|
|
326
|
+
if (!packageJson?.Package.types) {
|
|
337
327
|
throw messages.createError('packageXmlDoesNotContainPackageTypes');
|
|
338
328
|
}
|
|
339
329
|
fs.mkdirSync(packageVersMetadataFolder, { recursive: true });
|
|
340
330
|
fs.mkdirSync(packageVersProfileFolder, { recursive: true });
|
|
341
|
-
this.apiVersionFromPackageXml =
|
|
331
|
+
this.apiVersionFromPackageXml = packageJson.Package.version;
|
|
342
332
|
const sourceApiVersion = this.project?.getSfProjectJson()?.get('sourceApiVersion');
|
|
343
333
|
const hasSeedMetadata = await this.metadataResolver.resolveMetadata(this.packageObject.seedMetadata?.path, seedMetadataFolder, 'seedMDDirectoryDoesNotExist', sourceApiVersion);
|
|
344
334
|
let hasUnpackagedMetadata = false;
|
|
@@ -351,16 +341,32 @@ class PackageVersionCreate {
|
|
|
351
341
|
.getPackageDirectories()
|
|
352
342
|
.map((packageDir) => packageDir.unpackagedMetadata?.path)
|
|
353
343
|
.filter((packageDirPath) => packageDirPath);
|
|
354
|
-
const typesArr = this.options?.profileApi?.filterAndGenerateProfilesForManifest(
|
|
355
|
-
|
|
344
|
+
const typesArr = this.options?.profileApi?.filterAndGenerateProfilesForManifest(packageJson.Package.types, profileExcludeDirs) ??
|
|
345
|
+
packageJson.Package.types;
|
|
356
346
|
// Next generate profiles and retrieve any profiles that were excluded because they had no matching nodes.
|
|
357
|
-
const excludedProfiles = this.options?.profileApi?.generateProfiles(packageVersProfileFolder,
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
347
|
+
const excludedProfiles = this.options?.profileApi?.generateProfiles(packageVersProfileFolder, {
|
|
348
|
+
Package: typesArr,
|
|
349
|
+
}, profileExcludeDirs);
|
|
350
|
+
if (excludedProfiles?.length) {
|
|
351
|
+
const profileIdx = typesArr.findIndex((e) => e.name[0] === 'Profile');
|
|
352
|
+
typesArr[profileIdx].members = typesArr[profileIdx].members.filter((e) => !excludedProfiles.includes(e));
|
|
353
|
+
}
|
|
354
|
+
packageJson.Package.types = typesArr;
|
|
355
|
+
// Re-write the package.xml in case profiles have been added or removed
|
|
356
|
+
const xmlBuilder = new xml2js.Builder({
|
|
357
|
+
xmldec: { version: '1.0', encoding: 'UTF-8' },
|
|
358
|
+
});
|
|
359
|
+
const xml = xmlBuilder.buildObject(packageJson);
|
|
360
|
+
// Log information about the profiles being packaged up
|
|
361
|
+
const profiles = this.options?.profileApi?.getProfileInformation() ?? [];
|
|
362
|
+
profiles.forEach((profile) => {
|
|
363
|
+
if (this.logger.shouldLog(core_1.LoggerLevel.DEBUG)) {
|
|
364
|
+
this.logger.debug(profile.logDebug());
|
|
365
|
+
}
|
|
366
|
+
else if (this.logger.shouldLog(core_1.LoggerLevel.INFO)) {
|
|
367
|
+
this.logger.info(profile.logInfo());
|
|
368
|
+
}
|
|
362
369
|
});
|
|
363
|
-
const xml = (0, exports.packageXmlJsonToXmlString)(packageXmlAsJson);
|
|
364
370
|
await fs.promises.writeFile(path.join(packageVersMetadataFolder, 'package.xml'), xml, 'utf-8');
|
|
365
371
|
// Zip the packageVersMetadataFolder folder and put the zip in {packageVersBlobDirectory}/package.zip
|
|
366
372
|
await (0, packageUtils_1.zipDir)(packageVersMetadataFolder, metadataZipFile);
|
|
@@ -506,9 +512,11 @@ class PackageVersionCreate {
|
|
|
506
512
|
return this.pkg.getType();
|
|
507
513
|
}
|
|
508
514
|
async resolveUserLicenses(includeUserLicenses) {
|
|
515
|
+
const shouldGenerateProfileInformation = this.logger.shouldLog(core_1.LoggerLevel.INFO) || this.logger.shouldLog(core_1.LoggerLevel.DEBUG);
|
|
509
516
|
return packageProfileApi_1.PackageProfileApi.create({
|
|
510
517
|
project: this.project,
|
|
511
518
|
includeUserLicenses,
|
|
519
|
+
generateProfileInformation: shouldGenerateProfileInformation,
|
|
512
520
|
});
|
|
513
521
|
}
|
|
514
522
|
async validateOptionsForPackageType() {
|
|
@@ -847,39 +855,4 @@ class MetadataResolver {
|
|
|
847
855
|
}
|
|
848
856
|
}
|
|
849
857
|
exports.MetadataResolver = MetadataResolver;
|
|
850
|
-
const packageXmlStringToPackageXmlJson = (rawXml) => {
|
|
851
|
-
const parser = new fast_xml_parser_1.XMLParser({
|
|
852
|
-
ignoreAttributes: true,
|
|
853
|
-
parseTagValue: false,
|
|
854
|
-
parseAttributeValue: false,
|
|
855
|
-
cdataPropName: '__cdata',
|
|
856
|
-
ignoreDeclaration: true,
|
|
857
|
-
numberParseOptions: { leadingZeros: false, hex: false },
|
|
858
|
-
// make sure types and members is always an array
|
|
859
|
-
isArray: (name) => ['types', 'members'].includes(name),
|
|
860
|
-
});
|
|
861
|
-
return parser.parse(rawXml).Package;
|
|
862
|
-
};
|
|
863
|
-
exports.packageXmlStringToPackageXmlJson = packageXmlStringToPackageXmlJson;
|
|
864
|
-
/**
|
|
865
|
-
* Converts PackageXmlJson to a string representing the Xml
|
|
866
|
-
* */
|
|
867
|
-
const packageXmlJsonToXmlString = (packageXmlJson) => {
|
|
868
|
-
const builder = new fast_xml_parser_1.XMLBuilder({
|
|
869
|
-
format: true,
|
|
870
|
-
indentBy: ' ',
|
|
871
|
-
ignoreAttributes: false,
|
|
872
|
-
cdataPropName: '__cdata',
|
|
873
|
-
processEntities: false,
|
|
874
|
-
attributeNamePrefix: '@@@',
|
|
875
|
-
});
|
|
876
|
-
return String(builder.build({
|
|
877
|
-
'?xml': {
|
|
878
|
-
'@@@version': '1.0',
|
|
879
|
-
'@@@encoding': 'UTF-8',
|
|
880
|
-
},
|
|
881
|
-
Package: { ...packageXmlJson, '@@@xmlns': 'http://soap.sforce.com/2006/04/metadata' },
|
|
882
|
-
}));
|
|
883
|
-
};
|
|
884
|
-
exports.packageXmlJsonToXmlString = packageXmlJsonToXmlString;
|
|
885
858
|
//# sourceMappingURL=packageVersionCreate.js.map
|
|
@@ -14,7 +14,7 @@ core_1.Messages.importMessagesDirectory(__dirname);
|
|
|
14
14
|
const messages = core_1.Messages.loadMessages('@salesforce/packaging', 'package_version_create');
|
|
15
15
|
const STATUS_ERROR = 'Error';
|
|
16
16
|
const QUERY = 'SELECT Id, Status, Package2Id, Package2VersionId, Package2Version.SubscriberPackageVersionId, Tag, Branch, ' +
|
|
17
|
-
'CreatedDate, Package2Version.HasMetadataRemoved, CreatedById ' +
|
|
17
|
+
'CreatedDate, Package2Version.HasMetadataRemoved, CreatedById, IsConversionRequest, Package2Version.ConvertedFromVersionId ' +
|
|
18
18
|
'FROM Package2VersionCreateRequest ' +
|
|
19
19
|
'%s' + // WHERE, if applicable
|
|
20
20
|
'ORDER BY CreatedDate desc';
|
|
@@ -58,8 +58,23 @@ async function query(query, connection) {
|
|
|
58
58
|
CreatedDate: formatDate(new Date(record.CreatedDate)),
|
|
59
59
|
HasMetadataRemoved: record.Package2Version != null ? record.Package2Version.HasMetadataRemoved : null,
|
|
60
60
|
CreatedBy: record.CreatedById,
|
|
61
|
+
ConvertedFromVersionId: convertedFromVersionMessage(record.Status, record.Package2Version?.ConvertedFromVersionId),
|
|
61
62
|
}));
|
|
62
63
|
}
|
|
64
|
+
function convertedFromVersionMessage(status, convertedFromVersionId) {
|
|
65
|
+
switch (status) {
|
|
66
|
+
case 'Success':
|
|
67
|
+
return convertedFromVersionId;
|
|
68
|
+
case 'Queued':
|
|
69
|
+
return messages.getMessage('IdUnavailableWhenQueued');
|
|
70
|
+
case 'InProgress':
|
|
71
|
+
return messages.getMessage('IdUnavailableWhenInProgress');
|
|
72
|
+
case 'Error':
|
|
73
|
+
return messages.getMessage('IdUnavailableWhenError');
|
|
74
|
+
default:
|
|
75
|
+
return messages.getMessage('IdUnavailableWhenInProgress');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
63
78
|
async function queryForErrors(packageVersionCreateRequestId, connection) {
|
|
64
79
|
const queryResult = await connection.tooling.query(`SELECT Message FROM Package2VersionCreateRequestError WHERE ParentRequest.Id = '${packageVersionCreateRequestId}'`);
|
|
65
80
|
return queryResult.records ? queryResult.records.map((record) => record.Message) : [];
|
|
@@ -80,6 +95,10 @@ function constructWhere(options) {
|
|
|
80
95
|
if (options?.status) {
|
|
81
96
|
where.push(`Status = '${options.status.toLowerCase()}'`);
|
|
82
97
|
}
|
|
98
|
+
// show only conversions
|
|
99
|
+
if (options?.showConversionsOnly) {
|
|
100
|
+
where.push('IsConversionRequest = true ');
|
|
101
|
+
}
|
|
83
102
|
return where.length > 0 ? `WHERE ${where.join(' AND ')}` : '';
|
|
84
103
|
}
|
|
85
104
|
//# sourceMappingURL=packageVersionCreateRequest.js.map
|
|
@@ -34,11 +34,11 @@ const defaultFields = [
|
|
|
34
34
|
'AncestorId',
|
|
35
35
|
'ValidationSkipped',
|
|
36
36
|
'CreatedById',
|
|
37
|
+
'ConvertedFromVersionId',
|
|
37
38
|
];
|
|
38
39
|
const verboseFields = [
|
|
39
40
|
'CodeCoverage',
|
|
40
41
|
'HasPassedCodeCoverageCheck',
|
|
41
|
-
'ConvertedFromVersionId',
|
|
42
42
|
'ReleaseVersion',
|
|
43
43
|
'BuildDurationInSeconds',
|
|
44
44
|
'HasMetadataRemoved',
|
|
@@ -107,6 +107,9 @@ function constructWhere(options) {
|
|
|
107
107
|
if (options?.isReleased) {
|
|
108
108
|
where.push('IsReleased = true');
|
|
109
109
|
}
|
|
110
|
+
if (options?.showConversionsOnly) {
|
|
111
|
+
where.push('ConvertedFromVersionId != null');
|
|
112
|
+
}
|
|
110
113
|
// exclude deleted
|
|
111
114
|
where.push('IsDeprecated = false');
|
|
112
115
|
return where;
|
|
@@ -14,7 +14,7 @@ const core_1 = require("@salesforce/core");
|
|
|
14
14
|
const pkgUtils = require("../utils/packageUtils");
|
|
15
15
|
const QUERY = 'SELECT Package2Id, SubscriberPackageVersionId, Name, Description, Tag, Branch, AncestorId, ValidationSkipped, ' +
|
|
16
16
|
'MajorVersion, MinorVersion, PatchVersion, BuildNumber, IsReleased, CodeCoverage, HasPassedCodeCoverageCheck, ' +
|
|
17
|
-
'Package2.IsOrgDependent, ReleaseVersion, BuildDurationInSeconds, HasMetadataRemoved, CreatedById ' +
|
|
17
|
+
'Package2.IsOrgDependent, ReleaseVersion, BuildDurationInSeconds, HasMetadataRemoved, CreatedById, ConvertedFromVersionId ' +
|
|
18
18
|
'FROM Package2Version ' +
|
|
19
19
|
"WHERE Id = '%s' AND IsDeprecated != true " +
|
|
20
20
|
'ORDER BY Package2Id, Branch, MajorVersion, MinorVersion, PatchVersion, BuildNumber';
|
|
@@ -194,3 +194,15 @@ The version number is required and was not found in the options or in package js
|
|
|
194
194
|
# missingConnection
|
|
195
195
|
|
|
196
196
|
A connection is required.
|
|
197
|
+
|
|
198
|
+
# IdUnavailableWhenQueued
|
|
199
|
+
|
|
200
|
+
Request is queued. ID unavailable.
|
|
201
|
+
|
|
202
|
+
# IdUnavailableWhenInProgress
|
|
203
|
+
|
|
204
|
+
Request is in progress. ID unavailable.
|
|
205
|
+
|
|
206
|
+
# IdUnavailableWhenError
|
|
207
|
+
|
|
208
|
+
ID Unavailable
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/packaging",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Packaging library for the Salesforce packaging platform",
|
|
5
5
|
"main": "lib/exported",
|
|
6
6
|
"types": "lib/exported.d.ts",
|
|
@@ -42,37 +42,44 @@
|
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@oclif/core": "^2.9.4",
|
|
45
|
-
"@salesforce/core": "^4.3.
|
|
46
|
-
"@salesforce/kit": "^3.0.
|
|
45
|
+
"@salesforce/core": "^4.3.2",
|
|
46
|
+
"@salesforce/kit": "^3.0.3",
|
|
47
47
|
"@salesforce/schemas": "^1.6.0",
|
|
48
|
-
"@salesforce/source-deploy-retrieve": "^
|
|
48
|
+
"@salesforce/source-deploy-retrieve": "^8.6.0",
|
|
49
49
|
"@salesforce/ts-types": "^2.0.3",
|
|
50
|
-
"
|
|
50
|
+
"@xmldom/xmldom": "^0.8.9",
|
|
51
|
+
"debug": "^4.3.4",
|
|
51
52
|
"globby": "^11",
|
|
52
53
|
"graphology": "^0.25.1",
|
|
53
54
|
"graphology-traversal": "^0.3.1",
|
|
54
55
|
"graphology-types": "^0.24.7",
|
|
55
|
-
"
|
|
56
|
-
"
|
|
56
|
+
"js2xmlparser": "^4.0.2",
|
|
57
|
+
"jsforce": "^2.0.0-beta.23",
|
|
58
|
+
"jszip": "^3.10.1",
|
|
59
|
+
"ts-retry-promise": "^0.7.0",
|
|
60
|
+
"xml2js": "^0.6.0"
|
|
57
61
|
},
|
|
58
62
|
"devDependencies": {
|
|
59
63
|
"@salesforce/cli-plugins-testkit": "^4.1.1",
|
|
60
64
|
"@salesforce/dev-config": "^4.0.1",
|
|
61
65
|
"@salesforce/dev-scripts": "^5.4.3",
|
|
62
66
|
"@salesforce/prettier-config": "^0.0.3",
|
|
63
|
-
"@salesforce/ts-sinon": "^1.4.
|
|
67
|
+
"@salesforce/ts-sinon": "^1.4.12",
|
|
68
|
+
"@types/debug": "4.1.8",
|
|
64
69
|
"@types/globby": "^9.1.0",
|
|
65
70
|
"@types/jszip": "^3.4.1",
|
|
71
|
+
"@types/xml2js": "^0.4.11",
|
|
66
72
|
"@typescript-eslint/eslint-plugin": "^5.60.0",
|
|
67
73
|
"@typescript-eslint/parser": "^5.61.0",
|
|
68
74
|
"chai": "^4.3.7",
|
|
75
|
+
"commitizen": "^4.2.6",
|
|
69
76
|
"eslint": "^8.44.0",
|
|
70
77
|
"eslint-config-prettier": "^8.8.0",
|
|
71
78
|
"eslint-config-salesforce": "^2.0.1",
|
|
72
79
|
"eslint-config-salesforce-license": "^0.2.0",
|
|
73
80
|
"eslint-config-salesforce-typescript": "^1.1.1",
|
|
74
81
|
"eslint-plugin-header": "3.1.1",
|
|
75
|
-
"eslint-plugin-import": "^2.
|
|
82
|
+
"eslint-plugin-import": "^2.28.0",
|
|
76
83
|
"eslint-plugin-jsdoc": "^44.2.7",
|
|
77
84
|
"eslint-plugin-sf-plugin": "^1.15.9",
|
|
78
85
|
"husky": "^8.0.3",
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { Profile } from 'jsforce/api/metadata';
|
|
2
|
-
import { PackageXml } from '../interfaces';
|
|
3
|
-
type ProfileCustomSettingAccess = {
|
|
4
|
-
name: string;
|
|
5
|
-
enabled: boolean;
|
|
6
|
-
};
|
|
7
|
-
export type CorrectedProfile = Profile & {
|
|
8
|
-
customSettingAccesses: ProfileCustomSettingAccess[];
|
|
9
|
-
};
|
|
10
|
-
/**
|
|
11
|
-
*
|
|
12
|
-
* Takes a Profile that's been converted from package.xml to json.
|
|
13
|
-
* Filters out all Profile props that are not
|
|
14
|
-
* 1. used by packaging (ex: ipRanges)
|
|
15
|
-
* 2. present in the package.xml (ex: ClassAccesses for a class not in the package)
|
|
16
|
-
* 3. optionally retains the UserLicense prop only if the param is true
|
|
17
|
-
*
|
|
18
|
-
* @param profileJson json representation of a profile
|
|
19
|
-
* @param packageXml package.xml as json
|
|
20
|
-
* @param retainUserLicense boolean will preserve userLicense if true
|
|
21
|
-
* @returns Profile
|
|
22
|
-
*/
|
|
23
|
-
export declare const profileRewriter: (profileJson: CorrectedProfile, packageXml: PackageMap, retainUserLicense?: boolean) => CorrectedProfile;
|
|
24
|
-
export declare const fieldCorrections: (fieldName: string) => string;
|
|
25
|
-
/**
|
|
26
|
-
* @param profileString: raw xml read from the file
|
|
27
|
-
* @returns CorrectedProfile (json representation of the profile)
|
|
28
|
-
*/
|
|
29
|
-
export declare const profileStringToProfile: (profileString: string) => CorrectedProfile;
|
|
30
|
-
/** pass in an object that has the Profile props at the top level.
|
|
31
|
-
* This function will add the outer wrapper `Profile` and convert the result to xml
|
|
32
|
-
* */
|
|
33
|
-
export declare const profileObjectToString: (profileObject: Partial<CorrectedProfile>) => string;
|
|
34
|
-
/** it's easier to do lookups by Metadata Type on a Map */
|
|
35
|
-
export declare const manifestTypesToMap: (original: PackageXml['types']) => PackageMap;
|
|
36
|
-
type PackageMap = Map<string, string[]>;
|
|
37
|
-
export {};
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.manifestTypesToMap = exports.profileObjectToString = exports.profileStringToProfile = exports.fieldCorrections = exports.profileRewriter = void 0;
|
|
4
|
-
const fast_xml_parser_1 = require("fast-xml-parser");
|
|
5
|
-
/**
|
|
6
|
-
*
|
|
7
|
-
* Takes a Profile that's been converted from package.xml to json.
|
|
8
|
-
* Filters out all Profile props that are not
|
|
9
|
-
* 1. used by packaging (ex: ipRanges)
|
|
10
|
-
* 2. present in the package.xml (ex: ClassAccesses for a class not in the package)
|
|
11
|
-
* 3. optionally retains the UserLicense prop only if the param is true
|
|
12
|
-
*
|
|
13
|
-
* @param profileJson json representation of a profile
|
|
14
|
-
* @param packageXml package.xml as json
|
|
15
|
-
* @param retainUserLicense boolean will preserve userLicense if true
|
|
16
|
-
* @returns Profile
|
|
17
|
-
*/
|
|
18
|
-
const profileRewriter = (profileJson, packageXml, retainUserLicense = false) => ({
|
|
19
|
-
...Object.fromEntries(Object.entries(profileJson)
|
|
20
|
-
// remove settings that are not used for packaging
|
|
21
|
-
.filter(isRewriteProp)
|
|
22
|
-
// @ts-expect-error the previous filter restricts us to only things that appear in filterFunctions
|
|
23
|
-
.map(([key, value]) => [key, filterFunctions[key]?.(value, packageXml)] ?? [])
|
|
24
|
-
// some profileSettings might now be empty Arrays if the package.xml didn't have those types, so remove the entire property
|
|
25
|
-
.filter(([, value]) => (Array.isArray(value) ? value.length : true))),
|
|
26
|
-
// this one prop is controlled by a param. Put it back the way it was if the param is true
|
|
27
|
-
...(retainUserLicense && profileJson.userLicense ? { userLicense: profileJson.userLicense } : {}),
|
|
28
|
-
});
|
|
29
|
-
exports.profileRewriter = profileRewriter;
|
|
30
|
-
// it's both a filter and a typeguard to make sure props are represented in filterFunctions
|
|
31
|
-
const isRewriteProp = (prop) => rewriteProps.includes(prop[0]);
|
|
32
|
-
const rewriteProps = [
|
|
33
|
-
'objectPermissions',
|
|
34
|
-
'fieldPermissions',
|
|
35
|
-
'layoutAssignments',
|
|
36
|
-
'applicationVisibilities',
|
|
37
|
-
'classAccesses',
|
|
38
|
-
'externalDataSourceAccesses',
|
|
39
|
-
'tabVisibilities',
|
|
40
|
-
'pageAccesses',
|
|
41
|
-
'customPermissions',
|
|
42
|
-
'customMetadataTypeAccesses',
|
|
43
|
-
'customSettingAccesses',
|
|
44
|
-
'recordTypeVisibilities',
|
|
45
|
-
];
|
|
46
|
-
const filterFunctions = {
|
|
47
|
-
objectPermissions: (props, packageXml) => props.filter((item) => packageXml.get('CustomObject')?.includes(item.object)),
|
|
48
|
-
fieldPermissions: (props, packageXml) => props.filter((item) => packageXml.get('CustomField')?.includes((0, exports.fieldCorrections)(item.field))),
|
|
49
|
-
layoutAssignments: (props, packageXml) => props.filter((item) => packageXml.get('Layout')?.includes(item.layout)),
|
|
50
|
-
tabVisibilities: (props, packageXml) => props.filter((item) => packageXml.get('CustomTab')?.includes(item.tab)),
|
|
51
|
-
applicationVisibilities: (props, packageXml) => props.filter((item) => packageXml.get('Application')?.includes(item.application)),
|
|
52
|
-
classAccesses: (props, packageXml) => props.filter((item) => packageXml.get('ApexClass')?.includes(item.apexClass)),
|
|
53
|
-
customPermissions: (props, packageXml) => props.filter((item) => packageXml.get('CustomPermission')?.includes(item.name)),
|
|
54
|
-
pageAccesses: (props, packageXml) => props.filter((item) => packageXml.get('ApexPage')?.includes(item.apexPage)),
|
|
55
|
-
externalDataSourceAccesses: (props, packageXml) => props.filter((item) => packageXml.get('ExternalDataSource')?.includes(item.externalDataSource)),
|
|
56
|
-
recordTypeVisibilities: (props, packageXml) => props.filter((item) => packageXml.get('RecordType')?.includes(item.recordType)),
|
|
57
|
-
customSettingAccesses: (props, packageXml) => props.filter((item) => allMembers(packageXml).includes(item.name)),
|
|
58
|
-
customMetadataTypeAccesses: (props, packageXml) => props.filter((item) => allMembers(packageXml).includes(item.name)),
|
|
59
|
-
};
|
|
60
|
-
const allMembers = (packageXml) => Array.from(packageXml.values()).flat();
|
|
61
|
-
// github.com/forcedotcom/cli/issues/2278
|
|
62
|
-
// Activity Object is polymorphic (Task and Event)
|
|
63
|
-
// package.xml will display them as 'Activity'
|
|
64
|
-
// profile.fieldPermissions will display them with the more specific 'Task' or 'Event'
|
|
65
|
-
const fieldCorrections = (fieldName) => fieldName.replace(/^Event\./, 'Activity.').replace(/^Task\./, 'Activity.');
|
|
66
|
-
exports.fieldCorrections = fieldCorrections;
|
|
67
|
-
/**
|
|
68
|
-
* @param profileString: raw xml read from the file
|
|
69
|
-
* @returns CorrectedProfile (json representation of the profile)
|
|
70
|
-
*/
|
|
71
|
-
const profileStringToProfile = (profileString) => {
|
|
72
|
-
const parser = new fast_xml_parser_1.XMLParser({
|
|
73
|
-
ignoreAttributes: true,
|
|
74
|
-
parseTagValue: false,
|
|
75
|
-
parseAttributeValue: false,
|
|
76
|
-
cdataPropName: '__cdata',
|
|
77
|
-
ignoreDeclaration: true,
|
|
78
|
-
numberParseOptions: { leadingZeros: false, hex: false },
|
|
79
|
-
isArray: (name) => rewriteProps.includes(name),
|
|
80
|
-
});
|
|
81
|
-
return parser.parse(profileString).Profile;
|
|
82
|
-
};
|
|
83
|
-
exports.profileStringToProfile = profileStringToProfile;
|
|
84
|
-
/** pass in an object that has the Profile props at the top level.
|
|
85
|
-
* This function will add the outer wrapper `Profile` and convert the result to xml
|
|
86
|
-
* */
|
|
87
|
-
const profileObjectToString = (profileObject) => {
|
|
88
|
-
const builder = new fast_xml_parser_1.XMLBuilder({
|
|
89
|
-
format: true,
|
|
90
|
-
indentBy: ' ',
|
|
91
|
-
ignoreAttributes: false,
|
|
92
|
-
cdataPropName: '__cdata',
|
|
93
|
-
processEntities: false,
|
|
94
|
-
attributeNamePrefix: '@@@',
|
|
95
|
-
});
|
|
96
|
-
return String(builder.build({
|
|
97
|
-
'?xml': {
|
|
98
|
-
'@@@version': '1.0',
|
|
99
|
-
'@@@encoding': 'UTF-8',
|
|
100
|
-
},
|
|
101
|
-
Profile: { ...profileObject, '@@@xmlns': 'http://soap.sforce.com/2006/04/metadata' },
|
|
102
|
-
}));
|
|
103
|
-
};
|
|
104
|
-
exports.profileObjectToString = profileObjectToString;
|
|
105
|
-
/** it's easier to do lookups by Metadata Type on a Map */
|
|
106
|
-
const manifestTypesToMap = (original) => new Map(original.map((item) => [item.name, item.members]));
|
|
107
|
-
exports.manifestTypesToMap = manifestTypesToMap;
|
|
108
|
-
//# sourceMappingURL=profileRewriter.js.map
|