Skip to content

Commit f65b65a

Browse files
Merge pull request #433 from shaurabh-tiwari-git/metadataEnablementEnhancements
@W-19808193 - Metdata API enhancements
2 parents d98239d + 6125f29 commit f65b65a

File tree

6 files changed

+130
-43
lines changed

6 files changed

+130
-43
lines changed

messages/assess.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@
197197
"dmNameUpdateFailed": "DM reference %s can’t be updated",
198198
"invalidTypeAssessErrorMessage": "We couldn't assess your Omnistudio components in the %s namespace. Select the correct namespace and try again",
199199
"assessmentSuccessfulMessage": "Migration assessment for org %s is complete and reports are ready for review in the folder %s",
200-
"errorCheckingOmniStudioMetadata": "We couldn't check whether the Omnistudio Metadata is enabled: %s. Try again later.",
201-
"omniStudioSettingsMetadataAlreadyEnabled": "The Omnistudio Metadata setting is already enabled with standard data model. No need for migration.",
200+
"errorCheckingOmniStudioMetadata": "Error while checking the Omnistudio settings metadata status: %s.",
201+
"omniStudioSettingsMetadataAlreadyEnabled": "The Omnistudio Metadata setting is already enabled with standard data model. No need for migration."
202202
"missingMandatoryField": "Missing mandatory field: %s for %s"
203203
}

messages/migrate.json

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -288,17 +288,19 @@
288288
"omniscriptPackageDeploymentFailedWithMessage": "Omniscript package deployment failed: %s",
289289
"metadataTablesAlreadyClean": "OmniStudio metadata tables are empty",
290290
"startingMetadataCleanup": "Initiated cleanup process for Omnistudio metadata tables.",
291-
"failedToCleanTables": "Table cleanup failed: %s",
291+
"failedToCleanTables": "Table cleanup failed: %s, Please contact Salesforce Support.",
292292
"errorCheckingMetadataTables": "Error checking Omnistudio metadata tables: %s",
293293
"metadataCleanupCompleted": "The Omnistudio metadata table cleanup process is complete. Total records cleaned: %s",
294294
"metadataCleanupConsentMessage": "By proceeding further, you hereby consent to clean up the Omnistudio metadata tables. Proceeding with the cleanup process will permanently delete all records from OmniUiCardConfig, OmniScriptConfig, OmniIntegrationProcConfig, and OmniDataTransformConfig tables. Do you want to proceed? [y/n]",
295295
"metadataCleanupConsentNotGiven": "You’ve not consented to proceed with the Omnistudio metadata table cleanup. We’ll not be able to proceed with the migration.",
296296
"omniStudioMetadataEnableConsentMessage": "As part of the migration process, the Omnistudio Metadata setting will be enabled. After the setting is enabled, you cannot disable it. Do you want to proceed? [y/n]",
297-
"errorCheckingOmniStudioMetadata": "We couldn't check whether the Omnistudio Metadata is enabled: %s. Try again later.",
297+
"errorCheckingOmniStudioMetadata": "Error while checking the Omnistudio settings metadata status: %s.",
298298
"omniStudioSettingsMetadataAlreadyEnabled": "The Omnistudio Metadata setting is already enabled with standard data model. No need for migration.",
299299
"omniStudioSettingsMetadataEnabled": "The Omnistudio Metadata setting is enabled with standard data model.",
300-
"errorEnablingOmniStudioSettingsMetadata": "We couldn't enable the Omnistudio Metadata setting: %s. Enable it manually.",
301-
"manuallyEnableOmniStudioSettingsMetadata": "Manually enable the Omnistudio Metadata setting in your org’s Omnistudio Settings page.",
300+
"timeoutEnablingOmniStudioSettingsMetadata": "Timeout while checking the metadata enablement status. Tried for %s seconds. Refer to registered email for more details.",
301+
"errorEnablingOmniStudioSettingsMetadata": "Error while enabling the Omnistudio Metadata setting: %s.",
302+
"fieldIntegrityExceptions": "Metadata cleanup failed for %s due to a field integrity exception. Deactivate the %s and try again.",
303+
"manuallyEnableOmniStudioSettingsMetadata": "Manually enable the Omnistudio Metadata setting in your org’s Omnistudio Settings page. Refer <a href='https://help.salesforce.com/s/articleView?id=xcloud.os_enable_omnistudio_metadata_api_support.htm&type=5' target='_blank'>this documentation</a> for more details.",
302304
"omniStudioMetadataEnableConsentNotGiven": "You’ve not consented to proceed with enabling the Omnistudio Metadata setting. We’ll not be able to proceed with the migration.",
303305
"enablingOmniStudioSettingsMetadataStatus": "Enabling Omnistudio Metadata setting…",
304306
"omniStudioAllVersionsMigrationConsent": "Org uses the standard data model. All versions of Omnistudio components such as Omniscripts, Data Mappers, Integration Procedures, and Flexcards must be migrated to enable the Omnistudio Metadata API setting. But you’ve not specified -a or allversions flag in the command. Proceed to migrate all versions of the Omnistudio components? [y/n]",

src/migration/postMigrate.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,9 @@ export class PostMigrate extends BaseMigrationTool {
146146
*/
147147
const maxAttempts = 6;
148148
let attempts = 0;
149+
const metadataService = new OmniStudioMetadataCleanupService(this.connection, this.messages);
149150
while (attempts < maxAttempts) {
150151
await new Promise((resolve) => setTimeout(resolve, 20000));
151-
const metadataService = new OmniStudioMetadataCleanupService(this.connection, this.messages);
152152
const isConfigTablesEmpty = await metadataService.hasCleanOmniStudioMetadataTables(); //Check is the config tables are populated or not.
153153
if (!isConfigTablesEmpty) {
154154
// If the config tables are populated, means the metadata is enabled.
@@ -159,7 +159,7 @@ export class PostMigrate extends BaseMigrationTool {
159159
}
160160
if (attempts === maxAttempts) {
161161
// TODO: We need to figure out and show the user that what is the actual issue which is causing the metadata to not be enabled.
162-
Logger.error(this.messages.getMessage('errorEnablingOmniStudioSettingsMetadata', ['Unknown error']));
162+
Logger.error(this.messages.getMessage('timeoutEnablingOmniStudioSettingsMetadata', [maxAttempts * 20]));
163163
userActionMessage.push(this.messages.getMessage('manuallyEnableOmniStudioSettingsMetadata'));
164164
}
165165
} else {

src/utils/config/OmniStudioMetadataCleanupService.ts

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Connection, Messages } from '@salesforce/core';
22
import { Logger } from '../logger';
33
import { QueryTools } from '../query';
44
import { NetUtils } from '../net';
5+
import { getMigrationHeading } from '../stringUtils';
56

67
/**
78
* OmniStudioMetadataCleanupService
@@ -21,6 +22,13 @@ export class OmniStudioMetadataCleanupService {
2122
'OmniDataTransformConfig',
2223
];
2324

25+
private static readonly FIELD_MAP = {
26+
OmniUiCardConfig: getMigrationHeading('Flexcard'),
27+
OmniScriptConfig: getMigrationHeading('OmniScript'),
28+
OmniIntegrationProcConfig: getMigrationHeading('Integration Procedure'),
29+
OmniDataTransformConfig: getMigrationHeading('Data Mapper'),
30+
};
31+
2432
private readonly connection: Connection;
2533
private readonly messages: Messages;
2634

@@ -60,17 +68,33 @@ export class OmniStudioMetadataCleanupService {
6068

6169
let totalCleanedRecords = 0;
6270
const failedTables: string[] = [];
71+
const tablesWithFieldIntegrityExceptions: string[] = [];
72+
const toDeactivate: string[] = [];
6373

6474
for (const tableName of OmniStudioMetadataCleanupService.CONFIG_TABLES) {
65-
const recordCount = await this.cleanupOmniStudioMetadataTable(tableName);
75+
const result = await this.cleanupOmniStudioMetadataTable(tableName);
6676

67-
if (recordCount >= 0) {
68-
totalCleanedRecords += recordCount;
77+
if (result.recordCount >= 0) {
78+
totalCleanedRecords += result.recordCount;
6979
} else {
7080
failedTables.push(tableName);
81+
if (result.statusCode === 'FIELD_INTEGRITY_EXCEPTION') {
82+
tablesWithFieldIntegrityExceptions.push(tableName);
83+
toDeactivate.push(OmniStudioMetadataCleanupService.FIELD_MAP[tableName]);
84+
}
7185
}
7286
}
7387

88+
if (tablesWithFieldIntegrityExceptions.length > 0) {
89+
Logger.error(
90+
this.messages.getMessage('fieldIntegrityExceptions', [
91+
tablesWithFieldIntegrityExceptions.join(', '),
92+
toDeactivate.join(', '),
93+
])
94+
);
95+
return false;
96+
}
97+
7498
if (failedTables.length > 0) {
7599
Logger.error(this.messages.getMessage('failedToCleanTables', [failedTables.join(', ')]));
76100
return false;
@@ -88,16 +112,21 @@ export class OmniStudioMetadataCleanupService {
88112
* Checks a specific table for records and cleans them if found
89113
*
90114
* @param tableName - Name of the table to check and clean
91-
* @returns Promise<number> - number of cleaned records, or -1 if failed
115+
* @returns Promise<{recordCount: number, statusCode?: string}> - recordCount: number of cleaned records (or -1 if failed), statusCode: optional error status code
92116
*/
93-
private async cleanupOmniStudioMetadataTable(tableName: string): Promise<number> {
117+
private async cleanupOmniStudioMetadataTable(
118+
tableName: string
119+
): Promise<{ recordCount: number; statusCode?: string }> {
94120
const recordIds = await QueryTools.queryIds(this.connection, tableName);
95121

96122
if (recordIds.length === 0) {
97-
return 0;
123+
return { recordCount: 0 };
98124
}
99125

100-
const deleteSuccess = await NetUtils.delete(this.connection, recordIds);
101-
return deleteSuccess ? recordIds.length : -1;
126+
const deleteResult = await NetUtils.deleteWithFieldIntegrityException(this.connection, recordIds);
127+
if (!deleteResult.success) {
128+
return { recordCount: -1, statusCode: deleteResult.statusCode };
129+
}
130+
return { recordCount: recordIds.length };
102131
}
103132
}

src/utils/net/index.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,52 @@ class NetUtils {
124124
return true;
125125
}
126126

127+
public static async deleteWithFieldIntegrityException(
128+
connection: Connection,
129+
data: string[]
130+
): Promise<{ success: boolean; statusCode?: string; message?: string }> {
131+
// Metadata API only accepts 200 records per request
132+
const chunks = chunk(data, NetUtils.CHUNK_SIZE);
133+
let hasFieldIntegrityException = false;
134+
let hasErrors = false;
135+
let errorCode: string | undefined;
136+
let message: string | undefined;
137+
138+
for (let curr of chunks) {
139+
const deleteUrl = 'composite/sobjects?allOrNone=true&ids=' + curr.join(',');
140+
141+
const response = await this.request<DeleteResponse[]>(connection, deleteUrl, [], RequestMethod.DELETE);
142+
// Check each response for errors
143+
response.forEach((result) => {
144+
if (!result.success && result.errors && result.errors.length > 0) {
145+
result.errors.forEach((error) => {
146+
hasErrors = true;
147+
// Check if this error is a FIELD_INTEGRITY_EXCEPTION
148+
if (error.statusCode === 'FIELD_INTEGRITY_EXCEPTION') {
149+
hasFieldIntegrityException = true;
150+
message = error.message;
151+
return;
152+
} else {
153+
errorCode = error.statusCode;
154+
}
155+
});
156+
}
157+
});
158+
}
159+
160+
// If there are failed records, return failure with status code
161+
if (hasErrors) {
162+
return {
163+
success: false,
164+
// Override with FIELD_INTEGRITY_EXCEPTION if found, otherwise use first encountered
165+
statusCode: hasFieldIntegrityException ? 'FIELD_INTEGRITY_EXCEPTION' : errorCode,
166+
message: message,
167+
};
168+
}
169+
170+
return { success: true };
171+
}
172+
127173
public static async request<TResultType>(
128174
connection: Connection,
129175
url: string,
@@ -156,4 +202,14 @@ interface TreeResult {
156202
results: UploadRecordResult[];
157203
}
158204

205+
interface DeleteResponse {
206+
id: string;
207+
success: boolean;
208+
errors: Array<{
209+
statusCode: string;
210+
message: string;
211+
fields: string[];
212+
}>;
213+
}
214+
159215
export { NetUtils, RequestMethod, TreeResult };

0 commit comments

Comments
 (0)