Skip to content
This repository has been archived by the owner on Dec 9, 2024. It is now read-only.

[CM-1540] Add support for ip and useragent #517

Merged
merged 6 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"url": "git+https://github.com/liveintent-berlin/live-connect.git"
},
"description": "LiveConnect, The first party identity provider",
"version": "7.0.0",
"version": "7.1.0-alpha-2b73ebd.0",
"versionPrefix": "live-connect-v",
"license": "Apache-2.0",
"private": false,
Expand Down Expand Up @@ -61,7 +61,7 @@
"release:ci:major": "release-it major --ci"
},
"dependencies": {
"live-connect-common": "^v4.0.0",
"live-connect-common": "^v4.1.0",
"tiny-hashes": "1.0.1"
},
"devDependencies": {
Expand Down Expand Up @@ -109,7 +109,7 @@
"eslint-plugin-wdio": "^9.0.5",
"express": "^4.19.2",
"global-jsdom": "^25.0.0",
"live-connect-handlers": "^3.0.0",
"live-connect-handlers": "^3.1.0",
"mocha": "^10.6.0",
"mocha-junit-reporter": "^2.2.1",
"release-it": "^17.4.1",
Expand Down
31 changes: 13 additions & 18 deletions src/events/error-pixel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@ import { EventBus, State } from '../types.js'

const MAX_ERROR_FIELD_LENGTH = 120

const defaultReturn: { errorDetails: ErrorDetails } = {
errorDetails: {
message: 'Unknown message',
name: 'Unknown name'
}
}

function _asInt(field: unknown): number | undefined {
try {
const intValue = (field as number) * 1
Expand All @@ -32,27 +25,29 @@ function _truncate(value: unknown): string | undefined {
}
}

export function asErrorDetails(e: unknown): { errorDetails: ErrorDetails } {
export function asErrorDetails(e: unknown): ErrorDetails {
if (isRecord(e)) {
return {
errorDetails: {
message: _truncate(e.message) || '',
name: _truncate(e.name) || '',
stackTrace: _truncate(e.stack),
lineNumber: _asInt(e.lineNumber),
columnNumber: _asInt(e.columnNumber),
fileName: _truncate(e.fileName)
}
message: _truncate(e.message) || '',
name: _truncate(e.name) || '',
stackTrace: _truncate(e.stack),
lineNumber: _asInt(e.lineNumber),
columnNumber: _asInt(e.columnNumber),
fileName: _truncate(e.fileName)
}
} else {
return defaultReturn
return {
message: 'Unknown message',
name: 'Unknown name'
}
}
}

export function register(state: State, sender: PixelSender, eventBus: EventBus): void {
try {
eventBus.on(ERRORS_CHANNEL, (error) => {
sender.sendPixel(new StateWrapper({ ...state, ...asErrorDetails(error) }, eventBus))
const wrapped = StateWrapper.fromError(state, asErrorDetails(error), eventBus)
sender.sendPixel(wrapped)
})
} catch (e) {
console.error('handlers.error.register', e)
Expand Down
7 changes: 4 additions & 3 deletions src/handlers/call-handler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EventBus, CallHandler } from 'live-connect-common'
import { EventBus, CallHandler, Headers } from 'live-connect-common'
import { Wrapped, WrappingContext } from '../utils/wrapping.js'

const empty = () => undefined
Expand Down Expand Up @@ -28,9 +28,10 @@ export class WrappedCallHandler implements CallHandler {
url: string,
onSuccess: (responseText: string, response: unknown) => void,
onError?: (error: unknown) => void,
timeout?: number
timeout?: number,
headers?: Headers
): void {
this.functions.ajaxGet(url, onSuccess, onError, timeout)
this.functions.ajaxGet(url, onSuccess, onError, timeout, headers)
}

pixelGet(
Expand Down
7 changes: 6 additions & 1 deletion src/idex.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { isFunction, isObject, isString, onNonNull } from 'live-connect-common'
import { QueryBuilder, encodeIdCookie } from './utils/query.js'
import { DEFAULT_IDEX_AJAX_TIMEOUT, DEFAULT_IDEX_URL, DEFAULT_REQUESTED_ATTRIBUTES } from './utils/consts.js'
import { IdentityResolutionConfig, State, ResolutionParams, EventBus, RetrievedIdentifier } from './types.js'
import { IdentityResolutionConfig, State, ResolutionParams, EventBus, RetrievedIdentifier, ExtraIdexAttributes } from './types.js'
import { WrappedCallHandler } from './handlers/call-handler.js'
import { stripQueryAndPath } from './pixel/url-collector.js'
import { base64UrlEncode } from './utils/b64.js'

const ID_COOKIE_ATTR = 'idCookie'

Expand All @@ -20,6 +21,7 @@ export class IdentityResolver {
publisherId: number | string
url: string
timeout: number
extraAttributes: ExtraIdexAttributes
requestedAttributes: string[]
// Be careful, this object is mutable. In cases where temporary changes are needed
// - e.g. adding parameters that are only valid for one single call - ensure to copy it
Expand All @@ -40,6 +42,7 @@ export class IdentityResolver {
this.eventBus = eventBus
this.calls = calls
this.idexConfig = nonNullConfig.identityResolutionConfig || {}
this.extraAttributes = this.idexConfig.extraAttributes || {}
this.externalIds = nonNullConfig.retrievedIdentifiers || []
this.source = this.idexConfig.source || 'unknown'
this.publisherId = this.idexConfig.publisherId || 'any'
Expand All @@ -63,6 +66,8 @@ export class IdentityResolver {
.addOptional('cd', nonNullConfig.cookieDomain)
.addOptional('ic', encodeIdCookie(nonNullConfig.resolvedIdCookie), { stripEmpty: false })
.addOptional('pu', onNonNull(nonNullConfig.pageUrl, stripQueryAndPath))
.addOptional('pip', onNonNull(this.extraAttributes.ipv4, v => base64UrlEncode(v)))
.addOptional('pip6', onNonNull(this.extraAttributes.ipv6, v => base64UrlEncode(v)))

this.externalIds.forEach(retrievedIdentifier => {
this.query.add(retrievedIdentifier.name, retrievedIdentifier.value)
Expand Down
70 changes: 46 additions & 24 deletions src/pixel/fiddler.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,77 @@
import { extractEmail } from '../utils/email.js'
import { decodeValue } from '../utils/url.js'
import { extractHashValue, hashEmail, isHash } from '../utils/hash.js'
import { isArray, isObject, safeToString, trim } from 'live-connect-common'
import { HashedEmail } from '../types.js'
import { isArray, isObject, isRecord, safeToString, trim } from 'live-connect-common'
import { FiddlerExtraFields } from '../types.js'

type AnyRecord = Record<string | symbol | number, unknown>

const MAX_ITEMS = 10
const LIMITING_KEYS = ['items', 'itemids']
const HASH_BEARERS = ['email', 'emailhash', 'hash', 'hashedemail']

function provided<A extends { eventSource?: Record<string, unknown> }>(state: A): A & { hashedEmail?: string[] } {
const eventSource = state.eventSource || {}
const objectKeys = Object.keys(eventSource)
for (const key of objectKeys) {
function extractProvidedAttributes(eventSource: AnyRecord): FiddlerExtraFields {
const extraFields: FiddlerExtraFields = { eventSource }

// add provided email hashes. Only consider the first one found.
for (const key of Object.keys(eventSource)) {
const lowerCased = key.toLowerCase()
if (HASH_BEARERS.indexOf(lowerCased) > -1) {
const value = trim(safeToString(eventSource[key as keyof (typeof eventSource)]))
const value = trim(safeToString(eventSource[key]))
const extractedEmail = extractEmail(value)
const extractedHash = extractHashValue(value)
if (extractedEmail) {
const hashes = hashEmail(decodeValue(extractedEmail))
return mergeObjects({ hashedEmail: [hashes.md5, hashes.sha1, hashes.sha256] }, state)
extraFields.hashedEmail = [hashes.md5, hashes.sha1, hashes.sha256]
break
} else if (extractedHash && isHash(extractedHash)) {
return mergeObjects({ hashedEmail: [extractedHash.toLowerCase()] }, state)
extraFields.hashedEmail = [extractedHash.toLowerCase()]
break
}
}
}
return state

// add provided user agent
if (typeof eventSource.userAgent === 'string') {
extraFields.providedUserAgent = eventSource.userAgent
}

// add provided ip4 address
if (typeof eventSource.ipv4 === 'string') {
extraFields.providedIPV4 = eventSource.ipv4
}

// add provided ip6 address
if (typeof eventSource.ipv6 === 'string') {
extraFields.providedIPV6 = eventSource.ipv6
}

return extraFields
}

function itemsLimiter(state: { eventSource?: Record<string, unknown> }): Record<string, never> {
const event = state.eventSource || {}
function limitItems(event: AnyRecord): AnyRecord {
const limitedEvent: AnyRecord = {}
Object.keys(event).forEach(key => {
const lowerCased = key.toLowerCase()
const value = event[key as keyof typeof event] as unknown
const value = event[key]
if (LIMITING_KEYS.indexOf(lowerCased) > -1 && isArray(value) && value.length > MAX_ITEMS) {
value.length = MAX_ITEMS
limitedEvent[key] = value.slice(0, MAX_ITEMS)
} else {
limitedEvent[key] = value
}
})
return {}
return limitedEvent
}

const fiddlers = [provided, itemsLimiter]

export function fiddle<A extends { eventSource?: Record<string, unknown> }>(state: A): A & { hashedEmail?: HashedEmail[] } {
function reducer<B extends object>(accumulator: A, func: (current: A) => B): A & B {
return mergeObjects(accumulator, func(accumulator))
}
if (isObject(state.eventSource)) {
return fiddlers.reduce(reducer, state)
export function fiddle(event: object): FiddlerExtraFields {
if (isRecord(event)) {
const extraAttributes = extractProvidedAttributes(event)
return {
...extraAttributes,
eventSource: limitItems(event)
}
} else {
return state
return {}
}
}

Expand Down
6 changes: 5 additions & 1 deletion src/pixel/sender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ export class PixelSender {
sendAjax(state: StateWrapper, opts: { onPreSend?: () => void, onLoad?: () => void } = {}): void {
this.sendState(state, 'j', uri => {
const go = (remainingRetries: number) => {
// additionally set headers extracted from the state only if the state is not empty
const headers = state.asHeaders()

this.calls.ajaxGet(
uri,
bakersJson => {
Expand All @@ -75,7 +78,8 @@ export class PixelSender {
go(remainingRetries - 1)
}
},
this.timeout
this.timeout,
headers
)
}

Expand Down
Loading
Loading