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

feat: high bounce rate and low ctr opportunities #384

Merged
merged 6 commits into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
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
18 changes: 2 additions & 16 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion src/common/audit.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
* governing permissions and limitations under the License.
*/

import { composeAuditURL } from '@adobe/spacecat-shared-utils';
import { composeAuditURL, hasText } from '@adobe/spacecat-shared-utils';
import { ok } from '@adobe/spacecat-shared-http-utils';
import URI from 'urijs';
import { retrieveSiteBySiteId } from '../utils/data-access.js';

export async function defaultMessageSender(resultMessage, context) {
Expand Down Expand Up @@ -52,6 +53,12 @@ export async function defaultUrlResolver(site) {
return composeAuditURL(site.getBaseURL());
}

export function wwwUrlResolver(site) {
const baseURL = site.getBaseURL();
const uri = new URI(baseURL);
return hasText(uri.subdomain()) ? baseURL.replace(/https?:\/\//, '') : baseURL.replace(/https?:\/\//, 'www.');
}

export async function noopUrlResolver(site) {
return site.getBaseURL();
}
Expand Down
15 changes: 4 additions & 11 deletions src/cwv/handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,18 @@
*/

import RUMAPIClient from '@adobe/spacecat-shared-rum-api-client';
import URI from 'urijs';
import { hasText } from '@adobe/spacecat-shared-utils';
import { getRUMDomainkey } from '../support/utils.js';
import { AuditBuilder } from '../common/audit-builder.js';
import { wwwUrlResolver } from '../common/audit.js';

const DAILY_THRESHOLD = 1000;
const INTERVAL = 7; // days

export function getAuditUrl(baseURL) {
const uri = new URI(baseURL);
return hasText(uri.subdomain()) ? baseURL.replace(/https?:\/\//, '') : baseURL.replace(/https?:\/\//, 'www.');
}

export async function CWVRunner(auditUrl, context, site) {
const rumAPIClient = RUMAPIClient.createFrom(context);
const domainkey = await getRUMDomainkey(site.getBaseURL(), context);
const finalUrl = getAuditUrl(auditUrl);
const options = {
domain: finalUrl,
domain: auditUrl,
domainkey,
interval: INTERVAL,
granularity: 'hourly',
Expand All @@ -44,11 +37,11 @@ export async function CWVRunner(auditUrl, context, site) {

return {
auditResult,
fullAuditRef: finalUrl,
fullAuditRef: auditUrl,
};
}

export default new AuditBuilder()
.withUrlResolver((site) => site.getBaseURL())
.withUrlResolver(wwwUrlResolver)
.withRunner(CWVRunner)
.build();
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@
* governing permissions and limitations under the License.
*/

/* c8 ignore start */
import RUMAPIClient from '@adobe/spacecat-shared-rum-api-client';
import { AuditBuilder } from '../common/audit-builder.js';
import { getRUMDomainkey } from '../support/utils.js';
import { wwwUrlResolver } from '../common/audit.js';

const DAYS = 30;

let log = console;
const OPPTY_QUERIES = [
'rageclick',
'high-inorganic-high-bounce-rate',
'high-organic-low-ctr',
];

/**
* Audit handler container for all the opportunities
Expand All @@ -27,10 +30,8 @@ let log = console;
* @returns
*/

export async function opportunitiesHandler(auditUrl, context, site) {
log = context.log;
log.info(`Received Opportunities audit request for ${auditUrl}`);
const startTime = process.hrtime();
export async function handler(auditUrl, context, site) {
const { log } = context;

const rumAPIClient = RUMAPIClient.createFrom(context);
const domainkey = await getRUMDomainkey(site.getBaseURL(), context);
Expand All @@ -40,30 +41,21 @@ export async function opportunitiesHandler(auditUrl, context, site) {
interval: DAYS,
granularity: 'hourly',
};
const experimentationHandlers = ['rageclick'];
const queryResults = await rumAPIClient.queryMulti(experimentationHandlers, options);
const auditData = {
experimentationOpportunities: [],
};
for (const queryResult of Object.keys(queryResults)) {
if (experimentationHandlers.includes(queryResult)) {
auditData.experimentationOpportunities.push(...queryResults[queryResult]);
}
}

const endTime = process.hrtime(startTime);
const elapsedSeconds = endTime[0] + endTime[1] / 1e9;
const formattedElapsed = elapsedSeconds.toFixed(2);
const queryResults = await rumAPIClient.queryMulti(OPPTY_QUERIES, options);
const experimentationOpportunities = Object.values(queryResults).flatMap((oppty) => oppty);

log.info(`Opportunities Audit is completed in ${formattedElapsed} seconds for ${auditUrl}`);
log.info(`Found ${experimentationOpportunities.length} many experimentation opportunites for ${auditUrl}`);

return {
auditResult: auditData,
auditResult: {
experimentationOpportunities,
},
fullAuditRef: auditUrl,
};
}

export default new AuditBuilder()
.withRunner(opportunitiesHandler)
.withRunner(handler)
.withUrlResolver(wwwUrlResolver)
.build();
/* c8 ignore stop */
10 changes: 5 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import experimentation from './experimentation/handler.js';
import conversion from './conversion/handler.js';
import essExperimentationDaily from './experimentation-ess/daily.js';
import essExperimentationAll from './experimentation-ess/all.js';
import opportunities from './opportunities/opportunities.js';
import experimentationOpportunities from './experimentation-opportunities/experimentation-opportunities.js';
import costs from './costs/handler.js';
import structuredData from './structured-data/handler.js';

Expand All @@ -46,7 +46,7 @@ const HANDLERS = {
conversion,
'experimentation-ess-daily': essExperimentationDaily,
'experimentation-ess-all': essExperimentationAll,
opportunities,
'experimentation-opportunities': experimentationOpportunities,
costs,
'structured-data': structuredData,
dummy: (message) => ok(message),
Expand All @@ -68,7 +68,7 @@ async function run(message, context) {
const { log } = context;
const { type, url } = message;

log.info(`Audit req received for url: ${url}`);
log.info(`Received ${type} audit request for: ${url}`);

const handler = HANDLERS[type];
if (!handler) {
Expand All @@ -82,11 +82,11 @@ async function run(message, context) {
try {
const result = await (typeof handler.run === 'function' ? handler.run(message, context) : handler(message, context));

log.info(`Audit for ${type} completed in ${getElapsedSeconds(startTime)} seconds`);
log.info(`${type} audit for ${url} completed in ${getElapsedSeconds(startTime)} seconds`);

return result;
} catch (e) {
log.error(`Audit failed after ${getElapsedSeconds(startTime)} seconds`, e);
log.error(`${type} audit for ${url} failed after ${getElapsedSeconds(startTime)} seconds`, e);
return internalServerError();
}
}
Expand Down
13 changes: 2 additions & 11 deletions test/audits/cwv.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import nock from 'nock';
import { createSite } from '@adobe/spacecat-shared-data-access/src/models/site.js';
import { CWVRunner, getAuditUrl } from '../../src/cwv/handler.js';
import { CWVRunner } from '../../src/cwv/handler.js';
import { rumData } from '../fixtures/rum-data.js';

use(sinonChai);
Expand Down Expand Up @@ -59,7 +59,7 @@ describe('Index Tests', () => {
});

it('cwv audit runs rum api client cwv query', async () => {
const result = await CWVRunner('https://spacecat.com', context, site);
const result = await CWVRunner('www.spacecat.com', context, site);
expect(result).to.deep.equal({
auditResult: {
cwv: rumData.filter((data) => data.pageviews >= 7000),
Expand All @@ -70,13 +70,4 @@ describe('Index Tests', () => {
fullAuditRef: auditUrl,
});
});

it('audit url calculated correctly', async () => {
expect(getAuditUrl('http://spacecat.com')).to.equal('www.spacecat.com');
expect(getAuditUrl('https://spacecat.com')).to.equal('www.spacecat.com');
expect(getAuditUrl('http://www.spacecat.com')).to.equal('www.spacecat.com');
expect(getAuditUrl('https://www.spacecat.com')).to.equal('www.spacecat.com');
expect(getAuditUrl('http://blog.spacecat.com')).to.equal('blog.spacecat.com');
expect(getAuditUrl('https://blog.spacecat.com')).to.equal('blog.spacecat.com');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { expect, use } from 'chai';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import nock from 'nock';
import { opportunitiesHandler } from '../../src/opportunities/opportunities.js';
import { handler } from '../../src/experimentation-opportunities/experimentation-opportunities.js';
import { MockContextBuilder } from '../shared.js';
import opportunitiesData from '../fixtures/opportunitiesdata.json' assert { type: 'json' };

Expand Down Expand Up @@ -91,19 +91,22 @@ describe('Opportunities Tests', () => {
const site = {
getBaseURL: () => 'https://abc.com',
};
const auditData = await opportunitiesHandler(url, context, site);
const auditData = await handler(url, context, site);

expect(context.rumApiClient.queryMulti).calledWith(
['rageclick'],
{
domain: 'https://abc.com',
domainkey: 'abc_dummy_key',
interval: 30,
granularity: 'hourly',
},
);
const expected = Object.values(opportunitiesData).flatMap((data) => data);

expect(context.rumApiClient.queryMulti).calledWith([
'rageclick',
'high-inorganic-high-bounce-rate',
'high-organic-low-ctr',
], {
domain: 'https://abc.com',
domainkey: 'abc_dummy_key',
interval: 30,
granularity: 'hourly',
});
expect(
auditData.auditResult.experimentationOpportunities,
).to.deep.equal(opportunitiesData.rageclick);
).to.deep.equal(expected);
});
});
16 changes: 14 additions & 2 deletions test/common/audit.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@ import { createOrganization } from '@adobe/spacecat-shared-data-access/src/model
import { createConfiguration } from '@adobe/spacecat-shared-data-access/src/models/configuration.js';
import { composeAuditURL, prependSchema } from '@adobe/spacecat-shared-utils';
import {
defaultMessageSender, defaultOrgProvider,
defaultMessageSender,
defaultOrgProvider,
defaultPersister,
defaultSiteProvider,
defaultUrlResolver, noopUrlResolver,
defaultUrlResolver,
noopUrlResolver,
wwwUrlResolver,
} from '../../src/common/audit.js';
import { AuditBuilder } from '../../src/common/audit-builder.js';
import { MockContextBuilder } from '../shared.js';
Expand Down Expand Up @@ -326,4 +329,13 @@ describe('Audit tests', () => {
expect(context.sqs.sendMessage).to.have.been.calledOnce;
expect(context.sqs.sendMessage).to.have.been.calledWith(queueUrl, expectedMessage);
});

it('wwwUrlResolver calculates audit urls correctly', async () => {
expect(wwwUrlResolver(createSite({ baseURL: 'http://spacecat.com' }))).to.equal('www.spacecat.com');
expect(wwwUrlResolver(createSite({ baseURL: 'https://spacecat.com' }))).to.equal('www.spacecat.com');
expect(wwwUrlResolver(createSite({ baseURL: 'http://www.spacecat.com' }))).to.equal('www.spacecat.com');
expect(wwwUrlResolver(createSite({ baseURL: 'https://www.spacecat.com' }))).to.equal('www.spacecat.com');
expect(wwwUrlResolver(createSite({ baseURL: 'http://blog.spacecat.com' }))).to.equal('blog.spacecat.com');
expect(wwwUrlResolver(createSite({ baseURL: 'https://blog.spacecat.com' }))).to.equal('blog.spacecat.com');
});
});
Loading
Loading