Skip to content

Commit a8ebab8

Browse files
committed
feat(kpis): track total fingerprints used per model
1 parent 6d1b15a commit a8ebab8

File tree

3 files changed

+106
-0
lines changed

3 files changed

+106
-0
lines changed

kpis/totalFingerprintsUsed.spec.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import {
2+
DynamoDBClient,
3+
type ScanInput,
4+
type ScanOutput,
5+
} from '@aws-sdk/client-dynamodb'
6+
import assert from 'assert'
7+
import { describe, it, mock } from 'node:test'
8+
import { totalFingerprintsUsed } from './totalFingerprintsUsed.ts'
9+
10+
await describe('totalFingerprintsUsed()', async () => {
11+
await it('should return the total number of fingerprints used per model', async () => {
12+
const send = mock.fn<(args: { input: ScanInput }) => Promise<ScanOutput>>(
13+
async () => ({
14+
Items: [
15+
{
16+
model: { S: 'model1' },
17+
lastSeen: { S: '2023-01-01T12:34:56.000Z' },
18+
},
19+
{
20+
model: { S: 'model1' },
21+
lastSeen: { S: '2023-01-02T12:34:56.000Z' },
22+
},
23+
{
24+
model: { S: 'model2' },
25+
lastSeen: { S: '2023-01-01T12:34:56.000Z' },
26+
},
27+
],
28+
}),
29+
)
30+
const db = new DynamoDBClient({})
31+
db.send = send
32+
const TableName = 'test-table'
33+
34+
const result = await totalFingerprintsUsed(db, TableName)()
35+
assert.deepStrictEqual(
36+
result,
37+
new Map([
38+
['model1', 2],
39+
['model2', 1],
40+
]),
41+
)
42+
43+
assert.deepStrictEqual(send.mock.calls.length, 1)
44+
assert.deepEqual(send.mock.calls[0]!.arguments[0].input, {
45+
ExclusiveStartKey: undefined,
46+
Limit: undefined,
47+
TableName,
48+
ExpressionAttributeNames: {
49+
'#model': 'model',
50+
'#lastSeen': 'lastSeen',
51+
},
52+
ProjectionExpression: '#model',
53+
FilterExpression: 'attribute_exists(#lastSeen)',
54+
})
55+
})
56+
57+
await it('should return an empty map if no items are found', async () => {
58+
const send = mock.fn<(args: { input: ScanInput }) => Promise<ScanOutput>>(
59+
async () => ({}),
60+
)
61+
const db = new DynamoDBClient({})
62+
db.send = send
63+
const TableName = 'test-table'
64+
65+
const result = await totalFingerprintsUsed(db, TableName)()
66+
assert.deepStrictEqual(result, new Map())
67+
assert.equal(result.size, 0)
68+
})
69+
})

kpis/totalFingerprintsUsed.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { type DynamoDBClient, paginateScan } from '@aws-sdk/client-dynamodb'
2+
import { unmarshall } from '@aws-sdk/util-dynamodb'
3+
4+
/**
5+
* Returns the total number of fingerprints used per model.
6+
*/
7+
export const totalFingerprintsUsed =
8+
(db: DynamoDBClient, TableName: string) =>
9+
async (): Promise<Map<string, number>> => {
10+
const results = paginateScan(
11+
{ client: db },
12+
{
13+
TableName,
14+
ExpressionAttributeNames: {
15+
'#model': 'model',
16+
'#lastSeen': 'lastSeen',
17+
},
18+
ProjectionExpression: '#model',
19+
FilterExpression: 'attribute_exists(#lastSeen)',
20+
},
21+
)
22+
const countPerModel = new Map<string, number>()
23+
for await (const { Items } of results) {
24+
for (const item of (Items ?? []).map((i) => unmarshall(i))) {
25+
countPerModel.set(item.model, (countPerModel.get(item.model) ?? 0) + 1)
26+
}
27+
}
28+
return countPerModel
29+
}

lambda/kpis.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
import middy from '@middy/core'
1515
import { dailyActiveDevices } from '../kpis/dailyActiveDevices.ts'
1616
import { dailyActiveFingerprints } from '../kpis/dailyActiveFingerprints.ts'
17+
import { totalFingerprintsUsed } from '../kpis/totalFingerprintsUsed.ts'
1718

1819
const { lastSeenTableName, devicesTableName, stackName } = fromEnv({
1920
lastSeenTableName: 'LAST_SEEN_TABLE_NAME',
@@ -25,6 +26,7 @@ const ssm = new SSMClient({})
2526
const db = new DynamoDBClient({})
2627
const getDailyActiveDevices = dailyActiveDevices(db, lastSeenTableName)
2728
const getDailyActiveFingerprints = dailyActiveFingerprints(db, devicesTableName)
29+
const getTotalFingerprintsUsed = totalFingerprintsUsed(db, devicesTableName)
2830

2931
const { track, metrics } = metricsForComponent('KPIs')
3032

@@ -51,6 +53,12 @@ const h = async () => {
5153
)
5254
},
5355
),
56+
getTotalFingerprintsUsed().then((totalFingerprintsUsedPerModel) => {
57+
console.log({ totalFingerprintsUsedPerModel })
58+
for (const [model, count] of totalFingerprintsUsedPerModel) {
59+
track(`totalFingerprintsUsed:${model}`, MetricUnit.Count, count)
60+
}
61+
}),
5462
// Current month's nRF Cloud costs
5563
...(await getAllAccounts({ ssm, stackName })).map(async (account) => {
5664
const settingsPromise =

0 commit comments

Comments
 (0)