sfdx-easy-sources 0.9.1 → 0.9.3

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.
Files changed (30) hide show
  1. package/README.md +44 -2
  2. package/lib/api/permissionsets.d.ts +19 -4
  3. package/lib/api/permissionsets.js +19 -3
  4. package/lib/api/permissionsets.js.map +1 -1
  5. package/lib/api/profiles.d.ts +16 -1
  6. package/lib/api/profiles.js +16 -0
  7. package/lib/api/profiles.js.map +1 -1
  8. package/lib/commands/easysources/objecttranslations/arealigned.d.ts +3 -3
  9. package/lib/commands/easysources/objecttranslations/arealigned.js +71 -113
  10. package/lib/commands/easysources/objecttranslations/arealigned.js.map +1 -1
  11. package/lib/commands/easysources/permissionsets/customupsert.d.ts +25 -0
  12. package/lib/commands/easysources/permissionsets/customupsert.js +70 -0
  13. package/lib/commands/easysources/permissionsets/customupsert.js.map +1 -0
  14. package/lib/commands/easysources/profiles/customupsert.d.ts +18 -0
  15. package/lib/commands/easysources/profiles/customupsert.js +64 -0
  16. package/lib/commands/easysources/profiles/customupsert.js.map +1 -0
  17. package/lib/commands/easysources/recordtypes/arealigned.js +40 -76
  18. package/lib/commands/easysources/recordtypes/arealigned.js.map +1 -1
  19. package/lib/utils/commands/alignmentChecker.d.ts +6 -3
  20. package/lib/utils/commands/alignmentChecker.js +57 -59
  21. package/lib/utils/commands/alignmentChecker.js.map +1 -1
  22. package/lib/utils/commands/customupserter.d.ts +10 -0
  23. package/lib/utils/commands/customupserter.js +152 -0
  24. package/lib/utils/commands/customupserter.js.map +1 -0
  25. package/lib/utils/utils_objtransl.js +1 -33
  26. package/lib/utils/utils_objtransl.js.map +1 -1
  27. package/messages/permissionsets_customupsert.json +15 -0
  28. package/messages/profiles_customupsert.json +15 -0
  29. package/oclif.manifest.json +1 -1
  30. package/package.json +1 -1
package/README.md CHANGED
@@ -28,8 +28,8 @@ With this plugin you can:
28
28
  | Metadata Label| Metadata api | Available commands | Programmatic API |
29
29
  | :---: | :---: | :---: | :---: |
30
30
  | All Meta | allmeta | split, upsert, merge, minify, retrieve | ❌ |
31
- | Profiles | profiles | split, upsert, merge, minify, updatekey, delete, clean, clearempty, arealigned | ✅ **Complete** |
32
- | Permission Sets | permissionsets | split, upsert, merge, minify, updatekey, delete, clean, clearempty, arealigned | ✅ **Complete** |
31
+ | Profiles | profiles | split, upsert, merge, minify, updatekey, delete, customupsert, clean, clearempty, arealigned | ✅ **Complete** |
32
+ | Permission Sets | permissionsets | split, upsert, merge, minify, updatekey, delete, customupsert, clean, clearempty, arealigned | ✅ **Complete** |
33
33
  | Record Types | recordtypes | split, upsert, merge, updatekey, delete, clean, arealigned | ✅ **Complete** |
34
34
  | Labels | labels | split, upsert, merge, updatekey, arealigned | ✅ **Complete** |
35
35
  | Global Value Sets | globalvaluesets | split, upsert, merge, updatekey, arealigned | ✅ **Complete** |
@@ -68,8 +68,18 @@ async function automateMetadata() {
68
68
  await profiles.upsert({ input: 'Admin' });
69
69
  await profiles.merge({ input: 'Admin' });
70
70
 
71
+ // Custom upsert with JSON content
72
+ await profiles.customUpsert({
73
+ type: 'classAccesses',
74
+ content: { apexClass: 'MyClass', enabled: true }
75
+ });
76
+
71
77
  // Same pattern for all metadata types
72
78
  await permissionsets.split();
79
+ await permissionsets.customUpsert({
80
+ type: 'objectPermissions',
81
+ content: { object: 'Account', allowRead: true, allowEdit: true }
82
+ });
73
83
  await labels.upsert();
74
84
  await objectTranslations.minify();
75
85
  }
@@ -99,6 +109,7 @@ Based on the source type, this plugin provides the following commands:
99
109
  - **Split**: Splits the resources into various CSV files, and creates an XML file containing all the tags that weren't split
100
110
  - **Merge**: Merges back all the resources previously split from CSV files into XML format
101
111
  - **Upsert**: Similar to split, but adds new entries to existing CSV files instead of recreating them
112
+ - **CustomUpsert**: Inserts or updates specific entries in CSV files using JSON content, with automatic key calculation and smart field handling
102
113
  - **Updatekey**: Updates the `_tagid` column when developers make changes directly in CSV files
103
114
  - **Delete**: Bulk deletes a single permission from all resources of the same type (only applies to Profiles, PermissionSets and Record Types)
104
115
  - **Minify**: Removes entries that don't add value to the file (available for profiles, permissionsets, objecttranslations, and translations)
@@ -209,6 +220,37 @@ Another scenario could be during a cherry pick or a merge conflict. Suppose a de
209
220
  Suppose the developer deletes a field on the org, he needs to delete all the references for that field for all Profiles, PermissionSets and RecordTypes.
210
221
  This command is intended to delete references, and it has flags to specify the name of the field. Run with --help flag to get a better description of the possible flags for each metadata type.
211
222
 
223
+ ### CustomUpsert
224
+ **Note: only applies to Profiles and PermissionSets**
225
+
226
+ The customupsert command allows you to insert or update specific entries in CSV files using JSON content, providing a powerful way to programmatically manage permissions without manually editing CSV files.
227
+
228
+ **Key Features:**
229
+ - **JSON Input**: Pass structured data directly via the `--content` flag (single object or array)
230
+ - **Smart Field Handling**:
231
+ - Non-existent keys in the JSON are automatically ignored
232
+ - Omitted existing keys result in empty values in the CSV
233
+ - **Automatic Key Calculation**: The `_tagid` is automatically calculated based on the metadata type configuration
234
+ - **Bulk Operations**: Process multiple entries at once by passing an array
235
+ - **Target Specific Items**: Use `--input` to target specific profiles/permission sets, or omit to process all
236
+
237
+ **Examples:**
238
+ ```sh-session
239
+ # Insert/update a single class access permission
240
+ $ sf easysources profiles customupsert -t classAccesses -j '{"apexClass":"MyClass","enabled":true}'
241
+
242
+ # Insert/update multiple entries at once
243
+ $ sf easysources profiles customupsert -t classAccesses -j '[{"apexClass":"Class1","enabled":true},{"apexClass":"Class2","enabled":false}]'
244
+
245
+ # Update field permissions for a specific profile
246
+ $ sf easysources profiles customupsert -i Admin -t fieldPermissions -j '{"field":"Account.CustomField__c","editable":true,"readable":true}'
247
+
248
+ # Update object permissions for all permission sets
249
+ $ sf easysources permissionsets customupsert -t objectPermissions -j '{"object":"CustomObject__c","allowRead":true,"allowEdit":true}'
250
+ ```
251
+
252
+ This command is particularly useful for automation scripts, CI/CD pipelines, or when you need to apply the same permission changes across multiple profiles or permission sets programmatically.
253
+
212
254
  ### Minify
213
255
 
214
256
  This command is very useful if you don't want to have in your csv files all the lines that don't add value to the file. The minify command is available for **profiles**, **permissionsets**, **objecttranslations**, and **translations**.
@@ -24,10 +24,12 @@ export interface PermissionSetOptions extends PathOptions {
24
24
  'include-types'?: string;
25
25
  /** Log directory path (clean only) */
26
26
  'log-dir'?: string;
27
- /** Specific permission type to target (upsert, delete only) */
27
+ /** Specific permission type to target (upsert, delete, customUpsert only) */
28
28
  type?: string;
29
29
  /** Specific tag ID to target (upsert, delete only) */
30
30
  tagid?: string;
31
+ /** JSON content to insert/update (customUpsert only) - can be object or JSON string */
32
+ content?: string | object | object[];
31
33
  }
32
34
  /**
33
35
  * Permission Sets namespace containing all programmatic API functions for Salesforce Permission Set metadata operations.
@@ -44,20 +46,27 @@ export interface PermissionSetOptions extends PathOptions {
44
46
  * - updateKey(): Updates permission set keys in CSV files
45
47
  * - minify(): Removes entries with only false permissions
46
48
  * - delete(): Deletes specific entries from CSV files
49
+ * - customUpsert(): Inserts or updates entries via JSON content
47
50
  * - clean(): Removes references to non-existent metadata
48
51
  *
49
52
  * @example
50
53
  * ```javascript
51
- * const { permissionsets } = require('sfdx-easy-sources');
54
+ * const { permissionSets } = require('sfdx-easy-sources');
52
55
  *
53
56
  * // Using default settings from easysources-settings.json
54
- * await permissionsets.split({ input: 'MyPermSet' });
57
+ * await permissionSets.split({ input: 'MyPermSet' });
55
58
  *
56
59
  * // Override specific paths
57
- * await permissionsets.upsert({
60
+ * await permissionSets.upsert({
58
61
  * 'sf-xml': './custom-metadata-path',
59
62
  * 'es-csv': './custom-csv-path'
60
63
  * });
64
+ *
65
+ * // Custom upsert with JSON content
66
+ * await permissionSets.customUpsert({
67
+ * type: 'objectPermissions',
68
+ * content: { object: 'Account', allowRead: true, allowEdit: true }
69
+ * });
61
70
  * ```
62
71
  */
63
72
  export declare const permissionSets: {
@@ -109,6 +118,12 @@ export declare const permissionSets: {
109
118
  * @returns Promise resolving to operation result
110
119
  */
111
120
  delete: (options: PermissionSetOptions) => Promise<AnyJson>;
121
+ /**
122
+ * Inserts or updates specific entries in permission set CSV files using JSON content.
123
+ * @param options Configuration options for the custom upsert operation
124
+ * @returns Promise resolving to operation result
125
+ */
126
+ customUpsert: (options: PermissionSetOptions) => Promise<AnyJson>;
112
127
  /**
113
128
  * Cleans permission set CSV files by removing references to non-existent metadata.
114
129
  * @param options Optional configuration, automatically resolved from settings if not provided
@@ -11,6 +11,7 @@ const clearempty_1 = require("../commands/easysources/permissionsets/clearempty"
11
11
  const minify_1 = require("../commands/easysources/permissionsets/minify");
12
12
  const delete_1 = require("../commands/easysources/permissionsets/delete");
13
13
  const clean_1 = require("../commands/easysources/permissionsets/clean");
14
+ const customupsert_1 = require("../commands/easysources/permissionsets/customupsert");
14
15
  /**
15
16
  * Permission Sets namespace containing all programmatic API functions for Salesforce Permission Set metadata operations.
16
17
  *
@@ -26,20 +27,27 @@ const clean_1 = require("../commands/easysources/permissionsets/clean");
26
27
  * - updateKey(): Updates permission set keys in CSV files
27
28
  * - minify(): Removes entries with only false permissions
28
29
  * - delete(): Deletes specific entries from CSV files
30
+ * - customUpsert(): Inserts or updates entries via JSON content
29
31
  * - clean(): Removes references to non-existent metadata
30
32
  *
31
33
  * @example
32
34
  * ```javascript
33
- * const { permissionsets } = require('sfdx-easy-sources');
35
+ * const { permissionSets } = require('sfdx-easy-sources');
34
36
  *
35
37
  * // Using default settings from easysources-settings.json
36
- * await permissionsets.split({ input: 'MyPermSet' });
38
+ * await permissionSets.split({ input: 'MyPermSet' });
37
39
  *
38
40
  * // Override specific paths
39
- * await permissionsets.upsert({
41
+ * await permissionSets.upsert({
40
42
  * 'sf-xml': './custom-metadata-path',
41
43
  * 'es-csv': './custom-csv-path'
42
44
  * });
45
+ *
46
+ * // Custom upsert with JSON content
47
+ * await permissionSets.customUpsert({
48
+ * type: 'objectPermissions',
49
+ * content: { object: 'Account', allowRead: true, allowEdit: true }
50
+ * });
43
51
  * ```
44
52
  */
45
53
  exports.permissionSets = {
@@ -107,6 +115,14 @@ exports.permissionSets = {
107
115
  delete: async (options) => {
108
116
  return await (0, delete_1.permissionsetDelete)(options);
109
117
  },
118
+ /**
119
+ * Inserts or updates specific entries in permission set CSV files using JSON content.
120
+ * @param options Configuration options for the custom upsert operation
121
+ * @returns Promise resolving to operation result
122
+ */
123
+ customUpsert: async (options) => {
124
+ return await (0, customupsert_1.permissionsetCustomUpsert)(options);
125
+ },
110
126
  /**
111
127
  * Cleans permission set CSV files by removing references to non-existent metadata.
112
128
  * @param options Optional configuration, automatically resolved from settings if not provided
@@ -1 +1 @@
1
- {"version":3,"file":"permissionsets.js","sourceRoot":"","sources":["../../src/api/permissionsets.ts"],"names":[],"mappings":";;;AAGA,uEAAuE;AACvE,wEAAkF;AAClF,0EAAoF;AACpF,wEAAkF;AAClF,gFAA0F;AAC1F,kFAA4F;AAC5F,kFAA4F;AAC5F,0EAAoF;AACpF,0EAAoF;AACpF,wEAAkF;AAmClF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACU,QAAA,cAAc,GAAG;IAC5B;;;;OAIG;IACH,KAAK,EAAE,KAAK,EAAE,UAAgC,EAAE,EAAoB,EAAE;QACpE,OAAO,MAAM,IAAA,0BAAkB,EAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED;;;;OAIG;IACH,MAAM,EAAE,KAAK,EAAE,UAAgC,EAAE,EAAoB,EAAE;QACrE,OAAO,MAAM,IAAA,4BAAmB,EAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED;;;;OAIG;IACH,KAAK,EAAE,KAAK,EAAE,UAAgC,EAAE,EAAoB,EAAE;QACpE,OAAO,MAAM,IAAA,0BAAkB,EAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED;;;;OAIG;IACH,UAAU,EAAE,KAAK,EAAE,UAAgC,EAAE,EAAoB,EAAE;QACzE,OAAO,MAAM,IAAA,oCAAuB,EAAC,OAAO,CAAC,CAAC;IAChD,CAAC;IAED;;;;OAIG;IACH,UAAU,EAAE,KAAK,EAAE,UAAgC,EAAE,EAAoB,EAAE;QACzE,OAAO,MAAM,IAAA,oCAAuB,EAAC,OAAO,CAAC,CAAC;IAChD,CAAC;IAED;;;;OAIG;IACH,SAAS,EAAE,KAAK,EAAE,OAA6B,EAAoB,EAAE;QACnE,OAAO,MAAM,IAAA,kCAAsB,EAAC,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED;;;;OAIG;IACH,MAAM,EAAE,KAAK,EAAE,UAAgC,EAAE,EAAoB,EAAE;QACrE,OAAO,MAAM,IAAA,4BAAmB,EAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED;;;;OAIG;IACH,MAAM,EAAE,KAAK,EAAE,OAA6B,EAAoB,EAAE;QAChE,OAAO,MAAM,IAAA,4BAAmB,EAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED;;;;OAIG;IACH,KAAK,EAAE,KAAK,EAAE,UAAgC,EAAE,EAAoB,EAAE;QACpE,OAAO,MAAM,IAAA,0BAAkB,EAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"permissionsets.js","sourceRoot":"","sources":["../../src/api/permissionsets.ts"],"names":[],"mappings":";;;AAGA,uEAAuE;AACvE,wEAAkF;AAClF,0EAAoF;AACpF,wEAAkF;AAClF,gFAA0F;AAC1F,kFAA4F;AAC5F,kFAA4F;AAC5F,0EAAoF;AACpF,0EAAoF;AACpF,wEAAkF;AAClF,sFAAgG;AAqChG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACU,QAAA,cAAc,GAAG;IAC5B;;;;OAIG;IACH,KAAK,EAAE,KAAK,EAAE,UAAgC,EAAE,EAAoB,EAAE;QACpE,OAAO,MAAM,IAAA,0BAAkB,EAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED;;;;OAIG;IACH,MAAM,EAAE,KAAK,EAAE,UAAgC,EAAE,EAAoB,EAAE;QACrE,OAAO,MAAM,IAAA,4BAAmB,EAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED;;;;OAIG;IACH,KAAK,EAAE,KAAK,EAAE,UAAgC,EAAE,EAAoB,EAAE;QACpE,OAAO,MAAM,IAAA,0BAAkB,EAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED;;;;OAIG;IACH,UAAU,EAAE,KAAK,EAAE,UAAgC,EAAE,EAAoB,EAAE;QACzE,OAAO,MAAM,IAAA,oCAAuB,EAAC,OAAO,CAAC,CAAC;IAChD,CAAC;IAED;;;;OAIG;IACH,UAAU,EAAE,KAAK,EAAE,UAAgC,EAAE,EAAoB,EAAE;QACzE,OAAO,MAAM,IAAA,oCAAuB,EAAC,OAAO,CAAC,CAAC;IAChD,CAAC;IAED;;;;OAIG;IACH,SAAS,EAAE,KAAK,EAAE,OAA6B,EAAoB,EAAE;QACnE,OAAO,MAAM,IAAA,kCAAsB,EAAC,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED;;;;OAIG;IACH,MAAM,EAAE,KAAK,EAAE,UAAgC,EAAE,EAAoB,EAAE;QACrE,OAAO,MAAM,IAAA,4BAAmB,EAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED;;;;OAIG;IACH,MAAM,EAAE,KAAK,EAAE,OAA6B,EAAoB,EAAE;QAChE,OAAO,MAAM,IAAA,4BAAmB,EAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED;;;;OAIG;IACH,YAAY,EAAE,KAAK,EAAE,OAA6B,EAAoB,EAAE;QACtE,OAAO,MAAM,IAAA,wCAAyB,EAAC,OAAO,CAAC,CAAC;IAClD,CAAC;IAED;;;;OAIG;IACH,KAAK,EAAE,KAAK,EAAE,UAAgC,EAAE,EAAoB,EAAE;QACpE,OAAO,MAAM,IAAA,0BAAkB,EAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;CACF,CAAC"}
@@ -26,10 +26,12 @@ export interface ProfileOptions extends PathOptions {
26
26
  'include-types'?: string;
27
27
  /** Log directory path (clean only) */
28
28
  'log-dir'?: string;
29
- /** Specific permission type to target (upsert, delete only) */
29
+ /** Specific permission type to target (upsert, delete, customUpsert only) */
30
30
  type?: string;
31
31
  /** Specific tag ID to target (upsert, delete only) */
32
32
  tagid?: string;
33
+ /** JSON content to insert/update (customUpsert only) - can be object or JSON string */
34
+ content?: string | object | object[];
33
35
  }
34
36
  /**
35
37
  * Profiles namespace containing all programmatic API functions for Salesforce Profile metadata operations.
@@ -46,6 +48,7 @@ export interface ProfileOptions extends PathOptions {
46
48
  * - updateKey(): Updates profile keys in CSV files
47
49
  * - minify(): Removes entries with only false permissions
48
50
  * - delete(): Deletes specific entries from CSV files
51
+ * - customUpsert(): Inserts or updates entries via JSON content
49
52
  * - clean(): Removes references to non-existent metadata
50
53
  *
51
54
  * @example
@@ -60,6 +63,12 @@ export interface ProfileOptions extends PathOptions {
60
63
  * 'sf-xml': './custom-metadata-path',
61
64
  * 'es-csv': './custom-csv-path'
62
65
  * });
66
+ *
67
+ * // Custom upsert with JSON content
68
+ * await profiles.customUpsert({
69
+ * type: 'classAccesses',
70
+ * content: { apexClass: 'MyClass', enabled: true }
71
+ * });
63
72
  * ```
64
73
  */
65
74
  export declare const profiles: {
@@ -111,6 +120,12 @@ export declare const profiles: {
111
120
  * @returns Promise resolving to operation result
112
121
  */
113
122
  delete: (options: ProfileOptions) => Promise<AnyJson>;
123
+ /**
124
+ * Inserts or updates specific entries in profile CSV files using JSON content.
125
+ * @param options Configuration options for the custom upsert operation
126
+ * @returns Promise resolving to operation result
127
+ */
128
+ customUpsert: (options: ProfileOptions) => Promise<AnyJson>;
114
129
  /**
115
130
  * Cleans profile CSV files by removing references to non-existent metadata.
116
131
  * @param options Optional configuration, automatically resolved from settings if not provided
@@ -11,6 +11,7 @@ const clearempty_1 = require("../commands/easysources/profiles/clearempty");
11
11
  const minify_1 = require("../commands/easysources/profiles/minify");
12
12
  const delete_1 = require("../commands/easysources/profiles/delete");
13
13
  const clean_1 = require("../commands/easysources/profiles/clean");
14
+ const customupsert_1 = require("../commands/easysources/profiles/customupsert");
14
15
  /**
15
16
  * Profiles namespace containing all programmatic API functions for Salesforce Profile metadata operations.
16
17
  *
@@ -26,6 +27,7 @@ const clean_1 = require("../commands/easysources/profiles/clean");
26
27
  * - updateKey(): Updates profile keys in CSV files
27
28
  * - minify(): Removes entries with only false permissions
28
29
  * - delete(): Deletes specific entries from CSV files
30
+ * - customUpsert(): Inserts or updates entries via JSON content
29
31
  * - clean(): Removes references to non-existent metadata
30
32
  *
31
33
  * @example
@@ -40,6 +42,12 @@ const clean_1 = require("../commands/easysources/profiles/clean");
40
42
  * 'sf-xml': './custom-metadata-path',
41
43
  * 'es-csv': './custom-csv-path'
42
44
  * });
45
+ *
46
+ * // Custom upsert with JSON content
47
+ * await profiles.customUpsert({
48
+ * type: 'classAccesses',
49
+ * content: { apexClass: 'MyClass', enabled: true }
50
+ * });
43
51
  * ```
44
52
  */
45
53
  exports.profiles = {
@@ -107,6 +115,14 @@ exports.profiles = {
107
115
  delete: async (options) => {
108
116
  return await (0, delete_1.profileDelete)(options);
109
117
  },
118
+ /**
119
+ * Inserts or updates specific entries in profile CSV files using JSON content.
120
+ * @param options Configuration options for the custom upsert operation
121
+ * @returns Promise resolving to operation result
122
+ */
123
+ customUpsert: async (options) => {
124
+ return await (0, customupsert_1.profileCustomUpsert)(options);
125
+ },
110
126
  /**
111
127
  * Cleans profile CSV files by removing references to non-existent metadata.
112
128
  * @param options Optional configuration, automatically resolved from settings if not provided
@@ -1 +1 @@
1
- {"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../src/api/profiles.ts"],"names":[],"mappings":";;;AAGA,gEAAgE;AAChE,kEAAsE;AACtE,oEAAwE;AACxE,kEAAsE;AACtE,0EAA8E;AAC9E,4EAAgF;AAChF,4EAAgF;AAChF,oEAAwE;AACxE,oEAAwE;AACxE,kEAAsE;AAqCtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACU,QAAA,QAAQ,GAAG;IACtB;;;;OAIG;IACH,KAAK,EAAE,KAAK,EAAE,UAA0B,EAAE,EAAoB,EAAE;QAC9D,OAAO,MAAM,IAAA,oBAAY,EAAC,OAAO,CAAC,CAAC;IACrC,CAAC;IAED;;;;OAIG;IACH,MAAM,EAAE,KAAK,EAAE,UAA0B,EAAE,EAAoB,EAAE;QAC/D,OAAO,MAAM,IAAA,sBAAa,EAAC,OAAO,CAAC,CAAC;IACtC,CAAC;IAED;;;;OAIG;IACH,KAAK,EAAE,KAAK,EAAE,UAA0B,EAAE,EAAoB,EAAE;QAC9D,OAAO,MAAM,IAAA,oBAAY,EAAC,OAAO,CAAC,CAAC;IACrC,CAAC;IAED;;;;OAIG;IACH,UAAU,EAAE,KAAK,EAAE,UAA0B,EAAE,EAAoB,EAAE;QACnE,OAAO,MAAM,IAAA,8BAAiB,EAAC,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED;;;;OAIG;IACH,UAAU,EAAE,KAAK,EAAE,UAA0B,EAAE,EAAoB,EAAE;QACnE,OAAO,MAAM,IAAA,8BAAiB,EAAC,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED;;;;OAIG;IACH,SAAS,EAAE,KAAK,EAAE,OAAuB,EAAoB,EAAE;QAC7D,OAAO,MAAM,IAAA,4BAAgB,EAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IAED;;;;OAIG;IACH,MAAM,EAAE,KAAK,EAAE,UAA0B,EAAE,EAAoB,EAAE;QAC/D,OAAO,MAAM,IAAA,sBAAa,EAAC,OAAO,CAAC,CAAC;IACtC,CAAC;IAED;;;;OAIG;IACH,MAAM,EAAE,KAAK,EAAE,OAAuB,EAAoB,EAAE;QAC1D,OAAO,MAAM,IAAA,sBAAa,EAAC,OAAO,CAAC,CAAC;IACtC,CAAC;IAED;;;;OAIG;IACH,KAAK,EAAE,KAAK,EAAE,UAA0B,EAAE,EAAoB,EAAE;QAC9D,OAAO,MAAM,IAAA,oBAAY,EAAC,OAAO,CAAC,CAAC;IACrC,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../src/api/profiles.ts"],"names":[],"mappings":";;;AAGA,gEAAgE;AAChE,kEAAsE;AACtE,oEAAwE;AACxE,kEAAsE;AACtE,0EAA8E;AAC9E,4EAAgF;AAChF,4EAAgF;AAChF,oEAAwE;AACxE,oEAAwE;AACxE,kEAAsE;AACtE,gFAAoF;AAuCpF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACU,QAAA,QAAQ,GAAG;IACtB;;;;OAIG;IACH,KAAK,EAAE,KAAK,EAAE,UAA0B,EAAE,EAAoB,EAAE;QAC9D,OAAO,MAAM,IAAA,oBAAY,EAAC,OAAO,CAAC,CAAC;IACrC,CAAC;IAED;;;;OAIG;IACH,MAAM,EAAE,KAAK,EAAE,UAA0B,EAAE,EAAoB,EAAE;QAC/D,OAAO,MAAM,IAAA,sBAAa,EAAC,OAAO,CAAC,CAAC;IACtC,CAAC;IAED;;;;OAIG;IACH,KAAK,EAAE,KAAK,EAAE,UAA0B,EAAE,EAAoB,EAAE;QAC9D,OAAO,MAAM,IAAA,oBAAY,EAAC,OAAO,CAAC,CAAC;IACrC,CAAC;IAED;;;;OAIG;IACH,UAAU,EAAE,KAAK,EAAE,UAA0B,EAAE,EAAoB,EAAE;QACnE,OAAO,MAAM,IAAA,8BAAiB,EAAC,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED;;;;OAIG;IACH,UAAU,EAAE,KAAK,EAAE,UAA0B,EAAE,EAAoB,EAAE;QACnE,OAAO,MAAM,IAAA,8BAAiB,EAAC,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED;;;;OAIG;IACH,SAAS,EAAE,KAAK,EAAE,OAAuB,EAAoB,EAAE;QAC7D,OAAO,MAAM,IAAA,4BAAgB,EAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IAED;;;;OAIG;IACH,MAAM,EAAE,KAAK,EAAE,UAA0B,EAAE,EAAoB,EAAE;QAC/D,OAAO,MAAM,IAAA,sBAAa,EAAC,OAAO,CAAC,CAAC;IACtC,CAAC;IAED;;;;OAIG;IACH,MAAM,EAAE,KAAK,EAAE,OAAuB,EAAoB,EAAE;QAC1D,OAAO,MAAM,IAAA,sBAAa,EAAC,OAAO,CAAC,CAAC;IACtC,CAAC;IAED;;;;OAIG;IACH,YAAY,EAAE,KAAK,EAAE,OAAuB,EAAoB,EAAE;QAChE,OAAO,MAAM,IAAA,kCAAmB,EAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED;;;;OAIG;IACH,KAAK,EAAE,KAAK,EAAE,UAA0B,EAAE,EAAoB,EAAE;QAC9D,OAAO,MAAM,IAAA,oBAAY,EAAC,OAAO,CAAC,CAAC;IACrC,CAAC;CACF,CAAC"}
@@ -1,16 +1,16 @@
1
1
  import { flags, SfdxCommand } from '@salesforce/command';
2
2
  import { AnyJson } from '@salesforce/ts-types';
3
3
  interface ItemResult {
4
- result: 'OK' | 'KO' | 'WARN';
4
+ result: 'OK' | 'KO';
5
5
  error?: string;
6
6
  }
7
7
  interface ValidationSummary {
8
- result: 'OK';
8
+ result: 'OK' | 'KO';
9
+ error?: string;
9
10
  summary: {
10
11
  totalItems: number;
11
12
  alignedItems: number;
12
13
  misalignedItems: number;
13
- warningItems: number;
14
14
  };
15
15
  items: {
16
16
  [itemName: string]: ItemResult;
@@ -20,6 +20,7 @@ const localSettings_1 = require("../../../utils/localSettings");
20
20
  const os_1 = require("os");
21
21
  const merge_1 = require("./merge");
22
22
  const utils_objtransl_1 = require("../../../utils/utils_objtransl");
23
+ const alignmentChecker_1 = require("../../../utils/commands/alignmentChecker");
23
24
  const settings = (0, localSettings_1.loadSettings)();
24
25
  // Initialize Messages with the current plugin directory
25
26
  core_1.Messages.importMessagesDirectory(__dirname);
@@ -73,19 +74,21 @@ async function objectTranslationAreAligned(options = {}) {
73
74
  if (!fs.existsSync(baseXmlDir)) {
74
75
  console.log(`Missing XML directory: ${baseXmlDir}`);
75
76
  return {
76
- result: 'OK',
77
- summary: { totalItems: 0, alignedItems: 0, misalignedItems: 0, warningItems: 0 },
78
- items: {}
79
- };
80
- }
81
- if (!fs.existsSync(baseCsvDir)) {
82
- console.log(`Missing CSV directory: ${baseCsvDir}`);
83
- return {
84
- result: 'OK',
85
- summary: { totalItems: 0, alignedItems: 0, misalignedItems: 0, warningItems: 0 },
77
+ result: 'KO',
78
+ error: `Missing XML directory: ${baseXmlDir}`,
79
+ summary: { totalItems: 0, alignedItems: 0, misalignedItems: 0 },
86
80
  items: {}
87
81
  };
88
82
  }
83
+ // if (!fs.existsSync(baseCsvDir)) {
84
+ // console.log(`Missing CSV directory: ${baseCsvDir}`);
85
+ // return {
86
+ // result: 'KO',
87
+ // error: `Missing CSV directory: ${baseCsvDir}`,
88
+ // summary: { totalItems: 0, alignedItems: 0, misalignedItems: 0 },
89
+ // items: {}
90
+ // };
91
+ // }
89
92
  var objectList = [];
90
93
  if (inputObjects) {
91
94
  objectList = inputObjects.split(',');
@@ -98,14 +101,53 @@ async function objectTranslationAreAligned(options = {}) {
98
101
  }
99
102
  const items = {};
100
103
  let alignedCount = 0;
101
- let warningCount = 0;
102
104
  for (const objectName of objectList) {
105
+ const xmlFilePath = (0, path_1.join)(baseXmlDir, objectName, objectName + constants_objecttranslations_1.OBJTRANSL_EXTENSION);
106
+ const xmlDir = (0, path_1.join)(baseXmlDir, objectName);
107
+ const objectCsvDir = (0, path_1.join)(baseCsvDir, objectName, 'csv');
108
+ // Check if XML file exists
109
+ if (!fs.existsSync(xmlFilePath)) {
110
+ items[objectName] = {
111
+ result: 'KO',
112
+ error: `XML file not found: ${xmlFilePath}`
113
+ };
114
+ console.log(`❌ Object translation '${objectName}' has misalignment:`);
115
+ console.log(` - XML file not found: ${xmlFilePath}`);
116
+ continue;
117
+ }
118
+ // Read original XML to check content
119
+ const originalXml = (await (0, filesUtils_1.readXmlFromFile)(xmlFilePath)) ?? {};
120
+ const originalItem = originalXml[constants_objecttranslations_1.OBJTRANSL_ROOT_TAG] ?? {};
121
+ // Check if CSV directory exists
122
+ if (!fs.existsSync(objectCsvDir)) {
123
+ // Check if original XML has any content in OBJTRANSL_ITEMS sections (excluding fieldTranslations)
124
+ const itemsWithoutFieldTranslations = Object.keys(constants_objecttranslations_1.OBJTRANSL_ITEMS)
125
+ .filter(key => key !== constants_objecttranslations_1.OBJTRANSL_CFIELDTRANSL_ROOT)
126
+ .reduce((obj, key) => ({ ...obj, [key]: constants_objecttranslations_1.OBJTRANSL_ITEMS[key] }), {});
127
+ const hasContent = (0, alignmentChecker_1.hasFileItemsContent)(originalItem, itemsWithoutFieldTranslations);
128
+ // Also check if there are any .fieldTranslation-meta.xml files
129
+ const fieldTranslationFiles = (0, utils_objtransl_1.getFieldTranslationFiles)(xmlDir);
130
+ const hasFieldTranslations = fieldTranslationFiles.length > 0;
131
+ const message = `CSV directory not found: ${objectCsvDir}`;
132
+ // CSV should exist if there's content OR if there are fieldTranslation files
133
+ if (hasContent || hasFieldTranslations) {
134
+ items[objectName] = { result: 'KO', error: message };
135
+ console.log(`❌ Object translation '${objectName}' has misalignment:`);
136
+ console.log(` - ${message}`);
137
+ }
138
+ else {
139
+ items[objectName] = { result: 'OK' };
140
+ alignedCount++;
141
+ console.log(`✅ Object translation '${objectName}' is aligned`);
142
+ }
143
+ continue;
144
+ }
103
145
  let validationResult;
104
146
  if (mode === 'string') {
105
- validationResult = await compareStringsForObject(objectName, baseXmlDir, baseCsvDir, options);
147
+ validationResult = await compareStringsForObject(objectName, xmlFilePath, xmlDir, objectCsvDir, options);
106
148
  }
107
149
  else {
108
- validationResult = await validateSingleObjectTranslation(objectName, baseXmlDir, baseCsvDir, options);
150
+ validationResult = await validateSingleObjectTranslation(objectName, objectCsvDir, originalItem, options);
109
151
  }
110
152
  // Convert ValidationResult to ItemResult format
111
153
  if (validationResult.isAligned) {
@@ -113,15 +155,6 @@ async function objectTranslationAreAligned(options = {}) {
113
155
  alignedCount++;
114
156
  console.log(`✅ Object translation '${objectName}' is aligned`);
115
157
  }
116
- else if (validationResult.isWarning) {
117
- items[objectName] = {
118
- result: 'WARN',
119
- error: validationResult.differences.join('; ')
120
- };
121
- warningCount++;
122
- console.log(`⚠️ Object translation '${objectName}' has warnings:`);
123
- validationResult.differences.forEach(diff => console.log(` - ${diff}`));
124
- }
125
158
  else {
126
159
  items[objectName] = {
127
160
  result: 'KO',
@@ -136,56 +169,26 @@ async function objectTranslationAreAligned(options = {}) {
136
169
  summary: {
137
170
  totalItems: objectList.length,
138
171
  alignedItems: alignedCount,
139
- misalignedItems: objectList.length - alignedCount - warningCount,
140
- warningItems: warningCount
172
+ misalignedItems: objectList.length - alignedCount
141
173
  },
142
174
  items: items
143
175
  };
144
- console.log(`\n📊 Validation Summary: ${result.summary.totalItems} total, ${result.summary.alignedItems} aligned, ${result.summary.misalignedItems} misaligned, ${result.summary.warningItems} warnings`);
176
+ console.log(`\n📊 Validation Summary: ${result.summary.totalItems} total, ${result.summary.alignedItems} aligned, ${result.summary.misalignedItems} misaligned`);
145
177
  return result;
146
178
  }
147
179
  exports.objectTranslationAreAligned = objectTranslationAreAligned;
148
- async function validateSingleObjectTranslation(objectName, xmlDir, csvDir, options) {
180
+ async function validateSingleObjectTranslation(objectName, objectCsvDir, originalItem, options) {
149
181
  const differences = [];
150
182
  try {
151
- // Check main object translation XML
152
- const xmlFilePath = (0, path_1.join)(xmlDir, objectName, objectName + constants_objecttranslations_1.OBJTRANSL_EXTENSION);
153
- if (!fs.existsSync(xmlFilePath)) {
154
- return {
155
- itemName: objectName,
156
- isAligned: false,
157
- differences: [`XML file not found: ${xmlFilePath}`],
158
- isWarning: true
159
- };
160
- }
161
- const originalXml = await (0, filesUtils_1.readXmlFromFile)(xmlFilePath);
162
- if (!originalXml || !originalXml[constants_objecttranslations_1.OBJTRANSL_ROOT_TAG]) {
163
- return {
164
- itemName: objectName,
165
- isAligned: false,
166
- differences: [`Invalid XML structure in: ${xmlFilePath}`]
167
- };
168
- }
169
- // Check CSV directory
170
- const objectCsvDir = (0, path_1.join)(csvDir, objectName, 'csv');
171
- if (!fs.existsSync(objectCsvDir)) {
172
- return {
173
- itemName: objectName,
174
- isAligned: false,
175
- differences: [`CSV directory not found: ${objectCsvDir}`],
176
- isWarning: true
177
- };
178
- }
179
183
  // Reconstruct XML from CSV using shared merge logic
180
184
  const reconstructedXml = await (0, merge_1.mergeObjectTranslationFromCsv)(objectName, objectCsvDir, options);
181
185
  // Compare main object translation structures
182
- const originalData = originalXml[constants_objecttranslations_1.OBJTRANSL_ROOT_TAG] || {};
183
186
  const reconstructedData = reconstructedXml[constants_objecttranslations_1.OBJTRANSL_ROOT_TAG] || {};
184
187
  // Deep compare the relevant sections (excluding fieldTranslations)
185
188
  for (const sectionName in constants_objecttranslations_1.OBJTRANSL_ITEMS) {
186
189
  if (sectionName === constants_objecttranslations_1.OBJTRANSL_CFIELDTRANSL_ROOT)
187
190
  continue; // Skip fieldTranslations
188
- const originalSection = originalData[sectionName] || [];
191
+ const originalSection = originalItem[sectionName] || [];
189
192
  const reconstructedSection = reconstructedData[sectionName] || [];
190
193
  // Convert to arrays if they're objects
191
194
  const originalArray = Array.isArray(originalSection) ? originalSection : (originalSection ? [originalSection] : []);
@@ -207,7 +210,7 @@ async function validateSingleObjectTranslation(objectName, xmlDir, csvDir, optio
207
210
  }
208
211
  }
209
212
  // Validate fieldTranslations separately
210
- const fieldTranslationsDifferences = await validateFieldTranslationsForObject(objectName, xmlDir, objectCsvDir);
213
+ const fieldTranslationsDifferences = await validateFieldTranslationsForObject(objectName, objectCsvDir);
211
214
  differences.push(...fieldTranslationsDifferences);
212
215
  return {
213
216
  itemName: objectName,
@@ -223,42 +226,17 @@ async function validateSingleObjectTranslation(objectName, xmlDir, csvDir, optio
223
226
  };
224
227
  }
225
228
  }
226
- async function validateFieldTranslationsForObject(objectName, xmlDir, csvDir) {
229
+ async function validateFieldTranslationsForObject(objectName, csvDir) {
227
230
  const differences = [];
228
231
  try {
229
- // Get all existing fieldTranslation XML files
230
- const objectXmlDir = (0, path_1.join)(xmlDir, objectName);
231
- const fieldTranslationFiles = (0, utils_objtransl_1.getFieldTranslationFiles)(objectXmlDir);
232
232
  // Use shared logic to get field translations from CSV
233
233
  const fieldXmlArray = await (0, merge_1.getFieldTranslationsFromCsv)(objectName, csvDir);
234
- // Compare counts
235
- if (fieldTranslationFiles.length !== fieldXmlArray.length) {
236
- differences.push(`fieldTranslations: File count mismatch (XML: ${fieldTranslationFiles.length}, CSV: ${fieldXmlArray.length})`);
237
- }
238
- // Validate each field translation
239
- for (const fieldEntry of fieldXmlArray) {
240
- const expectedFileName = fieldEntry.name + constants_objecttranslations_1.OBJTRANSL_FIELDTRANSL_EXTENSION;
241
- const expectedFilePath = (0, path_1.join)(objectXmlDir, expectedFileName);
242
- if (!fs.existsSync(expectedFilePath)) {
243
- differences.push(`fieldTranslations: Missing XML file for ${fieldEntry.name}`);
244
- continue;
245
- }
246
- // Compare content
247
- const originalXml = await (0, filesUtils_1.readXmlFromFile)(expectedFilePath);
248
- const reconstructedXml = { [constants_objecttranslations_1.OBJTRANSL_CFIELDTRANSL_ROOT_TAG]: fieldEntry };
249
- const originalStr = JSON.stringify(originalXml);
250
- const reconstructedStr = JSON.stringify(reconstructedXml);
251
- if (originalStr !== reconstructedStr) {
252
- differences.push(`fieldTranslations: Content mismatch for ${fieldEntry.name}`);
253
- }
254
- }
255
- // Check for XML files not represented in CSV
256
- for (const xmlFile of fieldTranslationFiles) {
257
- const fieldName = xmlFile.replace(constants_objecttranslations_1.OBJTRANSL_FIELDTRANSL_EXTENSION, '');
258
- const foundInCsv = fieldXmlArray.some(entry => entry.name === fieldName);
259
- if (!foundInCsv) {
260
- differences.push(`fieldTranslations: XML file ${xmlFile} not represented in CSV`);
261
- }
234
+ // In logic mode, we just verify that we can reconstruct from CSV
235
+ // The actual comparison with XML files is done in the main validation
236
+ // This is mainly to catch CSV parsing errors
237
+ if (fieldXmlArray.length === 0) {
238
+ // No field translations in CSV - this is OK
239
+ return differences;
262
240
  }
263
241
  }
264
242
  catch (error) {
@@ -266,28 +244,8 @@ async function validateFieldTranslationsForObject(objectName, xmlDir, csvDir) {
266
244
  }
267
245
  return differences;
268
246
  }
269
- async function compareStringsForObject(objectName, xmlDir, csvDir, options) {
247
+ async function compareStringsForObject(objectName, xmlFilePath, xmlDir, objectCsvDir, options) {
270
248
  try {
271
- // Check main object translation XML
272
- const xmlFilePath = (0, path_1.join)(xmlDir, objectName, objectName + constants_objecttranslations_1.OBJTRANSL_EXTENSION);
273
- if (!fs.existsSync(xmlFilePath)) {
274
- return {
275
- itemName: objectName,
276
- isAligned: false,
277
- differences: [`XML file not found: ${xmlFilePath}`],
278
- isWarning: true
279
- };
280
- }
281
- // Check CSV directory
282
- const objectCsvDir = (0, path_1.join)(csvDir, objectName, 'csv');
283
- if (!fs.existsSync(objectCsvDir)) {
284
- return {
285
- itemName: objectName,
286
- isAligned: false,
287
- differences: [`CSV directory not found: ${objectCsvDir}`],
288
- isWarning: true
289
- };
290
- }
291
249
  const originalXmlString = await (0, filesUtils_1.readStringNormalizedFromFile)(xmlFilePath);
292
250
  // Reconstruct XML from CSV using shared merge logic
293
251
  const mergedXml = await (0, merge_1.mergeObjectTranslationFromCsv)(objectName, objectCsvDir, options);
@@ -329,13 +287,13 @@ async function compareStringsForObject(objectName, xmlDir, csvDir, options) {
329
287
  async function compareFieldTranslationsStringsForObject(objectName, xmlDir, csvDir, options) {
330
288
  const differences = [];
331
289
  try {
332
- const objectXmlDir = (0, path_1.join)(xmlDir, objectName);
333
- const fieldTranslationFiles = (0, utils_objtransl_1.getFieldTranslationFiles)(objectXmlDir);
290
+ // xmlDir is already the full path to the object directory (e.g., assets/default/objectTranslations/Account-es_CL)
291
+ const fieldTranslationFiles = (0, utils_objtransl_1.getFieldTranslationFiles)(xmlDir);
334
292
  // Use shared logic to get field translations from CSV
335
293
  const fieldXmlArray = await (0, merge_1.getFieldTranslationsFromCsv)(objectName, csvDir);
336
294
  // For each expected field translation file, reconstruct and compare
337
295
  for (const fieldEntry of fieldXmlArray) {
338
- const expectedFilePath = (0, path_1.join)(objectXmlDir, fieldEntry.name + constants_objecttranslations_1.OBJTRANSL_FIELDTRANSL_EXTENSION);
296
+ const expectedFilePath = (0, path_1.join)(xmlDir, fieldEntry.name + constants_objecttranslations_1.OBJTRANSL_FIELDTRANSL_EXTENSION);
339
297
  if (fs.existsSync(expectedFilePath)) {
340
298
  const originalString = await (0, filesUtils_1.readStringNormalizedFromFile)(expectedFilePath);
341
299
  // Create temp file for reconstructed content