Skip to content

Commit 53dd48b

Browse files
authored
fix: stop saving issues in opportunity object (#1614)
Please ensure your pull request adheres to the following guidelines: - [ ] make sure to link the related issues in this description - [ ] when merging / squashing, make sure the fixed issue references are visible in the commits, for easy compilation of release notes - [ ] If data sources for any opportunity has been updated/added, please update the [wiki](https://wiki.corp.adobe.com/display/AEMSites/Data+Sources+for+Opportunities) for same opportunity. ## Related Issues Thanks for contributing!
1 parent dc572f7 commit 53dd48b

File tree

6 files changed

+322
-993
lines changed

6 files changed

+322
-993
lines changed

src/accessibility/utils/data-processing.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,10 +1081,12 @@ export async function sendCodeFixMessagesToMystique(opportunity, auditId, site,
10811081

10821082
suggestions.forEach((suggestion) => {
10831083
const suggestionData = suggestion.getData();
1084-
const { url, source: formSource = 'default', issues } = suggestionData;
1084+
const {
1085+
url, source: formSource = 'default', issues, aiGenerated,
1086+
} = suggestionData;
10851087

10861088
// By design, data.issues will always have length 1
1087-
if (issues && issues.length > 0) {
1089+
if (issues && issues.length > 0 && !aiGenerated) {
10881090
const issueType = issues[0].type;
10891091
const groupKey = `${url}|${formSource}|${issueType}`;
10901092
if (!groupedSuggestions.has(groupKey)) {

src/accessibility/utils/generate-individual-opportunities.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -534,10 +534,7 @@ export async function createIndividualOpportunitySuggestions(
534534
// Rank by total occurrences across all issues for this URL
535535
rank: urlData.issues.reduce((total, issue) => total + issue.occurrences, 0),
536536
data: {
537-
url: urlData.url,
538-
type: urlData.type,
539-
issues: urlData.issues, // Array of formatted accessibility issues
540-
...(urlData.source && { source: urlData.source }),
537+
...urlData,
541538
jiraLink: '',
542539
},
543540
}),

src/forms-opportunities/oppty-handlers/accessibility-handler.js

Lines changed: 55 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,7 @@
1313
import { ok, notFound } from '@adobe/spacecat-shared-http-utils';
1414
import { Audit } from '@adobe/spacecat-shared-data-access';
1515
import { FORM_OPPORTUNITY_TYPES, formOpportunitiesMap } from '../constants.js';
16-
import {
17-
getSuccessCriteriaDetails,
18-
sendMessageToFormsQualityAgent,
19-
sendMessageToMystiqueForGuidance,
20-
} from '../utils.js';
16+
import { getSuccessCriteriaDetails } from '../utils.js';
2117
import { updateStatusToIgnored } from '../../accessibility/utils/scrape-utils.js';
2218
import {
2319
aggregateA11yIssuesByOppType,
@@ -86,8 +82,10 @@ export async function createFormAccessibilitySuggestionsFromMystique(
8682
url: pageUrl,
8783
...(source && { source }),
8884
issues: [formattedIssue],
89-
aiGenerated: issue.aiGenerated || false,
9085
};
86+
if ('aiGenerated' in issue) {
87+
urlObject.aiGenerated = issue.aiGenerated;
88+
}
9189

9290
formAccessibilityData.push(urlObject);
9391
});
@@ -123,162 +121,44 @@ export async function createFormAccessibilitySuggestionsFromMystique(
123121
* Create a11y opportunity for the given siteId and auditId
124122
* @param {string} auditId - The auditId of the audit
125123
* @param {string} siteId - The siteId of the site
126-
* @param {object} a11yData - The a11y data
127124
* @param {object} context - The context object
128125
* @returns {Promise<void>}
129126
*/
130-
async function createOrUpdateOpportunity(auditId, siteId, a11yData, context, opportunityId = null) {
127+
async function createOpportunity(auditId, siteId, context) {
131128
const {
132129
dataAccess, log,
133130
} = context;
134131
const { Opportunity } = dataAccess;
135132
let opportunity = null;
136133

137134
try {
138-
if (opportunityId) {
139-
opportunity = await Opportunity.findById(opportunityId);
140-
}
141-
142-
if (a11yData?.length === 0) {
143-
log.debug(`[Form Opportunity] [Site Id: ${siteId}] No a11y data found to create or update opportunity `);
144-
return opportunity;
145-
}
146-
147-
const filteredA11yData = a11yData.filter((a11y) => a11y.a11yIssues?.length > 0);
148-
if (filteredA11yData.length === 0) {
149-
log.debug(`[Form Opportunity] [Site Id: ${siteId}] No a11y issues found to create or update opportunity`);
150-
return opportunity;
151-
}
152-
153-
const a11yOpptyData = filteredA11yData.map((a11yOpty) => {
154-
const a11yIssues = a11yOpty.a11yIssues.map((issue) => ({
155-
...issue,
156-
successCriterias: Array.isArray(issue.successCriterias) && issue.successCriterias.length > 0
157-
? issue.successCriterias.map((criteria) => getSuccessCriteriaDetails(criteria))
158-
: [],
159-
}));
160-
return {
161-
form: a11yOpty.form,
162-
formSource: a11yOpty.formSource,
163-
a11yIssues,
164-
};
165-
});
166-
167-
// Update existing opportunity
168-
if (opportunity) {
169-
const data = opportunity.getData();
170-
const existingA11yData = data.accessibility;
171-
172-
// Merge new data with existing data
173-
const mergedData = [...existingA11yData];
174-
a11yOpptyData.forEach((newForm) => {
175-
const existingFormIndex = mergedData.findIndex(
176-
(form) => form.form === newForm.form && form.formSource === newForm.formSource,
177-
);
178-
179-
if (existingFormIndex !== -1) {
180-
// Update existing form's a11yIssues
181-
mergedData[existingFormIndex].a11yIssues = [
182-
...mergedData[existingFormIndex].a11yIssues,
183-
...newForm.a11yIssues,
184-
];
185-
} else {
186-
// Add new form data
187-
mergedData.push({
188-
form: newForm.form,
189-
formSource: newForm.formSource,
190-
a11yIssues: newForm.a11yIssues,
191-
});
192-
}
193-
});
194-
195-
opportunity.setData({
196-
...data,
197-
accessibility: mergedData,
198-
});
199-
opportunity = await opportunity.save();
200-
log.info(`[Form Opportunity] [Site Id: ${siteId}] Updated existing a11y opportunity`);
201-
}
202-
203-
// If no existing opportunity, create new opportunity
204-
if (!opportunity) {
205-
// change status to IGNORED for older opportunities
206-
await updateStatusToIgnored(dataAccess, siteId, log, null, filterAccessibilityOpportunities);
135+
// change status to IGNORED for older opportunities
136+
await updateStatusToIgnored(dataAccess, siteId, log, null, filterAccessibilityOpportunities);
207137

208-
const opportunityData = {
209-
siteId,
210-
auditId,
211-
runbook: 'https://adobe.sharepoint.com/:w:/s/AEM_Forms/Ebpoflp2gHFNl4w5-9C7dFEBBHHE4gTaRzHaofqSxJMuuQ?e=Ss6mep',
212-
type: FORM_OPPORTUNITY_TYPES.FORM_A11Y,
213-
origin: 'AUTOMATION',
214-
title: 'Accessibility - Assistive technology is incompatible on form',
215-
description: '',
216-
tags: [
217-
'Forms Accessibility',
218-
],
219-
data: {
220-
accessibility: a11yOpptyData,
221-
},
222-
};
223-
opportunity = await Opportunity.create(opportunityData);
224-
log.debug(`[Form Opportunity] [Site Id: ${siteId}] Created new a11y opportunity`);
225-
}
138+
const opportunityData = {
139+
siteId,
140+
auditId,
141+
runbook: 'https://adobe.sharepoint.com/:w:/s/AEM_Forms/Ebpoflp2gHFNl4w5-9C7dFEBBHHE4gTaRzHaofqSxJMuuQ?e=Ss6mep',
142+
type: FORM_OPPORTUNITY_TYPES.FORM_A11Y,
143+
origin: 'AUTOMATION',
144+
title: 'Accessibility - Assistive technology is incompatible on form',
145+
description: '',
146+
tags: [
147+
'Forms Accessibility',
148+
],
149+
data: {
150+
dataSources: ['axe-core'],
151+
},
152+
};
153+
opportunity = await Opportunity.create(opportunityData);
154+
log.debug(`[Form Opportunity] [Site Id: ${siteId}] Created new a11y opportunity`);
226155
} catch (e) {
227-
log.error(`[Form Opportunity] [Site Id: ${siteId}] Failed to create/update a11y opportunity with error: ${e.message}`);
228-
throw new Error(`[Form Opportunity] [Site Id: ${siteId}] Failed to create/update a11y opportunity with error: ${e.message}`);
156+
log.error(`[Form Opportunity] [Site Id: ${siteId}] Failed to create a11y opportunity with error: ${e.message}`);
157+
throw new Error(`[Form Opportunity] [Site Id: ${siteId}] Failed to create a11y opportunity with error: ${e.message}`);
229158
}
230159
return opportunity;
231160
}
232161

233-
function getWCAGCriteriaString(criteria) {
234-
const { name, criteriaNumber } = getSuccessCriteriaDetails(criteria);
235-
return `${criteriaNumber} ${name}`;
236-
}
237-
238-
/**
239-
* Transforms axe-core violation format to the expected output format
240-
* This is a temporary function to transform sites' accessibility schema to forms' old schema
241-
* to prevent impact on UI
242-
* @param {Object} axeData - The axe-core violation data
243-
* @returns {Object} Form with accessibility issues containing form, formSource, and a11yIssues
244-
*/
245-
export function transformAxeViolationsToA11yData(axeData) {
246-
const { violations, url, formSource } = axeData;
247-
const a11yIssues = [];
248-
249-
// Process critical violations
250-
if (violations?.critical?.items) {
251-
Object.values(violations.critical.items).forEach((violation) => {
252-
a11yIssues.push({
253-
issue: violation.description,
254-
level: violation.level,
255-
successCriterias: violation.successCriteriaTags.map(getWCAGCriteriaString),
256-
htmlWithIssues: violation.htmlWithIssues,
257-
recommendation: violation.failureSummary,
258-
});
259-
});
260-
}
261-
262-
// Process serious violations
263-
if (violations?.serious?.items) {
264-
Object.values(violations.serious.items).forEach((violation) => {
265-
a11yIssues.push({
266-
issue: violation.description,
267-
level: violation.level,
268-
successCriterias: violation.successCriteriaTags.map(getWCAGCriteriaString),
269-
htmlWithIssues: violation.htmlWithIssues,
270-
recommendation: violation.failureSummary,
271-
});
272-
});
273-
}
274-
275-
return {
276-
form: url,
277-
formSource,
278-
a11yIssues,
279-
};
280-
}
281-
282162
/**
283163
* Creates individual suggestions for form accessibility issues
284164
* This method processes the aggregated form data and creates individual suggestions
@@ -380,34 +260,37 @@ export async function createAccessibilityOpportunity(auditData, context) {
380260
const aggregatedData = aggregationResult.finalResultFiles.current;
381261
const a11yData = [];
382262

263+
// Get total violations from overall data
264+
const totalViolations = aggregatedData.overall?.violations?.total || 0;
265+
383266
// Process each form identified by composite key (URL + formSource)
384-
Object.entries(aggregatedData).forEach(([key, data]) => {
267+
Object.entries(aggregatedData).forEach(([key]) => {
385268
// Skip the 'overall' key as it contains summary data
386269
if (key === 'overall') return;
387270

388-
const { violations } = data;
389-
390271
// Extract URL and formSource from the composite key
391272
const [url, formSource] = key.includes(URL_SOURCE_SEPARATOR)
392273
? key.split(URL_SOURCE_SEPARATOR)
393274
: [key, null];
394275

395-
// Transform violations to the expected format
396-
const transformedData = transformAxeViolationsToA11yData({
397-
violations,
398-
url,
276+
// Add all forms to a11yData
277+
a11yData.push({
278+
form: url,
399279
formSource,
400280
});
401-
402-
a11yData.push(transformedData);
403281
});
404282

405-
// Create opportunity
406-
const opportunity = await createOrUpdateOpportunity(auditId, siteId, a11yData, context);
283+
// Create opportunity only if there are violations
284+
let opportunity = null;
285+
if (totalViolations > 0) {
286+
opportunity = await createOpportunity(auditId, siteId, context);
407287

408-
// Create individual suggestions for the opportunity (if opportunity was created/updated)
409-
if (opportunity) {
410-
await createFormAccessibilityIndividualSuggestions(aggregatedData, opportunity, context);
288+
// Create individual suggestions for the opportunity (if opportunity was created/updated)
289+
if (opportunity) {
290+
await createFormAccessibilityIndividualSuggestions(aggregatedData, opportunity, context);
291+
}
292+
} else {
293+
log.debug(`[Form Opportunity] [Site Id: ${siteId}] No accessibility violations found, skipping opportunity creation`);
411294
}
412295
// Send message to importer-worker to create/update a11y metrics
413296
log.debug(`[FormA11yAudit] [Site Id: ${siteId}] Sending message to importer-worker to create/update a11y metrics`);
@@ -449,7 +332,7 @@ export async function createAccessibilityOpportunity(auditData, context) {
449332

450333
export default async function handler(message, context) {
451334
const { log, dataAccess } = context;
452-
const { Site } = dataAccess;
335+
const { Site, Opportunity } = dataAccess;
453336
const { auditId, siteId, data } = message;
454337
const { opportunityId, a11y } = data;
455338
log.debug(`[Form Opportunity] [Site Id: ${siteId}] Received message in accessibility handler: ${JSON.stringify(message, null, 2)}`);
@@ -461,13 +344,16 @@ export default async function handler(message, context) {
461344
}
462345

463346
try {
464-
const opportunity = await createOrUpdateOpportunity(
465-
auditId,
466-
siteId,
467-
a11y,
468-
context,
469-
opportunityId,
470-
);
347+
let opportunity = null;
348+
if (opportunityId) {
349+
opportunity = await Opportunity.findById(opportunityId);
350+
if (!opportunity) {
351+
log.error(`[Form Opportunity] [Site Id: ${siteId}] A11y opportunity not found`);
352+
return notFound('A11y opportunity not found');
353+
}
354+
} else {
355+
opportunity = await createOpportunity(auditId, siteId, context);
356+
}
471357
if (!opportunity) {
472358
log.info(`[Form Opportunity] [Site Id: ${siteId}] A11y opportunity not detected, skipping guidance`);
473359
return ok();
@@ -488,16 +374,7 @@ export default async function handler(message, context) {
488374
} else {
489375
log.info(`[Form Opportunity] [Site Id: ${siteId}] ${opportunity.getType()}-auto-fix is disabled for site, skipping code-fix generation`);
490376
}
491-
492-
log.info(`[Form Opportunity] [Site Id: ${siteId}] a11y opportunity: ${JSON.stringify(opportunity, null, 2)}`);
493-
const opportunityData = opportunity.getData();
494-
const a11yData = opportunityData.accessibility;
495-
// eslint-disable-next-line max-len
496-
const formsList = a11yData.filter((item) => !item.formDetails).map((item) => ({ form: item.form, formSource: item.formSource }));
497-
log.info(`[Form Opportunity] [Site Id: ${siteId}] formsList: ${JSON.stringify(formsList, null, 2)}`);
498-
await (formsList.length === 0
499-
? sendMessageToMystiqueForGuidance(context, opportunity)
500-
: sendMessageToFormsQualityAgent(context, opportunity, formsList));
377+
// TODO: Send message to mystique for guidance
501378
} catch (error) {
502379
log.error(`[Form Opportunity] [Site Id: ${siteId}] Failed to process a11y opportunity from mystique: ${error.message}`);
503380
}

src/forms-opportunities/utils.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -521,17 +521,15 @@ export async function sendMessageToMystiqueForGuidance(context, opportunity) {
521521
if (opportunity) {
522522
log.debug(`Received forms opportunity for guidance: ${JSON.stringify(opportunity)}`);
523523
const opptyData = JSON.parse(JSON.stringify(opportunity));
524-
// Normalize type: convert forms-accessibility → forms-a11y
525-
const normalizedType = opptyData.type === 'form-accessibility' ? 'forms-a11y' : opptyData.type;
526524
const mystiqueMessage = {
527-
type: `guidance:${normalizedType}`,
525+
type: `guidance:${opptyData.type}`,
528526
siteId: opptyData.siteId,
529527
auditId: opptyData.auditId,
530528
deliveryType: site ? site.getDeliveryType() : 'aem_cs',
531529
time: new Date().toISOString(),
532530
// keys inside data should follow snake case and outside should follow camel case
533531
data: {
534-
url: opptyData.type === 'form-accessibility' ? opptyData.data?.accessibility?.[0]?.form || '' : opptyData.data?.form || '',
532+
url: opptyData.data?.form,
535533
cr: opptyData.data?.trackedFormKPIValue || 0,
536534
metrics: opptyData.data?.metrics || [],
537535
cta_source: opptyData.data?.formNavigation?.source || '',

0 commit comments

Comments
 (0)