Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: updating README for adding Opportunities and Suggestions #657

Merged
merged 1 commit into from
Feb 17, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 56 additions & 70 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ $ npm test
$ npm run lint
```

## Message Body Formats
## Message Body Formats

Audit worker consumes the `AUDIT_JOBS_QUEUE` queue, performs the requested audit, then queues the result to `AUDIT_RESULTS_QUEUE` for the interested parties to consume later on.

Expand Down Expand Up @@ -109,7 +109,7 @@ To create a new audit, you'll need to create an audit handler function. This fun

```js
export async function auditRunner(url, context) {

// your audit logic goes here...

return {
Expand All @@ -130,7 +130,7 @@ All audits share common components, such as persisting audit results to a databa

```js
export async function auditRunner(url, context) {

// your audit logic goes here...

return {
Expand All @@ -141,7 +141,7 @@ export async function auditRunner(url, context) {

export async function differentUrlResolver(site) {
// logic to override to default behavior of the audit step

return 'url';
}

Expand All @@ -158,7 +158,7 @@ Using a noop messageSender, audit results might not be sent to the audit results

```js
export async function auditRunner(url, context) {

// your audit logic goes here...

return {
Expand Down Expand Up @@ -195,7 +195,7 @@ Here's the full example:

```js
export async function auditRunner(url, context) {

// your audit logic goes here...

return {
Expand All @@ -218,81 +218,67 @@ export default new AuditBuilder()
```

### How to add Opportunities and Suggestions
```js
import { syncSuggestions } from '../utils/data-access.js';

export async function auditRunner(url, context) {
In the handler, the `opportunityAndSuggestions` function is responsible for converting audit data into an opportunity and synchronizing suggestions.

// your audit logic goes here...
This function utilizes the `convertToOpportunity` function to create or update an opportunity based on the audit data and type.

return {
auditResult: results,
fullAuditRef: baseURL,
};
}
The `buildKey` function is used to generate a unique key for each suggestion based on specific properties of the audit data.

async function convertToOpportunity(auditUrl, auditData, context) {
const { dataAccess, log } = context;

const opportunities = await dataAccess.Opportunity.allBySiteIdAndStatus(auditData.siteId, 'NEW');
let opportunity = opportunities.find((oppty) => oppty.getType() === 'audit-type');

if (!opportunity) {
const opportunityData = {
siteId: auditData.siteId,
auditId: auditData.id,
runbook: 'link-to-runbook',
type: 'audit-type',
origin: 'AUTOMATION',
title: 'Opportunity Title',
description: 'Opportunity Description',
guidance: {
steps: [
'Step 1',
'Step 2',
],
},
tags: ['tag1', 'tag2'],
};
try {
opportunity = await dataAccess.Opportunity.create(opportunityData);
} catch (e) {
log.error(`Failed to create new opportunity for siteId ${auditData.siteId} and auditId ${auditData.id}: ${e.message}`);
throw e;
}
} else {
opportunity.setAuditId(auditData.id);
await opportunity.save();
}

// this logic changes based on the audit type
const buildKey = (auditData) => `${auditData.property}|${auditData.anotherProperty}`;

await syncSuggestions({
opportunity,
newData: auditData,
buildKey,
mapNewSuggestion: (issue) => ({
opportunityId: opportunity.getId(),
type: 'SUGGESTION_TYPE',
rank: issue.rankMetric,
// data changes based on the audit type
data: {
property: issue.property,
anotherProperty: issue.anotherProperty,
},
}),
log,
});
}
It then uses the `syncSuggestions` function to map new suggestions to the opportunity and synchronize them.

```js
import { syncSuggestions } from '../utils/data-access.js';
import { convertToOpportunity } from '../common/opportunity.js';
import { createOpportunityData } from './opportunity-data-mapper.js';

export async function opportunityAndSuggestions(auditUrl, auditData, context) {
const opportunity = await convertToOpportunity(
auditUrl,
auditData,
context,
createOpportunityData,
auditType,
);

const { log } = context;

// buildKey and SyncSuggestions logic based on the auditType goes here...
)};
```
```js
export default new AuditBuilder()
.withRunner(auditRunner)
.withPostProcessors([ convertToOpportunity ])
.withPostProcessors([opportunityAndSuggestions])
.build();
```


The logic for converting to an opportunity is in `common/opportunity.js`. The function `convertToOpportunity` is used to create a new opportunity or update an existing one based on the audit type. The function takes the audit URL, audit data, context, createOpportunityData, auditType, and props as arguments. It first fetches the opportunities for the site. If the opportunity is not found, it creates a new one. If the opportunity is found, it updates the existing one with the new data. The function returns the opportunity entity.


How to map the opportunity data in the handler's `opportunity-data-mapper.js` file:

```js
export function createOpportunityData(parameters) {
return {
runbook: 'runbook',
origin: 'origin',
title: 'title',
description: 'description',
guidance: {
steps: [
'step1',
'step2',
],
},
tags: ['tag1'],
data: {data},
};
}
```


### How to add auto-suggest to an audit
A new auto-suggest feature can be added as a post processor step to the existing audit.

Expand Down