delightfulrachel commited on
Commit
cbf016f
·
verified ·
1 Parent(s): 56455aa

Update utils.py

Browse files
Files changed (1) hide show
  1. utils.py +245 -424
utils.py CHANGED
@@ -1,484 +1,305 @@
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}"
 
1
+ """
2
+ Utility functions for Salesforce B2B Commerce migration assistant.
3
+ """
4
 
5
  import re
6
  import json
7
  import logging
8
+ from typing import Dict, List, Tuple, Optional, Any
9
 
10
  # Configure logging
11
  logging.basicConfig(level=logging.INFO)
12
  logger = logging.getLogger(__name__)
13
 
14
+ # Validation schema for Apex code
15
+ VALIDATION_SCHEMA = {
16
+ "syntax_errors": ["missing_semicolon", "unclosed_bracket", "invalid_syntax"],
17
+ "security_issues": ["soql_injection", "hardcoded_credentials", "unsafe_dml"],
18
+ "performance_issues": ["governor_limits", "bulk_operations", "inefficient_queries"],
19
+ "b2b_commerce_issues": ["deprecated_apis", "missing_null_checks", "incorrect_field_references"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  }
21
 
22
+ # B2B Commerce patterns for detection
23
  B2B_COMMERCE_PATTERNS = {
24
+ "cloudcraze_reference": r"(ccrz__|E_[A-Z]|CloudCraze)",
25
+ "trigger_pattern": r"trigger\s+\w+\s+on\s+\w+",
26
+ "apex_class_pattern": r"(public|private|global)\s+class\s+\w+",
27
+ "soql_pattern": r"SELECT\s+.+\s+FROM\s+\w+",
28
+ "dml_pattern": r"(insert|update|delete|upsert)\s+\w+"
29
  }
30
 
31
+ def validate_apex_syntax(code: str) -> Tuple[bool, List[Dict[str, Any]]]:
32
+ """
33
+ Validate Apex code syntax and return issues found.
34
+
35
+ Args:
36
+ code: Apex code to validate
37
+
38
+ Returns:
39
+ Tuple of (is_valid, list_of_issues)
40
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  issues = []
42
 
43
+ # Basic syntax checks
44
+ if not code.strip():
45
+ issues.append({"type": "error", "message": "Empty code provided", "line": 0})
46
+ return False, issues
47
+
48
+ # Check for balanced brackets
49
+ brackets = {"(": ")", "{": "}", "[": "]"}
50
+ stack = []
51
+
52
+ for i, char in enumerate(code):
53
+ if char in brackets:
54
+ stack.append((char, i))
55
+ elif char in brackets.values():
56
+ if not stack:
57
+ issues.append({"type": "error", "message": f"Unmatched closing bracket '{char}'", "line": code[:i].count('\n') + 1})
58
+ else:
59
+ open_char, _ = stack.pop()
60
+ if brackets[open_char] != char:
61
+ issues.append({"type": "error", "message": f"Mismatched bracket pair", "line": code[:i].count('\n') + 1})
62
+
63
+ # Check for unclosed brackets
64
+ if stack:
65
+ for char, pos in stack:
66
+ issues.append({"type": "error", "message": f"Unclosed bracket '{char}'", "line": code[:pos].count('\n') + 1})
67
+
68
+ # Check for missing semicolons (basic check)
69
+ lines = code.split('\n')
70
+ for line_num, line in enumerate(lines, 1):
71
+ stripped = line.strip()
72
+ if stripped and not stripped.endswith((';', '{', '}', '//', '/*', '*/', '*')):
73
+ if any(keyword in stripped for keyword in ['if', 'for', 'while', 'try', 'catch', 'class', 'trigger']):
74
+ continue
75
+ if re.search(r'\b(insert|update|delete|upsert|return)\b', stripped):
76
+ issues.append({"type": "warning", "message": "Possible missing semicolon", "line": line_num})
77
+
78
+ is_valid = not any(issue["type"] == "error" for issue in issues)
79
+ return is_valid, issues
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
+ def perform_skeptical_evaluation(code: str, code_type: str = "trigger") -> Dict[str, List[str]]:
82
+ """
83
+ Perform skeptical evaluation of code to find potential issues.
84
+
85
+ Args:
86
+ code: Code to evaluate
87
+ code_type: Type of code (trigger, class, object)
88
+
89
+ Returns:
90
+ Dictionary with categorized issues
91
+ """
92
  evaluation = {
 
93
  "security_concerns": [],
94
  "performance_issues": [],
95
+ "b2b_commerce_issues": [],
96
+ "best_practice_violations": []
97
  }
98
 
 
 
 
 
99
  # Security checks
100
+ if re.search(r"String\.format|String\.valueOf.*user", code, re.IGNORECASE):
101
+ evaluation["security_concerns"].append("Potential SOQL injection vulnerability")
102
+
103
+ if re.search(r"password\s*=\s*['\"][^'\"]+['\"]", code, re.IGNORECASE):
104
+ evaluation["security_concerns"].append("Hardcoded credentials detected")
 
 
 
 
 
 
105
 
106
  # Performance checks
107
+ if re.search(r"for\s*\([^)]*:[^)]*\)\s*\{[^}]*\b(insert|update|delete|upsert)\b", code):
108
+ evaluation["performance_issues"].append("DML operation inside loop - governor limit risk")
109
+
110
+ if re.search(r"for\s*\([^)]*:[^)]*\)\s*\{[^}]*\bSELECT\b", code, re.IGNORECASE):
111
+ evaluation["performance_issues"].append("SOQL query inside loop - governor limit risk")
 
 
 
 
 
 
 
 
 
112
 
113
  # B2B Commerce specific checks
114
+ if re.search(B2B_COMMERCE_PATTERNS["cloudcraze_reference"], code):
115
+ evaluation["b2b_commerce_issues"].append("CloudCraze references need migration to B2B LEX")
116
+
117
+ if code_type == "trigger" and not re.search(r"Trigger\.(isInsert|isUpdate|isDelete)", code):
118
+ evaluation["best_practice_violations"].append("Missing trigger context checks")
 
 
 
 
 
 
 
 
119
 
120
  return evaluation
121
 
122
+ def extract_code_blocks(response: str) -> str:
123
+ """
124
+ Extract code blocks from AI response.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
 
126
+ Args:
127
+ response: AI model response
128
+
129
+ Returns:
130
+ Extracted code or empty string if none found
131
+ """
132
+ # Look for code blocks with various delimiters
133
+ patterns = [
134
+ r"```(?:apex|java|salesforce)?\s*(.*?)```",
135
+ r"```\s*(.*?)```",
136
+ r"<code>(.*?)</code>",
137
+ r"`([^`]+)`"
138
+ ]
139
+
140
+ for pattern in patterns:
141
+ matches = re.findall(pattern, response, re.DOTALL | re.IGNORECASE)
142
+ if matches:
143
+ # Return the longest match (most likely to be the main code block)
144
+ return max(matches, key=len).strip()
145
+
146
+ return ""
147
 
148
  def format_structured_explanation(response: str, code_output: str) -> str:
149
+ """
150
+ Format the AI response into a structured explanation.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
 
152
+ Args:
153
+ response: Raw AI response
154
+ code_output: Extracted code
155
+
156
+ Returns:
157
+ Formatted explanation
158
+ """
159
+ explanation = ""
160
+
161
+ # Extract key changes section
162
+ key_changes_match = re.search(r"## KEY CHANGES.*?(?=##|$)", response, re.DOTALL | re.IGNORECASE)
163
+ if key_changes_match:
164
+ explanation += "**Key Changes:**\n" + key_changes_match.group(0).replace("## KEY CHANGES", "").strip() + "\n\n"
165
+
166
+ # Extract critical issues section
167
+ critical_issues_match = re.search(r"## CRITICAL ISSUES.*?(?=##|$)", response, re.DOTALL | re.IGNORECASE)
168
+ if critical_issues_match:
169
+ explanation += "**Critical Issues Fixed:**\n" + critical_issues_match.group(0).replace("## CRITICAL ISSUES", "").strip() + "\n\n"
170
+
171
+ # Extract warnings section
172
+ warnings_match = re.search(r"## (?:REMAINING )?WARNINGS.*?(?=##|$)", response, re.DOTALL | re.IGNORECASE)
173
+ if warnings_match:
174
+ explanation += "**Remaining Warnings:**\n" + warnings_match.group(0).replace("## REMAINING WARNINGS", "").replace("## WARNINGS", "").strip()
175
+
176
+ return explanation if explanation else "Analysis completed. See full response for details."
177
 
178
  def format_object_conversion_explanation(response: str, code_output: str) -> str:
179
+ """
180
+ Format object conversion explanation.
181
+
182
+ Args:
183
+ response: Raw AI response
184
+ code_output: Extracted code
185
+
186
+ Returns:
187
+ Formatted explanation
188
+ """
189
+ explanation = ""
190
 
191
+ # Extract mapping section
192
+ mapping_match = re.search(r"## B2B LEX OBJECT MAPPING.*?(?=##|$)", response, re.DOTALL | re.IGNORECASE)
193
  if mapping_match:
194
+ explanation += "**Object Mapping:**\n" + mapping_match.group(0).replace("## B2B LEX OBJECT MAPPING", "").strip() + "\n\n"
195
 
196
+ # Extract field mappings
197
+ field_mappings_match = re.search(r"## FIELD MAPPINGS.*?(?=##|$)", response, re.DOTALL | re.IGNORECASE)
198
+ if field_mappings_match:
199
+ explanation += "**Field Mappings:**\n" + field_mappings_match.group(0).replace("## FIELD MAPPINGS", "").strip() + "\n\n"
200
 
201
  # Extract migration steps
202
+ steps_match = re.search(r"## MIGRATION STEPS.*?(?=##|$)", response, re.DOTALL | re.IGNORECASE)
203
  if steps_match:
204
+ explanation += "**Migration Steps:**\n" + steps_match.group(0).replace("## MIGRATION STEPS", "").strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
 
206
+ return explanation if explanation else "Conversion completed. See full response for details."
 
 
 
 
 
 
 
 
 
 
 
 
 
207
 
208
  def extract_validation_metrics(validation_text: str) -> Optional[Dict[str, float]]:
209
+ """
210
+ Extract validation metrics from AI response.
211
+
212
+ Args:
213
+ validation_text: AI validation response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
 
215
+ Returns:
216
+ Dictionary of metrics or None if parsing fails
217
+ """
218
+ try:
219
+ # Look for JSON block in the response
220
+ json_match = re.search(r"```json\s*(.*?)```", validation_text, re.DOTALL)
221
+ if json_match:
222
+ metrics_data = json.loads(json_match.group(1))
223
+ return {
224
+ "quality_rating": float(metrics_data.get("quality_rating", 0)),
225
+ "accuracy": float(metrics_data.get("accuracy", 0)),
226
+ "completeness": float(metrics_data.get("completeness", 0)),
227
+ "best_practices_alignment": float(metrics_data.get("best_practices_alignment", 0)),
228
+ "syntax_validity": float(metrics_data.get("syntax_validity", 0)),
229
+ "security_score": float(metrics_data.get("security_score", 0)),
230
+ "performance_score": float(metrics_data.get("performance_score", 0))
231
+ }
232
+ except (json.JSONDecodeError, ValueError, KeyError) as e:
233
+ logger.warning(f"Failed to parse validation metrics: {e}")
234
+
235
+ return None
236
 
237
+ def normalize_metrics(metrics: Dict[str, float]) -> Dict[str, float]:
238
+ """
239
+ Normalize metrics to 0-1 scale.
240
+
241
+ Args:
242
+ metrics: Raw metrics dictionary
243
+
244
+ Returns:
245
+ Normalized metrics
246
+ """
247
+ normalized = {}
248
+ for key, value in metrics.items():
249
+ if key == "quality_rating":
250
+ normalized[key] = value / 10.0 # Convert 1-10 scale to 0-1
251
+ else:
252
+ normalized[key] = max(0.0, min(1.0, value)) # Clamp to 0-1
253
+
254
  return normalized
255
 
256
+ def generate_test_cases(code: str, code_type: str = "trigger") -> str:
257
+ """
258
+ Generate basic test case templates for the given code.
 
 
 
 
 
 
 
 
 
259
 
260
+ Args:
261
+ code: Code to generate tests for
262
+ code_type: Type of code (trigger, class, object)
263
+
264
+ Returns:
265
+ Test case template as string
266
+ """
267
+ if code_type == "trigger":
268
+ # Extract trigger name and object
269
+ trigger_match = re.search(r"trigger\s+(\w+)\s+on\s+(\w+)", code, re.IGNORECASE)
270
+ if trigger_match:
271
+ trigger_name = trigger_match.group(1)
272
+ sobject_name = trigger_match.group(2)
273
+
274
+ return f"""@isTest
275
+ public class {trigger_name}Test {{
276
  @isTest
277
+ static void testInsert() {{
278
+ // Test insert scenario
279
+ {sobject_name} testRecord = new {sobject_name}();
280
+ // Set required fields
 
 
281
 
282
  Test.startTest();
283
+ insert testRecord;
284
  Test.stopTest();
285
 
286
+ // Assert expected behavior
 
287
  }}
288
 
289
  @isTest
290
+ static void testUpdate() {{
291
+ // Test update scenario
292
+ {sobject_name} testRecord = new {sobject_name}();
293
+ // Set required fields
294
+ insert testRecord;
295
+
296
+ Test.startTest();
297
+ // Modify fields
298
+ update testRecord;
299
+ Test.stopTest();
300
+
301
+ // Assert expected behavior
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
  }}
303
+ }}"""
304
 
305
+ return "// Test case template not available for this code type"