Skip to content

Commit 8456f1e

Browse files
Copilotkarpikpl
andcommitted
Fix: Update cache key to include query parameters and clear cache on errors
Co-authored-by: karpikpl <[email protected]>
1 parent 68605d6 commit 8456f1e

File tree

3 files changed

+81
-6
lines changed

3 files changed

+81
-6
lines changed

app/components/MainComponent.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ export default defineNuxtComponent({
174174
const config = useRuntimeConfig();
175175
176176
this.isLoading = true;
177+
// Clear previous API errors when making a new request
178+
this.apiError = undefined;
177179
178180
try {
179181
const options = Options.fromRoute(this.route, this.dateRange.since, this.dateRange.until);

shared/utils/metrics-util.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,18 @@ export async function getMetricsData(event: H3Event<EventHandlerRequest>): Promi
5252
return usageData;
5353
}
5454

55-
if (cache.has(event.path)) {
56-
const cachedData = cache.get(event.path);
55+
// Create cache key that includes query parameters to differentiate between different date ranges
56+
const queryString = new URLSearchParams(query as Record<string, string>).toString();
57+
const cacheKey = queryString ? `${event.path}?${queryString}` : event.path;
58+
59+
if (cache.has(cacheKey)) {
60+
const cachedData = cache.get(cacheKey);
5761
if (cachedData && cachedData.valid_until > Date.now() / 1000) {
58-
logger.info(`Returning cached data for ${event.path}`);
62+
logger.info(`Returning cached data for ${cacheKey}`);
5963
return cachedData.data;
6064
} else {
61-
logger.info(`Cached data for ${event.path} is expired, fetching new data`);
62-
cache.delete(event.path);
65+
logger.info(`Cached data for ${cacheKey} is expired, fetching new data`);
66+
cache.delete(cacheKey);
6367
}
6468
}
6569

@@ -81,10 +85,12 @@ export async function getMetricsData(event: H3Event<EventHandlerRequest>): Promi
8185
const filteredUsageData = filterHolidaysFromMetrics(usageData, options.excludeHolidays || false, options.locale);
8286
// metrics is the old API format
8387
const validUntil = Math.floor(Date.now() / 1000) + 5 * 60; // Cache for 5 minutes
84-
cache.set(event.path, { data: filteredUsageData, valid_until: validUntil });
88+
cache.set(cacheKey, { data: filteredUsageData, valid_until: validUntil });
8589
return filteredUsageData;
8690
} catch (error: unknown) {
8791
logger.error('Error fetching metrics data:', error);
92+
// Clear any cached data for this request to prevent stale data on retry
93+
cache.delete(cacheKey);
8894
const errorMessage = error instanceof Error ? error.message : String(error);
8995
const statusCode = (error && typeof error === 'object' && 'statusCode' in error)
9096
? (error as { statusCode: number }).statusCode

tests/metrics-cache.nuxt.spec.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// @vitest-environment nuxt
2+
import { describe, it, expect, beforeEach } from 'vitest'
3+
4+
describe('Metrics Cache Key Generation', () => {
5+
it('should create unique cache keys for different query parameters', () => {
6+
// Test the cache key logic that was implemented
7+
const createCacheKey = (path: string, query: Record<string, string>) => {
8+
const queryString = new URLSearchParams(query).toString()
9+
return queryString ? `${path}?${queryString}` : path
10+
}
11+
12+
const path = '/api/metrics'
13+
14+
// Different date ranges should create different cache keys
15+
const query1 = { since: '2024-01-01', until: '2024-01-31', scope: 'organization', githubOrg: 'test-org' }
16+
const query2 = { since: '2024-02-01', until: '2024-02-28', scope: 'organization', githubOrg: 'test-org' }
17+
const query3 = { since: '2024-01-01', until: '2024-01-31', scope: 'organization', githubOrg: 'test-org' }
18+
19+
const key1 = createCacheKey(path, query1)
20+
const key2 = createCacheKey(path, query2)
21+
const key3 = createCacheKey(path, query3)
22+
23+
// Different date ranges should have different keys
24+
expect(key1).not.toBe(key2)
25+
26+
// Same parameters should have same key
27+
expect(key1).toBe(key3)
28+
29+
// Keys should include query parameters
30+
expect(key1).toContain('since=2024-01-01')
31+
expect(key1).toContain('until=2024-01-31')
32+
expect(key2).toContain('since=2024-02-01')
33+
expect(key2).toContain('until=2024-02-28')
34+
})
35+
36+
it('should handle empty query parameters', () => {
37+
const createCacheKey = (path: string, query: Record<string, string>) => {
38+
const queryString = new URLSearchParams(query).toString()
39+
return queryString ? `${path}?${queryString}` : path
40+
}
41+
42+
const path = '/api/metrics'
43+
const emptyQuery = {}
44+
45+
const key = createCacheKey(path, emptyQuery)
46+
expect(key).toBe(path)
47+
})
48+
49+
it('should handle undefined query values', () => {
50+
const createCacheKey = (path: string, query: Record<string, any>) => {
51+
// Filter out undefined values before creating query string
52+
const filteredQuery = Object.fromEntries(
53+
Object.entries(query).filter(([_, value]) => value !== undefined)
54+
)
55+
const queryString = new URLSearchParams(filteredQuery).toString()
56+
return queryString ? `${path}?${queryString}` : path
57+
}
58+
59+
const path = '/api/metrics'
60+
const queryWithUndefined = { since: '2024-01-01', until: undefined, scope: 'organization' }
61+
62+
const key = createCacheKey(path, queryWithUndefined)
63+
expect(key).toContain('since=2024-01-01')
64+
expect(key).toContain('scope=organization')
65+
expect(key).not.toContain('until=')
66+
})
67+
})

0 commit comments

Comments
 (0)