Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add signup sdk method #4865

Draft
wants to merge 2 commits into
base: automatic_userid_blocking
Choose a base branch
from
Draft
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
3 changes: 3 additions & 0 deletions docs/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,9 @@ tracer.appsec.trackUserLoginFailureEvent('user_id', true, meta)
tracer.appsec.trackUserLoginFailureEvent('user_id', false)
tracer.appsec.trackUserLoginFailureEvent('user_id', false, meta)

tracer.appsec.trackUserSignupEvent('user_id')
tracer.appsec.trackUserSignupEvent('user_id', meta)

tracer.appsec.trackCustomEvent('event_name')
tracer.appsec.trackCustomEvent('event_name', meta)

Expand Down
25 changes: 17 additions & 8 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,7 @@ declare namespace tracer {
*/
maxDepth?: number
}

/**
* Configuration enabling LLM Observability. Enablement is superceded by the DD_LLMOBS_ENABLED environment variable.
*/
Expand Down Expand Up @@ -888,6 +888,15 @@ declare namespace tracer {
*/
trackUserLoginFailureEvent(userId: string, exists: boolean, metadata?: { [key: string]: string }): void

/**
* Links a signup event to the current trace.
* @param {string} userId The user id of the signed-up user
* @param {[key: string]: string} metadata Custom fields to link to the signup event.
*
* @beta This method is in beta and could change in future versions.
*/
trackUserSignupEvent(userId: string, metadata?: { [key: string]: string }): void

/**
* Links a custom event to the current trace.
* @param {string} eventName The name of the event.
Expand Down Expand Up @@ -2238,7 +2247,7 @@ declare namespace tracer {
* Disable LLM Observability tracing.
*/
disable (): void,

/**
* Instruments a function by automatically creating a span activated on its
* scope.
Expand Down Expand Up @@ -2280,10 +2289,10 @@ declare namespace tracer {
/**
* Decorate a function in a javascript runtime that supports function decorators.
* Note that this is **not** supported in the Node.js runtime, but is in TypeScript.
*
*
* In TypeScript, this decorator is only supported in contexts where general TypeScript
* function decorators are supported.
*
*
* @param options Optional LLM Observability span options.
*/
decorate (options: llmobs.LLMObsNamelessSpanOptions): any
Expand All @@ -2300,7 +2309,7 @@ declare namespace tracer {
/**
* Sets inputs, outputs, tags, metadata, and metrics as provided for a given LLM Observability span.
* Note that with the exception of tags, this method will override any existing values for the provided fields.
*
*
* For example:
* ```javascript
* llmobs.trace({ kind: 'llm', name: 'myLLM', modelName: 'gpt-4o', modelProvider: 'openai' }, () => {
Expand All @@ -2313,7 +2322,7 @@ declare namespace tracer {
* })
* })
* ```
*
*
* @param span The span to annotate (defaults to the current LLM Observability span if not provided)
* @param options An object containing the inputs, outputs, tags, metadata, and metrics to set on the span.
*/
Expand Down Expand Up @@ -2489,14 +2498,14 @@ declare namespace tracer {
* LLM Observability span kind. One of `agent`, `workflow`, `task`, `tool`, `retrieval`, `embedding`, or `llm`.
*/
kind: llmobs.spanKind,

/**
* The ID of the underlying user session. Required for tracking sessions.
*/
sessionId?: string,

/**
* The name of the ML application that the agent is orchestrating.
* The name of the ML application that the agent is orchestrating.
* If not provided, the default value will be set to mlApp provided during initalization, or `DD_LLMOBS_ML_APP`.
*/
mlApp?: string,
Expand Down
3 changes: 2 additions & 1 deletion packages/dd-trace/src/appsec/addresses.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ module.exports = {
DB_SYSTEM: 'server.db.system',

LOGIN_SUCCESS: 'server.business_logic.users.login.success',
LOGIN_FAILURE: 'server.business_logic.users.login.failure'
LOGIN_FAILURE: 'server.business_logic.users.login.failure',
SIGNUP: 'server.business_logic.users.signup'
}
6 changes: 5 additions & 1 deletion packages/dd-trace/src/appsec/sdk/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'

const { trackUserLoginSuccessEvent, trackUserLoginFailureEvent, trackCustomEvent } = require('./track_event')
const { trackUserLoginSuccessEvent, trackUserLoginFailureEvent, trackUserSignupEvent, trackCustomEvent } = require('./track_event')
const { checkUserAndSetUser, blockRequest } = require('./user_blocking')
const { setTemplates } = require('../blocking')
const { setUser } = require('./set_user')
Expand All @@ -21,6 +21,10 @@ class AppsecSdk {
return trackUserLoginFailureEvent(this._tracer, userId, exists, metadata)
}

trackUserSignupEvent (userId, metadata) {
return trackUserSignupEvent(this._tracer, userId, metadata)
}

trackCustomEvent (eventName, metadata) {
return trackCustomEvent(this._tracer, eventName, metadata)
}
Expand Down
23 changes: 22 additions & 1 deletion packages/dd-trace/src/appsec/sdk/track_event.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,26 @@ function trackUserLoginFailureEvent (tracer, userId, exists, metadata) {
trackEvent('users.login.failure', fields, 'trackUserLoginFailureEvent', getRootSpan(tracer), 'sdk')
}

function trackUserSignupEvent (tracer, userId, metadata) {
const rootSpan = getRootSpan(tracer)
if (!rootSpan) {
log.warn('Root span not available in trackUserSignupEvent')
return
}

if (!userId || typeof userId !== 'string') {
log.warn('Invalid userId provided to trackUserSignupEvent')
return
}

const fields = {
'usr.id': userId,
...metadata
}

trackEvent('users.signup', fields, 'trackUserSignupEvent', rootSpan, 'sdk')
}

function trackCustomEvent (tracer, eventName, metadata) {
if (!eventName || typeof eventName !== 'string') {
log.warn('Invalid eventName provided to trackCustomEvent')
Expand Down Expand Up @@ -99,14 +119,15 @@ function trackEvent (eventName, fields, sdkMethodName, rootSpan, mode, abortCont

standalone.sample(rootSpan)

if (['users.login.success', 'users.login.failure'].includes(eventName)) {
if (['users.login.success', 'users.login.failure', 'users.signup'].includes(eventName)) {
waf.run({ persistent: { [`server.business_logic.${eventName}`]: null } })
}
}

module.exports = {
trackUserLoginSuccessEvent,
trackUserLoginFailureEvent,
trackUserSignupEvent,
trackCustomEvent,
trackEvent
}
18 changes: 16 additions & 2 deletions packages/dd-trace/test/appsec/sdk/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const proxyquire = require('proxyquire')

describe('Appsec SDK', () => {
let trackUserLoginSuccessEvent, trackUserLoginFailureEvent, trackCustomEvent
let trackUserLoginSuccessEvent, trackUserLoginFailureEvent, trackUserSignupEvent, trackCustomEvent
let checkUserAndSetUser, blockRequest, setUser, setTemplates
let appsecSdk
const tracer = {}
Expand All @@ -12,14 +12,20 @@ describe('Appsec SDK', () => {
beforeEach(() => {
trackUserLoginSuccessEvent = sinon.stub()
trackUserLoginFailureEvent = sinon.stub()
trackUserSignupEvent = sinon.stub()
trackCustomEvent = sinon.stub()
checkUserAndSetUser = sinon.stub()
blockRequest = sinon.stub()
setTemplates = sinon.stub()
setUser = sinon.stub()

const AppsecSdk = proxyquire('../../../src/appsec/sdk', {
'./track_event': { trackUserLoginSuccessEvent, trackUserLoginFailureEvent, trackCustomEvent },
'./track_event': {
trackUserLoginSuccessEvent,
trackUserLoginFailureEvent,
trackUserSignupEvent,
trackCustomEvent
},
'./user_blocking': { checkUserAndSetUser, blockRequest },
'../blocking': { setTemplates },
'./set_user': { setUser }
Expand Down Expand Up @@ -49,6 +55,14 @@ describe('Appsec SDK', () => {
expect(trackUserLoginFailureEvent).to.have.been.calledOnceWithExactly(tracer, userId, exists, metadata)
})

it('trackUserSignupEvent should call internal function with proper params', () => {
const userId = 'user_id'
const metadata = { key: 'value' }
appsecSdk.trackUserSignupEvent(userId, metadata)

expect(trackUserSignupEvent).to.have.been.calledOnceWithExactly(tracer, userId, metadata)
})

it('trackCustomEvent should call internal function with proper params', () => {
const eventName = 'customEvent'
const metadata = { key: 'value' }
Expand Down
86 changes: 84 additions & 2 deletions packages/dd-trace/test/appsec/sdk/track_event.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const proxyquire = require('proxyquire')
const agent = require('../../plugins/agent')
const axios = require('axios')
const tracer = require('../../../../../index')
const { LOGIN_SUCCESS, LOGIN_FAILURE } = require('../../../src/appsec/addresses')
const { LOGIN_SUCCESS, LOGIN_FAILURE, SIGNUP } = require('../../../src/appsec/addresses')
const { SAMPLING_MECHANISM_APPSEC } = require('../../../src/constants')
const { USER_KEEP } = require('../../../../../ext/priority')

Expand All @@ -15,7 +15,7 @@ describe('track_event', () => {
let rootSpan
let getRootSpan
let setUserTags
let trackUserLoginSuccessEvent, trackUserLoginFailureEvent, trackCustomEvent, trackEvent
let trackUserLoginSuccessEvent, trackUserLoginFailureEvent, trackUserSignupEvent, trackCustomEvent, trackEvent
let sample
let waf
let prioritySampler
Expand Down Expand Up @@ -61,6 +61,7 @@ describe('track_event', () => {

trackUserLoginSuccessEvent = trackEvents.trackUserLoginSuccessEvent
trackUserLoginFailureEvent = trackEvents.trackUserLoginFailureEvent
trackUserSignupEvent = trackEvents.trackUserSignupEvent
trackCustomEvent = trackEvents.trackCustomEvent
trackEvent = trackEvents.trackEvent
})
Expand Down Expand Up @@ -227,6 +228,87 @@ describe('track_event', () => {
})
})

describe('trackUserSignupEvent', () => {
it('should log warning when passed invalid userId', () => {
trackUserSignupEvent(tracer, null)
trackUserSignupEvent(tracer, [])

expect(log.warn).to.have.been.calledTwice
expect(log.warn.firstCall)
.to.have.been.calledWithExactly('Invalid userId provided to trackUserSignupEvent')
expect(log.warn.secondCall)
.to.have.been.calledWithExactly('Invalid userId provided to trackUserSignupEvent')
expect(rootSpan.addTags).to.not.have.been.called
})

it('should log warning when root span is not available', () => {
rootSpan = undefined

trackUserSignupEvent(tracer, 'user_id')

expect(log.warn).to.have.been.calledOnceWithExactly('Root span not available in trackUserSignupEvent')
})

it('should call addTags with metadata', () => {
trackUserSignupEvent(tracer, 'user_id', {
metakey1: 'metaValue1', metakey2: 'metaValue2', metakey3: 'metaValue3'
})

expect(log.warn).to.not.have.been.called
expect(rootSpan.addTags).to.have.been.calledOnceWithExactly({
'appsec.events.users.signup.track': 'true',
'_dd.appsec.events.users.signup.sdk': 'true',
'appsec.events.users.signup.usr.id': 'user_id',
'appsec.events.users.signup.metakey1': 'metaValue1',
'appsec.events.users.signup.metakey2': 'metaValue2',
'appsec.events.users.signup.metakey3': 'metaValue3'
})
expect(prioritySampler.setPriority)
.to.have.been.calledOnceWithExactly(rootSpan, USER_KEEP, SAMPLING_MECHANISM_APPSEC)
})

it('should send false `usr.exists` property when the user does not exist', () => {
trackUserSignupEvent(tracer, 'user_id', {
metakey1: 'metaValue1', metakey2: 'metaValue2', metakey3: 'metaValue3'
})

expect(log.warn).to.not.have.been.called
expect(setUserTags).to.not.have.been.called
expect(rootSpan.addTags).to.have.been.calledOnceWithExactly({
'appsec.events.users.signup.track': 'true',
'_dd.appsec.events.users.signup.sdk': 'true',
'appsec.events.users.signup.usr.id': 'user_id',
'appsec.events.users.signup.metakey1': 'metaValue1',
'appsec.events.users.signup.metakey2': 'metaValue2',
'appsec.events.users.signup.metakey3': 'metaValue3'
})
expect(prioritySampler.setPriority)
.to.have.been.calledOnceWithExactly(rootSpan, USER_KEEP, SAMPLING_MECHANISM_APPSEC)
})

it('should call addTags without metadata', () => {
trackUserSignupEvent(tracer, 'user_id')

expect(log.warn).to.not.have.been.called
expect(setUserTags).to.not.have.been.called
expect(rootSpan.addTags).to.have.been.calledOnceWithExactly({
'appsec.events.users.signup.track': 'true',
'_dd.appsec.events.users.signup.sdk': 'true',
'appsec.events.users.signup.usr.id': 'user_id'
})
expect(prioritySampler.setPriority)
.to.have.been.calledOnceWithExactly(rootSpan, USER_KEEP, SAMPLING_MECHANISM_APPSEC)
})

it('should call waf run with signup address', () => {
trackUserSignupEvent(tracer, 'user_id')
sinon.assert.calledOnceWithExactly(
waf.run,
{ persistent: { [SIGNUP]: null } }
)
})
})

describe('trackCustomEvent', () => {
it('should log warning when passed invalid eventName', () => {
trackCustomEvent(tracer, null)
Expand Down
Loading