delightfulrachel commited on
Commit
c53558e
·
verified ·
1 Parent(s): c2acf75

Create utils.py

Browse files
Files changed (1) hide show
  1. utils.py +484 -0
utils.py ADDED
@@ -0,0 +1,484 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Utility functions for Salesforce B2B Commerce Migration Assistant"""
2
+
3
+ import re
4
+ import json
5
+ import logging
6
+ from typing import Dict, Tuple, List, Optional
7
+
8
+ # Configure logging
9
+ logging.basicConfig(level=logging.INFO)
10
+ logger = logging.getLogger(__name__)
11
+
12
+ # Apex syntax patterns for validation
13
+ APEX_PATTERNS = {
14
+ "class_declaration": r"(?:public|private|global|protected)\s+(?:virtual|abstract|with sharing|without sharing|inherited sharing)?\s*class\s+\w+",
15
+ "trigger_declaration": r"trigger\s+\w+\s+on\s+\w+\s*\([^)]+\)",
16
+ "method_declaration": r"(?:public|private|global|protected)\s+(?:static)?\s*(?:void|\w+)\s+\w+\s*\([^)]*\)",
17
+ "soql_query": r"(?:\[|Database\.query\s*\()\s*SELECT\s+.*?\s+FROM\s+\w+.*?(?:\]|\))",
18
+ "dml_operation": r"(?:insert|update|delete|undelete|upsert|merge)\s+\w+",
19
+ "bulkification_issue": r"for\s*\([^)]+\)\s*{[^}]*(?:insert|update|delete|undelete)\s+",
20
+ "hardcoded_id": r"(?:\'[a-zA-Z0-9]{15}\'|\'[a-zA-Z0-9]{18}\')",
21
+ "missing_null_check": r"(\w+)\.(\w+)(?!\s*(?:!=|==)\s*null)",
22
+ "governor_limit_risk": r"(?:for\s*\([^)]+\)\s*{[^}]*\[SELECT|Database\.query)",
23
+ }
24
+
25
+ # Common Apex errors and their fixes
26
+ APEX_ERRORS = {
27
+ "missing_semicolon": {
28
+ "pattern": r"[^{};]\s*\n\s*(?:public|private|global|protected|if|for|while|try)",
29
+ "message": "Missing semicolon at end of statement",
30
+ "severity": "error"
31
+ },
32
+ "unclosed_bracket": {
33
+ "pattern": r"(?:\{(?:[^{}]|(?:\{[^{}]*\}))*$)|(?:^[^{}]*\})",
34
+ "message": "Unclosed or extra bracket detected",
35
+ "severity": "error"
36
+ },
37
+ "invalid_soql": {
38
+ "pattern": r"\[\s*SELECT\s+FROM\s+\w+",
39
+ "message": "Invalid SOQL: Missing field selection",
40
+ "severity": "error"
41
+ },
42
+ "missing_try_catch_dml": {
43
+ "pattern": r"(?<!try\s{[^}]*)(insert|update|delete|upsert)\s+(?!.*catch)",
44
+ "message": "DML operation without try-catch block",
45
+ "severity": "warning"
46
+ }
47
+ }
48
+
49
+ # B2B Commerce specific patterns
50
+ B2B_COMMERCE_PATTERNS = {
51
+ "cloudcraze_reference": r"(?:ccrz__|E_\w+|CC_\w+)",
52
+ "b2b_lex_object": r"(?:OrderSummary|CartItem|WebCart|ProductCatalog|BuyerGroup|CommerceEntitlementPolicy)",
53
+ "deprecated_method": r"(?:ccrz\.cc_CallContext|ccrz\.ccAPI|cc_bean_\w+)",
54
+ "migration_required": r"(?:E_Product__|E_Cart__|E_Order__|CC_Promotions__|CC_Tax__)"
55
+ }
56
+
57
+ VALIDATION_SCHEMA = {
58
+ "quality_rating": "int (1–10)",
59
+ "accuracy": "float (0.0–1.0)",
60
+ "completeness": "float (0.0–1.0)",
61
+ "best_practices_alignment": "float (0.0–1.0)",
62
+ "syntax_validity": "float (0.0–1.0)",
63
+ "security_score": "float (0.0–1.0)",
64
+ "performance_score": "float (0.0–1.0)",
65
+ "explanations": {
66
+ "quality_rating": "string",
67
+ "accuracy": "string",
68
+ "completeness": "string",
69
+ "best_practices_alignment": "string",
70
+ "syntax_validity": "string",
71
+ "security_score": "string",
72
+ "performance_score": "string"
73
+ },
74
+ "errors": ["list of syntax errors"],
75
+ "warnings": ["list of potential issues"],
76
+ "suggestions": ["list of improvement suggestions"]
77
+ }
78
+
79
+ def validate_apex_syntax(code: str) -> Tuple[bool, List[Dict[str, str]]]:
80
+ """Validate Apex syntax and return errors/warnings."""
81
+ issues = []
82
+
83
+ # Check for basic syntax errors
84
+ for error_type, error_info in APEX_ERRORS.items():
85
+ matches = re.finditer(error_info["pattern"], code, re.MULTILINE | re.DOTALL)
86
+ for match in matches:
87
+ issues.append({
88
+ "type": error_info["severity"],
89
+ "message": error_info["message"],
90
+ "line": code[:match.start()].count('\n') + 1,
91
+ "position": match.start()
92
+ })
93
+
94
+ # Check for Apex-specific patterns
95
+ if not re.search(APEX_PATTERNS["class_declaration"], code) and \
96
+ not re.search(APEX_PATTERNS["trigger_declaration"], code):
97
+ issues.append({
98
+ "type": "error",
99
+ "message": "No valid Apex class or trigger declaration found",
100
+ "line": 1,
101
+ "position": 0
102
+ })
103
+
104
+ # Check for bulkification issues
105
+ bulk_issues = re.finditer(APEX_PATTERNS["bulkification_issue"], code, re.DOTALL)
106
+ for match in bulk_issues:
107
+ issues.append({
108
+ "type": "error",
109
+ "message": "DML operation inside loop - violates bulkification best practices",
110
+ "line": code[:match.start()].count('\n') + 1,
111
+ "position": match.start()
112
+ })
113
+
114
+ # Check for hardcoded IDs
115
+ hardcoded_ids = re.finditer(APEX_PATTERNS["hardcoded_id"], code)
116
+ for match in hardcoded_ids:
117
+ issues.append({
118
+ "type": "warning",
119
+ "message": "Hardcoded Salesforce ID detected - use Custom Settings or Custom Metadata",
120
+ "line": code[:match.start()].count('\n') + 1,
121
+ "position": match.start()
122
+ })
123
+
124
+ # Check for governor limit risks
125
+ gov_limit_risks = re.finditer(APEX_PATTERNS["governor_limit_risk"], code, re.DOTALL)
126
+ for match in gov_limit_risks:
127
+ issues.append({
128
+ "type": "warning",
129
+ "message": "SOQL query inside loop - potential governor limit issue",
130
+ "line": code[:match.start()].count('\n') + 1,
131
+ "position": match.start()
132
+ })
133
+
134
+ has_errors = any(issue["type"] == "error" for issue in issues)
135
+ return not has_errors, issues
136
+
137
+ def perform_skeptical_evaluation(code: str, context: str = "trigger") -> Dict[str, any]:
138
+ """Perform skeptical evaluation of code looking for common issues."""
139
+ evaluation = {
140
+ "syntax_issues": [],
141
+ "security_concerns": [],
142
+ "performance_issues": [],
143
+ "best_practice_violations": [],
144
+ "b2b_commerce_issues": []
145
+ }
146
+
147
+ # Syntax validation
148
+ is_valid, syntax_issues = validate_apex_syntax(code)
149
+ evaluation["syntax_issues"] = syntax_issues
150
+
151
+ # Security checks
152
+ if re.search(r"without\s+sharing", code, re.IGNORECASE):
153
+ evaluation["security_concerns"].append({
154
+ "type": "warning",
155
+ "message": "Class declared 'without sharing' - ensure this is intentional"
156
+ })
157
+
158
+ if not re.search(r"\.stripInaccessible\(", code) and re.search(r"(insert|update)\s+", code):
159
+ evaluation["security_concerns"].append({
160
+ "type": "warning",
161
+ "message": "DML operations without stripInaccessible - potential FLS violation"
162
+ })
163
+
164
+ # Performance checks
165
+ nested_loops = re.findall(r"for\s*\([^)]+\)\s*\{[^}]*for\s*\([^)]+\)", code, re.DOTALL)
166
+ if nested_loops:
167
+ evaluation["performance_issues"].append({
168
+ "type": "warning",
169
+ "message": f"Nested loops detected ({len(nested_loops)} occurrences) - review for O(n²) complexity"
170
+ })
171
+
172
+ # Check for missing test assertions (if it's a test class)
173
+ if re.search(r"@isTest|testMethod", code, re.IGNORECASE):
174
+ if not re.search(r"System\.assert|Assert\.", code):
175
+ evaluation["best_practice_violations"].append({
176
+ "type": "error",
177
+ "message": "Test class without assertions - tests must verify behavior"
178
+ })
179
+
180
+ # B2B Commerce specific checks
181
+ cloudcraze_refs = re.findall(B2B_COMMERCE_PATTERNS["cloudcraze_reference"], code)
182
+ if cloudcraze_refs:
183
+ evaluation["b2b_commerce_issues"].append({
184
+ "type": "error",
185
+ "message": f"CloudCraze references found ({len(set(cloudcraze_refs))} unique) - must be migrated to B2B LEX"
186
+ })
187
+
188
+ deprecated_methods = re.findall(B2B_COMMERCE_PATTERNS["deprecated_method"], code)
189
+ if deprecated_methods:
190
+ evaluation["b2b_commerce_issues"].append({
191
+ "type": "error",
192
+ "message": f"Deprecated CloudCraze methods found: {', '.join(set(deprecated_methods))}"
193
+ })
194
+
195
+ return evaluation
196
+
197
+ def extract_code_blocks(text: str) -> str:
198
+ """Enhanced code extraction with multiple strategies."""
199
+ # Strategy 1: Standard code blocks with language markers
200
+ pattern = r"```(?:apex|java|Apex|Java|APEX|JAVA)?\s*(.*?)```"
201
+ matches = re.findall(pattern, text, re.DOTALL | re.IGNORECASE)
202
+
203
+ code_blocks = []
204
+ for block in matches:
205
+ cleaned_block = block.strip()
206
+ if cleaned_block:
207
+ code_blocks.append(cleaned_block)
208
+
209
+ # Strategy 2: Improved fallback detection for Apex-specific patterns
210
+ if not code_blocks:
211
+ apex_patterns = [
212
+ # Class declarations (including inner classes)
213
+ r"((?:public|private|global|protected)\s+(?:virtual|abstract|with sharing|without sharing|inherited sharing)?\s*class\s+\w+(?:\s+extends\s+\w+)?(?:\s+implements\s+[\w\s,]+)?\s*\{(?:[^{}]|\{[^{}]*\})*\})",
214
+ # Trigger declarations
215
+ r"(trigger\s+\w+\s+on\s+\w+\s*\([^)]+\)\s*\{(?:[^{}]|\{[^{}]*\})*\})",
216
+ # Interface declarations
217
+ r"((?:public|private|global)\s+interface\s+\w+(?:\s+extends\s+[\w\s,]+)?\s*\{(?:[^{}]|\{[^{}]*\})*\})",
218
+ # Enum declarations
219
+ r"((?:public|private|global)\s+enum\s+\w+\s*\{[^}]+\})",
220
+ # Annotated methods or classes
221
+ r"(@\w+(?:\([^)]*\))?\s*(?:public|private|global|protected).*?(?:\{(?:[^{}]|\{[^{}]*\})*\}|;))"
222
+ ]
223
+
224
+ for pattern in apex_patterns:
225
+ found = re.findall(pattern, text, re.DOTALL | re.MULTILINE)
226
+ code_blocks.extend(found)
227
+
228
+ # Strategy 3: Look for code between specific markers
229
+ if not code_blocks:
230
+ # Look for code after phrases like "corrected code:", "here's the code:", etc.
231
+ marker_patterns = [
232
+ r"(?:corrected|fixed|updated|converted|modified)\s+code\s*:\s*\n((?:(?:public|private|global|trigger).*?)(?=\n\n|\Z))",
233
+ r"(?:here'?s?|below is)\s+(?:the|your)\s+(?:corrected|fixed|updated)\s+\w+\s*:\s*\n((?:(?:public|private|global|trigger).*?)(?=\n\n|\Z))"
234
+ ]
235
+
236
+ for pattern in marker_patterns:
237
+ found = re.findall(pattern, text, re.DOTALL | re.IGNORECASE)
238
+ code_blocks.extend(found)
239
+
240
+ return '\n\n'.join(filter(None, code_blocks))
241
+
242
+ def format_structured_explanation(response: str, code_output: str) -> str:
243
+ """Format the explanation in a structured, brief manner."""
244
+ # Extract key sections using regex
245
+ sections = {
246
+ "key_changes": "",
247
+ "critical_issues": "",
248
+ "warnings": ""
249
+ }
250
+
251
+ # Extract KEY CHANGES section
252
+ key_match = re.search(r"##\s*KEY CHANGES.*?\n((?:[-•]\s*.*?\n)+)", response, re.IGNORECASE | re.DOTALL)
253
+ if key_match:
254
+ sections["key_changes"] = key_match.group(1).strip()
255
+
256
+ # Extract CRITICAL ISSUES section
257
+ critical_match = re.search(r"##\s*CRITICAL ISSUES.*?\n((?:\d+\..*?\n)+)", response, re.IGNORECASE | re.DOTALL)
258
+ if critical_match:
259
+ sections["critical_issues"] = critical_match.group(1).strip()
260
+
261
+ # Extract WARNINGS section
262
+ warning_match = re.search(r"##\s*REMAINING WARNINGS.*?\n((?:[-•]\s*.*?\n)*)", response, re.IGNORECASE | re.DOTALL)
263
+ if warning_match:
264
+ sections["warnings"] = warning_match.group(1).strip()
265
+
266
+ # Build formatted explanation
267
+ formatted = "### Summary of Changes\n\n"
268
+
269
+ if sections["key_changes"]:
270
+ formatted += "**Key Changes:**\n" + sections["key_changes"] + "\n\n"
271
+
272
+ if sections["critical_issues"]:
273
+ formatted += "**Critical Issues Fixed:**\n" + sections["critical_issues"] + "\n\n"
274
+
275
+ if sections["warnings"]:
276
+ formatted += "**⚠️ Remaining Warnings:**\n" + sections["warnings"]
277
+
278
+ # If structured extraction failed, provide a brief summary
279
+ if not any(sections.values()):
280
+ # Fall back to a simple extraction
281
+ formatted = "### Code Correction Summary\n\n"
282
+ formatted += "The code has been corrected and optimized. "
283
+ formatted += "Check the code output for inline comments explaining specific changes.\n\n"
284
+ formatted += "For detailed analysis, see the Full Model Response."
285
+
286
+ return formatted.strip()
287
+
288
+ def format_object_conversion_explanation(response: str, code_output: str) -> str:
289
+ """Format the object conversion explanation in a structured manner."""
290
+ sections = {
291
+ "mapping": "",
292
+ "field_table": "",
293
+ "steps": "",
294
+ "warnings": ""
295
+ }
296
+
297
+ # Extract object mapping section
298
+ mapping_match = re.search(r"##\s*B2B LEX OBJECT MAPPING.*?\n((?:[-•]\s*.*?\n)+)", response, re.IGNORECASE | re.DOTALL)
299
+ if mapping_match:
300
+ sections["mapping"] = mapping_match.group(1).strip()
301
+
302
+ # Extract field mappings table
303
+ table_match = re.search(r"##\s*FIELD MAPPINGS.*?\n((?:\|.*?\|.*?\n)+)", response, re.IGNORECASE | re.DOTALL)
304
+ if table_match:
305
+ sections["field_table"] = table_match.group(1).strip()
306
+
307
+ # Extract migration steps
308
+ steps_match = re.search(r"##\s*MIGRATION STEPS.*?\n((?:\d+\..*?\n)+)", response, re.IGNORECASE | re.DOTALL)
309
+ if steps_match:
310
+ sections["steps"] = steps_match.group(1).strip()
311
+
312
+ # Extract warnings
313
+ warning_match = re.search(r"##\s*WARNINGS.*?\n((?:[-•]\s*.*?\n)*)", response, re.IGNORECASE | re.DOTALL)
314
+ if warning_match:
315
+ sections["warnings"] = warning_match.group(1).strip()
316
+
317
+ # Build formatted explanation
318
+ formatted = "### Conversion Summary\n\n"
319
+
320
+ if sections["mapping"]:
321
+ formatted += "**Object Mapping:**\n" + sections["mapping"] + "\n\n"
322
+
323
+ if sections["field_table"]:
324
+ formatted += "**Field Mappings:**\n" + sections["field_table"] + "\n\n"
325
+
326
+ if sections["steps"]:
327
+ formatted += "**Migration Steps:**\n" + sections["steps"] + "\n\n"
328
+
329
+ if sections["warnings"]:
330
+ formatted += "**⚠️ Important Notes:**\n" + sections["warnings"]
331
+
332
+ # Fallback if structured extraction failed
333
+ if not any(sections.values()):
334
+ formatted = "### Conversion Summary\n\n"
335
+ formatted += "The CloudCraze object has been converted to B2B Lightning Experience format. "
336
+ formatted += "Check the code output for the complete implementation.\n\n"
337
+ formatted += "For detailed field mappings and migration steps, see the Full Model Response."
338
+
339
+ return formatted.strip()
340
+
341
+ def extract_validation_metrics(validation_text: str) -> Optional[Dict[str, float]]:
342
+ """Enhanced JSON extraction for validation metrics."""
343
+ try:
344
+ # Strategy 1: Look for JSON after specific markers
345
+ json_patterns = [
346
+ r'(?:json|JSON|assessment|Assessment)[\s:]*({[^{}]*(?:{[^{}]*}[^{}]*)*})',
347
+ r'```json\s*({[^`]+})\s*```',
348
+ r'({[^{}]*"quality_rating"[^{}]*(?:{[^{}]*}[^{}]*)*})'
349
+ ]
350
+
351
+ for pattern in json_patterns:
352
+ matches = re.findall(pattern, validation_text, re.DOTALL)
353
+ for match in matches:
354
+ try:
355
+ data = json.loads(match)
356
+ if "quality_rating" in data:
357
+ return normalize_metrics(data)
358
+ except json.JSONDecodeError:
359
+ continue
360
+
361
+ # Strategy 2: Extract individual metrics if JSON parsing fails
362
+ metrics = {}
363
+ metric_patterns = {
364
+ "quality_rating": r"quality_rating[\"']?\s*:\s*(\d+(?:\.\d+)?)",
365
+ "accuracy": r"accuracy[\"']?\s*:\s*(\d+(?:\.\d+)?)",
366
+ "completeness": r"completeness[\"']?\s*:\s*(\d+(?:\.\d+)?)",
367
+ "best_practices_alignment": r"best_practices_alignment[\"']?\s*:\s*(\d+(?:\.\d+)?)",
368
+ "syntax_validity": r"syntax_validity[\"']?\s*:\s*(\d+(?:\.\d+)?)",
369
+ "security_score": r"security_score[\"']?\s*:\s*(\d+(?:\.\d+)?)",
370
+ "performance_score": r"performance_score[\"']?\s*:\s*(\d+(?:\.\d+)?)"
371
+ }
372
+
373
+ for metric, pattern in metric_patterns.items():
374
+ match = re.search(pattern, validation_text, re.IGNORECASE)
375
+ if match:
376
+ metrics[metric] = float(match.group(1))
377
+
378
+ if metrics:
379
+ return normalize_metrics(metrics)
380
+
381
+ return None
382
+
383
+ except Exception as e:
384
+ logger.error(f"Error extracting metrics: {e}")
385
+ return None
386
+
387
+ def normalize_metrics(data: Dict) -> Dict[str, float]:
388
+ """Ensure metrics are in the correct format and range."""
389
+ normalized = {
390
+ "quality_rating": min(10, max(0, float(data.get("quality_rating", 0)))),
391
+ "accuracy": min(1.0, max(0.0, float(data.get("accuracy", 0.0)))),
392
+ "completeness": min(1.0, max(0.0, float(data.get("completeness", 0.0)))),
393
+ "best_practices_alignment": min(1.0, max(0.0, float(data.get("best_practices_alignment", 0.0)))),
394
+ "syntax_validity": min(1.0, max(0.0, float(data.get("syntax_validity", 0.0)))),
395
+ "security_score": min(1.0, max(0.0, float(data.get("security_score", 0.0)))),
396
+ "performance_score": min(1.0, max(0.0, float(data.get("performance_score", 0.0))))
397
+ }
398
+ return normalized
399
+
400
+ def generate_test_cases(code_type: str, code: str) -> str:
401
+ """Generate test cases for the given code."""
402
+ if code_type == "trigger":
403
+ return f"""
404
+ // Test class for the trigger
405
+ @isTest
406
+ private class Test_MigratedTrigger {{
407
+ @TestSetup
408
+ static void setup() {{
409
+ // Create test data
410
+ // TODO: Add specific test data setup
411
+ }}
412
+
413
+ @isTest
414
+ static void testBulkInsert() {{
415
+ // Test bulk insert scenario
416
+ List<SObject> testRecords = new List<SObject>();
417
+ for(Integer i = 0; i < 200; i++) {{
418
+ // TODO: Create test records
419
+ }}
420
+
421
+ Test.startTest();
422
+ insert testRecords;
423
+ Test.stopTest();
424
+
425
+ // TODO: Add assertions
426
+ System.assert(true, 'Bulk insert test needs implementation');
427
+ }}
428
+
429
+ @isTest
430
+ static void testBulkUpdate() {{
431
+ // Test bulk update scenario
432
+ // TODO: Implement bulk update test
433
+ }}
434
+
435
+ @isTest
436
+ static void testErrorHandling() {{
437
+ // Test error scenarios
438
+ // TODO: Test validation rules, required fields, etc.
439
+ }}
440
+
441
+ @isTest
442
+ static void testGovernorLimits() {{
443
+ // Test near governor limits
444
+ // TODO: Test with large data volumes
445
+ }}
446
+ }}
447
+ """
448
+ else: # object conversion
449
+ return f"""
450
+ // Test data creation for migrated object
451
+ @isTest
452
+ public class Test_MigratedObjectData {{
453
+ public static SObject createTestRecord() {{
454
+ // TODO: Create and return test instance
455
+ return null;
456
+ }}
457
+
458
+ public static List<SObject> createBulkTestRecords(Integer count) {{
459
+ List<SObject> records = new List<SObject>();
460
+ for(Integer i = 0; i < count) {{
461
+ // TODO: Create test records
462
+ }}
463
+ return records;
464
+ }}
465
+
466
+ public static void validateMigrationMapping() {{
467
+ // Validate that all fields are properly mapped
468
+ // TODO: Add field mapping validation
469
+ }}
470
+ }}
471
+ """
472
+
473
+ def handle_api_error(status_code: int, response_text: str) -> str:
474
+ """Handle API errors with appropriate user-friendly messages."""
475
+ if status_code == 401:
476
+ return "Authentication failed. Please check API configuration."
477
+ elif status_code == 429:
478
+ return "Rate limit exceeded. Please try again later."
479
+ elif status_code == 403:
480
+ return "Access forbidden. Please check your permissions."
481
+ elif status_code >= 500:
482
+ return "Service temporarily unavailable. Please try again."
483
+ else:
484
+ return f"Request failed with status {status_code}"