-
Notifications
You must be signed in to change notification settings - Fork 190
Expand file tree
/
Copy pathremoteConfigurationCache.ts
More file actions
96 lines (77 loc) · 2.29 KB
/
Copy pathremoteConfigurationCache.ts
File metadata and controls
96 lines (77 loc) · 2.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import { timeStampNow } from '@datadog/js-core/time'
import type { TimeStamp } from '@datadog/js-core/time'
import { tryJsonParse } from '../../tools/utils/objectUtils'
export const CACHE_VERSION = 2
export const CACHE_KEY_PREFIX = 'dd_rc_'
interface CachedRemoteConfiguration<T> {
version: number
config: T
fetchedAt: TimeStamp
}
export type CacheReadStatus = 'hit' | 'miss' | 'error'
export type CacheReadResult<T> =
| {
status: Exclude<CacheReadStatus, 'hit'>
}
| { status: Extract<CacheReadStatus, 'hit'>; config: T }
export const CACHE_STATUS_TO_METRIC_MAP: Record<CacheReadStatus, 'success' | 'missing' | 'failure'> = {
hit: 'success',
miss: 'missing',
error: 'failure',
}
export function buildCacheKey(remoteConfigurationId: string): string {
return `${CACHE_KEY_PREFIX}${remoteConfigurationId}`
}
function isValidCacheEntry(value: unknown): value is CachedRemoteConfiguration<unknown> {
if (typeof value !== 'object' || value === null) {
return false
}
const hasVersion = 'version' in value && value.version === CACHE_VERSION
const hasConfig = 'config' in value && typeof value.config === 'object' && value.config !== null
return hasVersion && hasConfig
}
export function createConfigurationCache<T>({ remoteConfigurationId }: { remoteConfigurationId: string }) {
const key = buildCacheKey(remoteConfigurationId)
return {
read(): CacheReadResult<T> {
let raw: string | null
try {
raw = localStorage.getItem(key)
} catch {
return { status: 'error' }
}
if (raw === null) {
return { status: 'miss' }
}
const parsed = tryJsonParse(raw)
if (parsed === undefined) {
this.remove()
return { status: 'error' }
}
if (!isValidCacheEntry(parsed)) {
this.remove()
return { status: 'error' }
}
return { status: 'hit', config: parsed.config as T }
},
remove() {
try {
localStorage.removeItem(key)
} catch {
// Ignore
}
},
write(config: T) {
const entry: CachedRemoteConfiguration<T> = {
version: CACHE_VERSION,
config,
fetchedAt: timeStampNow(),
}
try {
localStorage.setItem(key, JSON.stringify(entry))
} catch {
// Ignore
}
},
}
}