/
endpointBuilder.ts
123 lines (106 loc) · 4.14 KB
/
endpointBuilder.ts
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import type { Payload } from '../../transport'
import { timeStampNow } from '../../tools/utils/timeUtils'
import { normalizeUrl } from '../../tools/utils/urlPolyfill'
import { ExperimentalFeature, isExperimentalFeatureEnabled } from '../../tools/experimentalFeatures'
import { generateUUID } from '../../tools/utils/stringUtils'
import type { InitConfiguration } from './configuration'
import { INTAKE_SITE_US1, INTAKE_SITE_FED_STAGING, PCI_INTAKE_HOST_US1 } from './intakeSites'
// replaced at build time
declare const __BUILD_ENV__SDK_VERSION__: string
export type TrackType = 'logs' | 'rum' | 'replay'
export type ApiType =
| 'xhr'
| 'fetch'
| 'beacon'
// 'manual' reflects that the request have been sent manually, outside of the SDK (ex: via curl or
// a Node.js script).
| 'manual'
export type EndpointBuilder = ReturnType<typeof createEndpointBuilder>
export function createEndpointBuilder(
initConfiguration: InitConfiguration,
trackType: TrackType,
configurationTags: string[]
) {
const buildUrlWithParameters = createEndpointUrlWithParametersBuilder(initConfiguration, trackType)
return {
build(api: ApiType, payload: Payload) {
const parameters = buildEndpointParameters(initConfiguration, trackType, configurationTags, api, payload)
return buildUrlWithParameters(parameters)
},
urlPrefix: buildUrlWithParameters(''),
trackType,
}
}
/**
* Create a function used to build a full endpoint url from provided parameters. The goal of this
* function is to pre-compute some parts of the URL to avoid re-computing everything on every
* request, as only parameters are changing.
*/
function createEndpointUrlWithParametersBuilder(
initConfiguration: InitConfiguration,
trackType: TrackType
): (parameters: string) => string {
const path = `/api/v2/${trackType}`
const proxy = initConfiguration.proxy
if (typeof proxy === 'string') {
const normalizedProxyUrl = normalizeUrl(proxy)
return (parameters) => `${normalizedProxyUrl}?ddforward=${encodeURIComponent(`${path}?${parameters}`)}`
}
if (typeof proxy === 'function') {
return (parameters) => proxy({ path, parameters })
}
const host = buildEndpointHost(trackType, initConfiguration)
return (parameters) => `https://${host}${path}?${parameters}`
}
function buildEndpointHost(trackType: TrackType, initConfiguration: InitConfiguration & { usePciIntake?: boolean }) {
const { site = INTAKE_SITE_US1, internalAnalyticsSubdomain } = initConfiguration
if (trackType === 'logs' && initConfiguration.usePciIntake && site === INTAKE_SITE_US1) {
return PCI_INTAKE_HOST_US1
}
if (internalAnalyticsSubdomain && site === INTAKE_SITE_US1) {
return `${internalAnalyticsSubdomain}.${INTAKE_SITE_US1}`
}
if (site === INTAKE_SITE_FED_STAGING) {
return `http-intake.logs.${site}`
}
const domainParts = site.split('.')
const extension = domainParts.pop()
return `browser-intake-${domainParts.join('-')}.${extension!}`
}
/**
* Build parameters to be used for an intake request. Parameters should be re-built for each
* request, as they change randomly.
*/
function buildEndpointParameters(
{ clientToken, internalAnalyticsSubdomain }: InitConfiguration,
trackType: TrackType,
configurationTags: string[],
api: ApiType,
{ retry, flushReason, encoding }: Payload
) {
const tags = [`sdk_version:${__BUILD_ENV__SDK_VERSION__}`, `api:${api}`].concat(configurationTags)
if (flushReason && isExperimentalFeatureEnabled(ExperimentalFeature.COLLECT_FLUSH_REASON)) {
tags.push(`flush_reason:${flushReason}`)
}
if (retry) {
tags.push(`retry_count:${retry.count}`, `retry_after:${retry.lastFailureStatus}`)
}
const parameters = [
'ddsource=browser',
`ddtags=${encodeURIComponent(tags.join(','))}`,
`dd-api-key=${clientToken}`,
`dd-evp-origin-version=${encodeURIComponent(__BUILD_ENV__SDK_VERSION__)}`,
'dd-evp-origin=browser',
`dd-request-id=${generateUUID()}`,
]
if (encoding) {
parameters.push(`dd-evp-encoding=${encoding}`)
}
if (trackType === 'rum') {
parameters.push(`batch_time=${timeStampNow()}`)
}
if (internalAnalyticsSubdomain) {
parameters.reverse()
}
return parameters.join('&')
}