1313import {
1414 DeleteObjectCommand ,
1515 DeleteObjectsCommand ,
16+ HeadObjectCommand ,
1617 ListObjectsV2Command ,
1718 PutObjectCommand ,
1819} from '@aws-sdk/client-s3' ;
@@ -489,8 +490,9 @@ export async function createReportOpportunitySuggestion(
489490 reportMarkdown ,
490491 auditData ,
491492 log ,
493+ context ,
492494) {
493- const suggestions = createReportOpportunitySuggestionInstance ( reportMarkdown ) ;
495+ const suggestions = createReportOpportunitySuggestionInstance ( reportMarkdown , context ) ;
494496
495497 try {
496498 const suggestion = await opportunity . addSuggestions ( suggestions ) ;
@@ -516,6 +518,7 @@ export async function createOrUpdateDeviceSpecificSuggestion(
516518 deviceType ,
517519 auditData ,
518520 log ,
521+ context = { } ,
519522) {
520523 const createSuggestionInstance = createDeviceSpecificSuggestionInstance ;
521524
@@ -534,7 +537,7 @@ export async function createOrUpdateDeviceSpecificSuggestion(
534537 currentSuggestionValue ,
535538 deviceType ,
536539 reportMarkdown ,
537- log ,
540+ context ,
538541 ) ;
539542
540543 // Update only the suggestionValue field to avoid ElectroDB timestamp conflicts
@@ -546,7 +549,7 @@ export async function createOrUpdateDeviceSpecificSuggestion(
546549 return { suggestion : existingSuggestion } ;
547550 } else {
548551 // Create new suggestion
549- suggestions = createSuggestionInstance ( null , deviceType , reportMarkdown , log ) ;
552+ suggestions = createSuggestionInstance ( null , deviceType , reportMarkdown , context ) ;
550553
551554 const suggestion = await opportunity . addSuggestions ( suggestions ) ;
552555
@@ -834,6 +837,7 @@ export async function generateReportOpportunity(
834837 deviceType . toLowerCase ( ) ,
835838 auditData ,
836839 log ,
840+ context ,
837841 ) ;
838842 } catch ( error ) {
839843 log . error ( `[A11yProcessingError] Failed to create/update device-specific suggestion for ${ reportName } ` , error . message ) ;
@@ -973,25 +977,98 @@ export async function sendRunImportMessage(
973977 } ) ;
974978}
975979
980+ /**
981+ * Retrieves code path information saved in S3 bucket
982+ * @param {Object } site - The site object
983+ * @param {string } opportunityType - Opportunity type for logging
984+ * @param {Object } context - The context object containing log, s3Client, env
985+ * @returns {Promise<Object|null> } Object containing codeBucket and codePath, or null if should skip
986+ */
987+ async function getCodeInfo ( site , opportunityType , context ) {
988+ const { log, s3Client, env } = context ;
989+ const siteId = site . getId ( ) ;
990+ const deliveryType = site . getDeliveryType ( ) ;
991+ const codeConfig = site . getCode ( ) ;
992+
993+ // For aem_edge delivery type, proceed without codeConfig
994+ if ( ! codeConfig ) {
995+ if ( deliveryType === 'aem_edge' ) {
996+ return {
997+ codeBucket : env . S3_IMPORTER_BUCKET_NAME ,
998+ codePath : '' ,
999+ } ;
1000+ }
1001+ log . warn ( `[${ opportunityType } ] [Site Id: ${ siteId } ] No code configuration found for site` ) ;
1002+ return null ;
1003+ }
1004+
1005+ const {
1006+ type : source , owner, repo, ref,
1007+ } = codeConfig ;
1008+
1009+ const codeBucket = env . S3_IMPORTER_BUCKET_NAME ;
1010+ const codePath = `code/${ siteId } /${ source } /${ owner } /${ repo } /${ ref } /repository.zip` ;
1011+
1012+ // Verify if the file exists in S3 bucket
1013+ let fileExists = false ;
1014+ try {
1015+ await s3Client . send ( new HeadObjectCommand ( {
1016+ Bucket : codeBucket ,
1017+ Key : codePath ,
1018+ } ) ) ;
1019+ fileExists = true ;
1020+ log . info ( `[${ opportunityType } ] [Site Id: ${ siteId } ] Code file verified in S3 bucket` ) ;
1021+ } catch ( error ) {
1022+ if ( error . name === 'NotFound' || error . $metadata ?. httpStatusCode === 404 ) {
1023+ log . warn ( `[${ opportunityType } ] [Site Id: ${ siteId } ] Code file not found in S3: ${ codePath } ` ) ;
1024+ } else {
1025+ log . error ( `[${ opportunityType } ] [Site Id: ${ siteId } ] Error checking S3 file: ${ error . message } ` ) ;
1026+ }
1027+ }
1028+
1029+ // Handle based on file existence and delivery type
1030+ if ( ! fileExists && deliveryType !== 'aem_edge' ) {
1031+ return null ;
1032+ }
1033+
1034+ return {
1035+ codeBucket,
1036+ codePath : ( ! fileExists && deliveryType === 'aem_edge' ) ? '' : codePath ,
1037+ } ;
1038+ }
1039+
9761040/**
9771041 * Groups suggestions by URL, source, and issue type, then sends messages
978- * to the importer worker for code-fix generation
1042+ * directly to Mystique for code-fix generation.
1043+ * Verifies the code file exists in S3 bucket before sending messages.
1044+ * For aem_edge delivery type, sends message with empty codePath if file doesn't exist.
1045+ * For other delivery types, skips sending if file doesn't exist.
9791046 *
9801047 * @param {Object } opportunity - The opportunity object containing suggestions
9811048 * @param {string } auditId - The audit ID
982- * @param {Object } context - The context object containing log, sqs, env, and site
1049+ * @param {Object } site - The site object
1050+ * @param {Object } context - The context object containing log, sqs, env, s3Client, and site
9831051 * @returns {Promise<void> }
9841052 */
985- export async function sendCodeFixMessagesToImporter ( opportunity , auditId , context ) {
1053+ export async function sendCodeFixMessagesToMystique ( opportunity , auditId , site , context ) {
9861054 const {
987- log, sqs, env, site ,
1055+ log, sqs, env,
9881056 } = context ;
9891057
9901058 const siteId = opportunity . getSiteId ( ) ;
9911059 const baseUrl = site . getBaseURL ( ) ;
9921060 const opportunityType = opportunity . getType ( ) ;
9931061
9941062 try {
1063+ // Verify and get code path information
1064+ const codeInfo = await getCodeInfo ( site , opportunityType , context ) ;
1065+
1066+ if ( ! codeInfo ) {
1067+ return ;
1068+ }
1069+
1070+ const { codeBucket, codePath } = codeInfo ;
1071+
9951072 // Get all suggestions from the opportunity
9961073 const suggestions = await opportunity . getSuggestions ( ) ;
9971074 if ( ! suggestions || suggestions . length === 0 ) {
@@ -1004,16 +1081,16 @@ export async function sendCodeFixMessagesToImporter(opportunity, auditId, contex
10041081
10051082 suggestions . forEach ( ( suggestion ) => {
10061083 const suggestionData = suggestion . getData ( ) ;
1007- const { url, source = 'default' , issues } = suggestionData ;
1084+ const { url, source : formSource = 'default' , issues } = suggestionData ;
10081085
10091086 // By design, data.issues will always have length 1
10101087 if ( issues && issues . length > 0 ) {
10111088 const issueType = issues [ 0 ] . type ;
1012- const groupKey = `${ url } |${ source } |${ issueType } ` ;
1089+ const groupKey = `${ url } |${ formSource } |${ issueType } ` ;
10131090 if ( ! groupedSuggestions . has ( groupKey ) ) {
10141091 groupedSuggestions . set ( groupKey , {
10151092 url,
1016- source,
1093+ source : formSource ,
10171094 issueType,
10181095 suggestionIds : [ ] ,
10191096 } ) ;
@@ -1028,33 +1105,32 @@ export async function sendCodeFixMessagesToImporter(opportunity, auditId, contex
10281105
10291106 const messagePromises = Array . from ( groupedSuggestions . values ( ) ) . map ( async ( group ) => {
10301107 const message = {
1031- type : 'code' ,
1108+ type : `codefix: ${ opportunityType } ` ,
10321109 siteId,
1033- forward : {
1034- queue : env . QUEUE_SPACECAT_TO_MYSTIQUE ,
1035- type : `codefix:${ opportunityType } ` ,
1036- siteId,
1037- auditId,
1038- url : baseUrl ,
1039- deliveryType : site . getDeliveryType ( ) ,
1040- data : {
1041- opportunityId : opportunity . getId ( ) ,
1042- suggestionIds : group . suggestionIds ,
1043- } ,
1110+ auditId,
1111+ url : baseUrl ,
1112+ deliveryType : site . getDeliveryType ( ) ,
1113+ source : 'spacecat' ,
1114+ observation : 'Auto optimize form accessibility' ,
1115+ data : {
1116+ opportunityId : opportunity . getId ( ) ,
1117+ suggestionIds : group . suggestionIds ,
1118+ codeBucket,
1119+ codePath,
10441120 } ,
10451121 } ;
10461122
10471123 try {
1048- await sqs . sendMessage ( env . IMPORT_WORKER_QUEUE_URL , message ) ;
1049- log . info ( `[${ opportunityType } ] [Site Id: ${ siteId } ] Sent code-fix message to importer for URL: ${ group . url } , source: ${ group . source } , issueType: ${ group . issueType } , suggestions: ${ group . suggestionIds . length } ` ) ;
1124+ await sqs . sendMessage ( env . QUEUE_SPACECAT_TO_MYSTIQUE , message ) ;
1125+ log . info ( `[${ opportunityType } ] [Site Id: ${ siteId } ] Sent code-fix message to Mystique for URL: ${ group . url } , source: ${ group . source } , issueType: ${ group . issueType } , suggestions: ${ group . suggestionIds . length } ` ) ;
10501126 } catch ( error ) {
10511127 log . error ( `[${ opportunityType } ] [Site Id: ${ siteId } ] Failed to send code-fix message for URL: ${ group . url } , error: ${ error . message } ` ) ;
10521128 }
10531129 } ) ;
10541130
10551131 await Promise . all ( messagePromises ) ;
1056- log . info ( `[${ opportunityType } ] [Site Id: ${ siteId } ] Completed sending ${ messagePromises . length } code-fix messages to importer ` ) ;
1132+ log . info ( `[${ opportunityType } ] [Site Id: ${ siteId } ] Completed sending ${ messagePromises . length } code-fix messages to Mystique ` ) ;
10571133 } catch ( error ) {
1058- log . error ( `[${ opportunityType } ] [Site Id: ${ siteId } ] Error in sendCodeFixMessagesToImporter : ${ error . message } ` ) ;
1134+ log . error ( `[${ opportunityType } ] [Site Id: ${ siteId } ] Error in sendCodeFixMessagesToMystique : ${ error . message } ` ) ;
10591135 }
10601136}
0 commit comments