Skip to content

Commit

Permalink
✨ [RUMF-1236] Add support for OTel headers (#1832)
Browse files Browse the repository at this point in the history
* 🐛 Fix Cookies are not authorized random issue
* ✨ Open Telemetry header support for APM
* 👌 Rename configureTracingUrls to allowedTracingUrls + Updates following review
* 👌🎨 Reuse matchList in tracer
* 👌 Renamed variables to follow OTel conventions
  • Loading branch information
yannickadam authored Dec 15, 2022
1 parent e2b75c4 commit 83b2be6
Show file tree
Hide file tree
Showing 8 changed files with 575 additions and 68 deletions.
4 changes: 2 additions & 2 deletions packages/core/src/browser/cookie.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { display } from '../tools/display'
import { findCommaSeparatedValue, generateUUID, ONE_SECOND } from '../tools/utils'
import { findCommaSeparatedValue, generateUUID, ONE_MINUTE, ONE_SECOND } from '../tools/utils'

export const COOKIE_ACCESS_DELAY = ONE_SECOND

Expand Down Expand Up @@ -36,7 +36,7 @@ export function areCookiesAuthorized(options: CookieOptions): boolean {
// the test cookie lifetime
const testCookieName = `dd_cookie_test_${generateUUID()}`
const testCookieValue = 'test'
setCookie(testCookieName, testCookieValue, ONE_SECOND, options)
setCookie(testCookieName, testCookieValue, ONE_MINUTE, options)
const isCookieCorrectlySet = getCookie(testCookieName) === testCookieValue
deleteCookie(testCookieName, options)
return isCookieCorrectlySet
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/tools/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,11 @@ describe('matchList', () => {
expect(matchList(list, 'qux')).toBe(false)
})

it('should compare strings using startsWith when enabling the option', () => {
const list = ['http://my.domain.com']
expect(matchList(list, 'http://my.domain.com/action', true)).toBe(true)
})

it('should catch error from provided function', () => {
spyOn(display, 'error')
const list = [
Expand Down
29 changes: 19 additions & 10 deletions packages/core/src/tools/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -556,20 +556,29 @@ export function removeDuplicates<T>(array: T[]) {
}

export type MatchOption = string | RegExp | ((value: string) => boolean)
export function matchList(list: MatchOption[], value: string): boolean {
export function isMatchOption(item: unknown): item is MatchOption {
const itemType = getType(item)
return itemType === 'string' || itemType === 'function' || item instanceof RegExp
}
/**
* Returns true if value can be matched by at least one of the provided MatchOptions.
* When comparing strings, setting useStartsWith to true will compare the value with the start of
* the option, instead of requiring an exact match.
*/
export function matchList(list: MatchOption[], value: string, useStartsWith = false): boolean {
return list.some((item) => {
if (typeof item === 'function') {
try {
try {
if (typeof item === 'function') {
return item(value)
} catch (e) {
display.error(e)
return false
} else if (item instanceof RegExp) {
return item.test(value)
} else if (typeof item === 'string') {
return useStartsWith ? startsWith(value, item) : item === value
}
} catch (e) {
display.error(e)
}
if (item instanceof RegExp) {
return item.test(value)
}
return item === value
return false
})
}

Expand Down
137 changes: 124 additions & 13 deletions packages/rum-core/src/domain/configuration.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { DefaultPrivacyLevel, display } from '@datadog/browser-core'
import { validateAndBuildRumConfiguration } from './configuration'
import type { RumInitConfiguration } from './configuration'
import { serializeRumConfiguration, validateAndBuildRumConfiguration } from './configuration'

const DEFAULT_INIT_CONFIGURATION = { clientToken: 'xxx', applicationId: 'xxx' }

Expand Down Expand Up @@ -166,44 +167,122 @@ describe('validateAndBuildRumConfiguration', () => {
})

describe('allowedTracingOrigins', () => {
it('is set to provided value', () => {
expect(
validateAndBuildRumConfiguration({
...DEFAULT_INIT_CONFIGURATION,
allowedTracingOrigins: ['foo'],
service: 'bar',
})!.allowedTracingUrls
).toEqual([{ match: 'foo', propagatorTypes: ['datadog'] }])
})

it('accepts functions', () => {
const originMatchSpy = jasmine.createSpy<(origin: string) => boolean>()

const tracingUrlOptionMatch = validateAndBuildRumConfiguration({
...DEFAULT_INIT_CONFIGURATION,
allowedTracingOrigins: [originMatchSpy],
service: 'bar',
})!.allowedTracingUrls[0].match as (url: string) => boolean

expect(typeof tracingUrlOptionMatch).toBe('function')
// Replicating behavior from allowedTracingOrigins, new function will treat the origin part of the URL
tracingUrlOptionMatch('https://my.origin.com/api')
expect(originMatchSpy).toHaveBeenCalledWith('https://my.origin.com')
})

it('does not validate the configuration if a value is provided and service is undefined', () => {
expect(
validateAndBuildRumConfiguration({ ...DEFAULT_INIT_CONFIGURATION, allowedTracingOrigins: ['foo'] })
).toBeUndefined()
expect(displayErrorSpy).toHaveBeenCalledOnceWith('Service needs to be configured when tracing is enabled')
})

it('does not validate the configuration if an incorrect value is provided', () => {
expect(
validateAndBuildRumConfiguration({ ...DEFAULT_INIT_CONFIGURATION, allowedTracingOrigins: 'foo' as any })
).toBeUndefined()
expect(displayErrorSpy).toHaveBeenCalledOnceWith('Allowed Tracing Origins should be an array')
})
})

describe('allowedTracingUrls', () => {
it('defaults to an empty array', () => {
expect(validateAndBuildRumConfiguration(DEFAULT_INIT_CONFIGURATION)!.allowedTracingOrigins).toEqual([])
expect(validateAndBuildRumConfiguration(DEFAULT_INIT_CONFIGURATION)!.allowedTracingUrls).toEqual([])
})

it('is set to provided value', () => {
expect(
validateAndBuildRumConfiguration({
...DEFAULT_INIT_CONFIGURATION,
allowedTracingOrigins: ['foo'],
allowedTracingUrls: ['foo'],
service: 'bar',
})!.allowedTracingOrigins
).toEqual(['foo'])
})!.allowedTracingUrls
).toEqual([{ match: 'foo', propagatorTypes: ['datadog'] }])
})

it('accepts functions', () => {
const customOriginFunction = (origin: string): boolean => origin === 'foo'
const customOriginFunction = (url: string): boolean => url === 'https://my.origin.com'

expect(
validateAndBuildRumConfiguration({
...DEFAULT_INIT_CONFIGURATION,
allowedTracingOrigins: [customOriginFunction],
allowedTracingUrls: [customOriginFunction],
service: 'bar',
})!.allowedTracingOrigins
).toEqual([customOriginFunction])
})!.allowedTracingUrls
).toEqual([{ match: customOriginFunction, propagatorTypes: ['datadog'] }])
})

it('accepts RegExp', () => {
expect(
validateAndBuildRumConfiguration({
...DEFAULT_INIT_CONFIGURATION,
allowedTracingUrls: [/az/i],
service: 'bar',
})!.allowedTracingUrls
).toEqual([{ match: /az/i, propagatorTypes: ['datadog'] }])
})

it('keeps headers', () => {
expect(
validateAndBuildRumConfiguration({
...DEFAULT_INIT_CONFIGURATION,
allowedTracingUrls: [{ match: 'simple', propagatorTypes: ['b3multi', 'tracecontext'] }],
service: 'bar',
})!.allowedTracingUrls
).toEqual([{ match: 'simple', propagatorTypes: ['b3multi', 'tracecontext'] }])
})

it('should filter out unexpected parameter types', () => {
expect(
validateAndBuildRumConfiguration({
...DEFAULT_INIT_CONFIGURATION,
service: 'bar',
allowedTracingUrls: [
42 as any,
undefined,
{ match: 42 as any, propagatorTypes: ['datadog'] },
{ match: 'toto' },
],
})!.allowedTracingUrls
).toEqual([])

expect(displayWarnSpy).toHaveBeenCalledTimes(4)
})

it('does not validate the configuration if a value is provided and service is undefined', () => {
expect(
validateAndBuildRumConfiguration({ ...DEFAULT_INIT_CONFIGURATION, allowedTracingOrigins: ['foo'] })
validateAndBuildRumConfiguration({ ...DEFAULT_INIT_CONFIGURATION, allowedTracingUrls: ['foo'] })
).toBeUndefined()
expect(displayErrorSpy).toHaveBeenCalledOnceWith('Service need to be configured when tracing is enabled')
expect(displayErrorSpy).toHaveBeenCalledOnceWith('Service needs to be configured when tracing is enabled')
})

it('does not validate the configuration if an incorrect value is provided', () => {
expect(
validateAndBuildRumConfiguration({ ...DEFAULT_INIT_CONFIGURATION, allowedTracingOrigins: 'foo' as any })
validateAndBuildRumConfiguration({ ...DEFAULT_INIT_CONFIGURATION, allowedTracingUrls: 'foo' as any })
).toBeUndefined()
expect(displayErrorSpy).toHaveBeenCalledOnceWith('Allowed Tracing Origins should be an array')
expect(displayErrorSpy).toHaveBeenCalledOnceWith('Allowed Tracing URLs should be an array')
})
})

Expand Down Expand Up @@ -382,4 +461,36 @@ describe('validateAndBuildRumConfiguration', () => {
).toBeFalse()
})
})

describe('serializeRumConfiguration', () => {
describe('selected tracing propagators serialization', () => {
it('should not return any propagator type', () => {
expect(serializeRumConfiguration(DEFAULT_INIT_CONFIGURATION).selected_tracing_propagators).toEqual([])
})

it('should return Datadog propagator type', () => {
const simpleTracingConfig: RumInitConfiguration = {
...DEFAULT_INIT_CONFIGURATION,
allowedTracingUrls: ['foo'],
}
expect(serializeRumConfiguration(simpleTracingConfig).selected_tracing_propagators).toEqual(['datadog'])
})

it('should return all propagator types', () => {
const complexTracingConfig: RumInitConfiguration = {
...DEFAULT_INIT_CONFIGURATION,
allowedTracingUrls: [
'foo',
{ match: 'first', propagatorTypes: ['datadog'] },
{ match: 'test', propagatorTypes: ['tracecontext'] },
{ match: 'other', propagatorTypes: ['b3'] },
{ match: 'final', propagatorTypes: ['b3multi'] },
],
}
expect(serializeRumConfiguration(complexTracingConfig).selected_tracing_propagators).toEqual(
jasmine.arrayWithExactContents(['datadog', 'b3', 'b3multi', 'tracecontext'])
)
})
})
})
})
Loading

0 comments on commit 83b2be6

Please sign in to comment.