Skip to content

Commit 936fe87

Browse files
authored
feat(costs): add costs audit that returns the ahrefs API units consumed and limit (#365)
Add costs audit that returns the Ahrefs API units consumed and limit. Ahrefs API call to get limits and usage is free. Audit will be scheduled weekly. Since this is a global audit, it will be by default disabled in the global configuration. It will only be enabled on dev, for ahrefs.com site. The follow-up PR #369 will add a post-processor notifying only internally about the audit result.
1 parent 80c3c16 commit 936fe87

File tree

3 files changed

+155
-0
lines changed

3 files changed

+155
-0
lines changed

src/costs/handler.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2024 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
import AhrefsAPIClient from '@adobe/spacecat-shared-ahrefs-client';
14+
import { AuditBuilder } from '../common/audit-builder.js';
15+
16+
export async function runner(auditUrl, context) {
17+
const { log } = context;
18+
const ahrefsAPIClient = AhrefsAPIClient.createFrom(context);
19+
20+
let ahrefsCostsAuditResult;
21+
try {
22+
const {
23+
result,
24+
fullAuditRef,
25+
} = await ahrefsAPIClient.getLimitsAndUsage();
26+
27+
log.info(`Retrieved Ahrefs limits and usage: ${JSON.stringify(result)}`);
28+
ahrefsCostsAuditResult = {
29+
usedApiUnits: result?.limits_and_usage?.units_usage_api_key,
30+
limitApiUnits: result?.limits_and_usage?.units_limit_api_key,
31+
fullAuditRef,
32+
};
33+
} catch (e) {
34+
log.error(`Ahrefs costs type audit failed with error: ${e.message}`, e);
35+
ahrefsCostsAuditResult = {
36+
error: `Ahrefs costs type audit failed with error: ${e.message}`,
37+
};
38+
}
39+
40+
return {
41+
auditResult: {
42+
ahrefs: ahrefsCostsAuditResult,
43+
},
44+
fullAuditRef: ahrefsCostsAuditResult?.fullAuditRef,
45+
};
46+
}
47+
48+
export default new AuditBuilder()
49+
.withRunner(runner)
50+
.withMessageSender(() => {})
51+
.build();

src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import experimentation from './experimentation/handler.js';
2929
import conversion from './conversion/handler.js';
3030
import essExperimentationDaily from './experimentation-ess/daily.js';
3131
import essExperimentationAll from './experimentation-ess/all.js';
32+
import costs from './costs/handler.js';
3233

3334
const HANDLERS = {
3435
apex,
@@ -43,6 +44,7 @@ const HANDLERS = {
4344
conversion,
4445
'experimentation-ess-daily': essExperimentationDaily,
4546
'experimentation-ess-all': essExperimentationAll,
47+
costs,
4648
};
4749

4850
function getElapsedSeconds(startTime) {

test/audits/costs.test.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright 2024 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
/* eslint-env mocha */
14+
import { expect, use } from 'chai';
15+
import sinon from 'sinon';
16+
import sinonChai from 'sinon-chai';
17+
import chaiAsPromised from 'chai-as-promised';
18+
import nock from 'nock';
19+
import { MockContextBuilder } from '../shared.js';
20+
import { runner } from '../../src/costs/handler.js';
21+
22+
use(sinonChai);
23+
use(chaiAsPromised);
24+
25+
const message = {
26+
type: 'costs',
27+
url: 'site-id',
28+
};
29+
const sandbox = sinon.createSandbox();
30+
31+
describe('Costs audit', () => {
32+
let context;
33+
34+
beforeEach('setup', () => {
35+
context = new MockContextBuilder()
36+
.withSandbox(sandbox)
37+
.withOverrides({
38+
env: {
39+
AHREFS_API_BASE_URL: 'https://ahrefs-example.com',
40+
AHREFS_API_KEY: 'ahrefs-token',
41+
},
42+
})
43+
.build(message);
44+
});
45+
46+
afterEach(() => {
47+
nock.cleanAll();
48+
sandbox.restore();
49+
});
50+
51+
it('costs audit returns ahrefs costs succesfully', async () => {
52+
const limitsUsageResponse = {
53+
limits_and_usage: {
54+
subscription: 'Enterprise, billed yearly',
55+
usage_reset_date: '2024-08-28T00:00:00Z',
56+
units_limit_workspace: 12000000,
57+
units_usage_workspace: 6618294,
58+
units_limit_api_key: 1000000,
59+
units_usage_api_key: 198771,
60+
api_key_expiration_date: '2025-01-04T17:44:07Z',
61+
},
62+
};
63+
64+
nock('https://ahrefs-example.com')
65+
.get('/subscription-info/limits-and-usage')
66+
.reply(200, limitsUsageResponse);
67+
68+
const result = await runner('https://spacecat.com', context);
69+
70+
const expectedAuditResult = {
71+
ahrefs: {
72+
usedApiUnits: 198771,
73+
limitApiUnits: 1000000,
74+
fullAuditRef: 'https://ahrefs-example.com/subscription-info/limits-and-usage',
75+
},
76+
};
77+
78+
expect(result).to.eql({
79+
auditResult: expectedAuditResult,
80+
fullAuditRef: 'https://ahrefs-example.com/subscription-info/limits-and-usage',
81+
});
82+
});
83+
84+
it('costs audit returns error for ahrefs costs when call to ahrefs throws', async () => {
85+
nock('https://ahrefs-example.com')
86+
.get('/subscription-info/limits-and-usage')
87+
.reply(500);
88+
89+
const result = await runner('https://spacecat.com', context);
90+
91+
const expectedAuditResult = {
92+
ahrefs: {
93+
error: 'Ahrefs costs type audit failed with error: Ahrefs API request failed with status: 500',
94+
},
95+
};
96+
97+
expect(result).to.eql({
98+
auditResult: expectedAuditResult,
99+
fullAuditRef: undefined,
100+
});
101+
});
102+
});

0 commit comments

Comments
 (0)