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

feat(surveys): return API only surveys method #736

Closed
wants to merge 3 commits into from
Closed
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
189 changes: 107 additions & 82 deletions src/__tests__/surveys.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,96 @@ describe('surveys', () => {
},
]

const draftSurvey = {
name: 'draft survey',
description: 'draft survey description',
type: SurveyType.Popover,
questions: [{ type: SurveyQuestionType.Open, question: 'what is a draft survey?' }],
start_date: null,
}
const activeSurvey = {
name: 'active survey',
description: 'active survey description',
type: SurveyType.Popover,
questions: [{ type: SurveyQuestionType.Open, question: 'what is a active survey?' }],
start_date: new Date().toISOString(),
end_date: null,
}
const completedSurvey = {
name: 'completed survey',
description: 'completed survey description',
type: SurveyType.Popover,
questions: [{ type: SurveyQuestionType.Open, question: 'what is a completed survey?' }],
start_date: new Date('09/10/2022').toISOString(),
end_date: new Date('10/10/2022').toISOString(),
}
const surveyWithUrl = {
name: 'survey with url',
description: 'survey with url description',
type: SurveyType.Popover,
questions: [{ type: SurveyQuestionType.Open, question: 'what is a survey with url?' }],
conditions: { url: 'posthog.com' },
start_date: new Date().toISOString(),
end_date: null,
}
const surveyWithSelector = {
name: 'survey with selector',
description: 'survey with selector description',
type: SurveyType.Popover,
questions: [{ type: SurveyQuestionType.Open, question: 'what is a survey with selector?' }],
conditions: { selector: '.test-selector' },
start_date: new Date().toISOString(),
end_date: null,
}
const surveyWithUrlAndSelector = {
name: 'survey with url and selector',
description: 'survey with url and selector description',
type: SurveyType.Popover,
questions: [{ type: SurveyQuestionType.Open, question: 'what is a survey with url and selector?' }],
conditions: { url: 'posthogapp.com', selector: '#foo' },
start_date: new Date().toISOString(),
end_date: null,
}
const surveyWithFlags = {
name: 'survey with flags',
description: 'survey with flags description',
type: SurveyType.Popover,
questions: [{ type: SurveyQuestionType.Open, question: 'what is a survey with flags?' }],
linked_flag_key: 'linked-flag-key',
targeting_flag_key: 'survey-targeting-flag-key',
start_date: new Date().toISOString(),
end_date: null,
}
const surveyWithUnmatchedFlags = {
name: 'survey with flags2',
description: 'survey with flags description',
type: SurveyType.Popover,
questions: [{ type: SurveyQuestionType.Open, question: 'what is a survey with flags?' }],
linked_flag_key: 'linked-flag-key2',
targeting_flag_key: 'survey-targeting-flag-key2',
start_date: new Date().toISOString(),
end_date: null,
}
const surveyWithEverything = {
name: 'survey with everything',
description: 'survey with everything description',
type: SurveyType.Popover,
questions: [{ type: SurveyQuestionType.Open, question: 'what is a survey with everything?' }],
start_date: new Date().toISOString(),
end_date: null,
conditions: { url: 'posthogapp.com', selector: '.test-selector' },
linked_flag_key: 'linked-flag-key',
targeting_flag_key: 'survey-targeting-flag-key',
}
const APISurvey = {
name: 'custom api survey',
description: '',
type: SurveyType.API,
questions: [],
start_date: new Date().toISOString(),
end_date: null,
}

given('surveysResponse', () => ({ surveys: firstSurveys }))

it('getSurveys gets a list of surveys if not present already', () => {
Expand Down Expand Up @@ -101,88 +191,6 @@ describe('surveys', () => {
})

describe('getActiveMatchingSurveys', () => {
const draftSurvey = {
name: 'draft survey',
description: 'draft survey description',
type: SurveyType.Popover,
questions: [{ type: SurveyQuestionType.Open, question: 'what is a draft survey?' }],
start_date: null,
}
const activeSurvey = {
name: 'active survey',
description: 'active survey description',
type: SurveyType.Popover,
questions: [{ type: SurveyQuestionType.Open, question: 'what is a active survey?' }],
start_date: new Date().toISOString(),
end_date: null,
}
const completedSurvey = {
name: 'completed survey',
description: 'completed survey description',
type: SurveyType.Popover,
questions: [{ type: SurveyQuestionType.Open, question: 'what is a completed survey?' }],
start_date: new Date('09/10/2022').toISOString(),
end_date: new Date('10/10/2022').toISOString(),
}
const surveyWithUrl = {
name: 'survey with url',
description: 'survey with url description',
type: SurveyType.Popover,
questions: [{ type: SurveyQuestionType.Open, question: 'what is a survey with url?' }],
conditions: { url: 'posthog.com' },
start_date: new Date().toISOString(),
end_date: null,
}
const surveyWithSelector = {
name: 'survey with selector',
description: 'survey with selector description',
type: SurveyType.Popover,
questions: [{ type: SurveyQuestionType.Open, question: 'what is a survey with selector?' }],
conditions: { selector: '.test-selector' },
start_date: new Date().toISOString(),
end_date: null,
}
const surveyWithUrlAndSelector = {
name: 'survey with url and selector',
description: 'survey with url and selector description',
type: SurveyType.Popover,
questions: [{ type: SurveyQuestionType.Open, question: 'what is a survey with url and selector?' }],
conditions: { url: 'posthogapp.com', selector: '#foo' },
start_date: new Date().toISOString(),
end_date: null,
}
const surveyWithFlags = {
name: 'survey with flags',
description: 'survey with flags description',
type: SurveyType.Popover,
questions: [{ type: SurveyQuestionType.Open, question: 'what is a survey with flags?' }],
linked_flag_key: 'linked-flag-key',
targeting_flag_key: 'survey-targeting-flag-key',
start_date: new Date().toISOString(),
end_date: null,
}
const surveyWithUnmatchedFlags = {
name: 'survey with flags2',
description: 'survey with flags description',
type: SurveyType.Popover,
questions: [{ type: SurveyQuestionType.Open, question: 'what is a survey with flags?' }],
linked_flag_key: 'linked-flag-key2',
targeting_flag_key: 'survey-targeting-flag-key2',
start_date: new Date().toISOString(),
end_date: null,
}
const surveyWithEverything = {
name: 'survey with everything',
description: 'survey with everything description',
type: SurveyType.Popover,
questions: [{ type: SurveyQuestionType.Open, question: 'what is a survey with everything?' }],
start_date: new Date().toISOString(),
end_date: null,
conditions: { url: 'posthogapp.com', selector: '.test-selector' },
linked_flag_key: 'linked-flag-key',
targeting_flag_key: 'survey-targeting-flag-key',
}

it('returns surveys that are active', () => {
given('surveysResponse', () => ({ surveys: [draftSurvey, activeSurvey, completedSurvey] }))

Expand Down Expand Up @@ -252,5 +260,22 @@ describe('surveys', () => {
expect(data).toEqual([activeSurvey, surveyWithSelector, surveyWithEverything])
})
})

it('does not return API surveys', () => {
given('surveysResponse', () => ({ surveys: [activeSurvey, APISurvey] }))
given.surveys.getActiveMatchingSurveys((data) => {
expect(data).toEqual([activeSurvey])
})
})
})

describe('getActiveMatchingAPISurveys', () => {
it('returns surveys that are API type only', () => {
given('surveysResponse', () => ({ surveys: [draftSurvey, activeSurvey, completedSurvey, APISurvey] }))

given.surveys.getActiveMatchingAPISurveys((data) => {
expect(data).toEqual([APISurvey])
})
})
})
})
16 changes: 13 additions & 3 deletions src/posthog-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ import { SentryIntegration } from './extensions/sentry-integration'
import { createSegmentIntegration } from './extensions/segment-integration'
import { PageViewIdManager } from './page-view-id'
import { ExceptionObserver } from './extensions/exceptions/exception-autocapture'
import { PostHogSurveys, SurveyCallback } from './posthog-surveys'
import { PostHogSurveys, Survey, SurveyCallback } from './posthog-surveys'

/*
SIMPLE STYLE GUIDE:
Expand Down Expand Up @@ -1179,16 +1179,26 @@ export class PostHog {
return this.sessionManager.onSessionId(callback)
}

/** Get list of all surveys. */
/** Get list of all existing surveys. */
getSurveys(callback: SurveyCallback, forceReload = false): void {
this.surveys.getSurveys(callback, forceReload)
}

/** Get surveys that should be enabled for the current user. */
/** Get list of all active matching surveys. */
getAllSurveyMatches(surveys: Survey[]): Survey[] {
return this.surveys.getAllSurveyMatches(surveys)
}

/** Get non API surveys that should be enabled for the current user. */
getActiveMatchingSurveys(callback: SurveyCallback, forceReload = false): void {
this.surveys.getActiveMatchingSurveys(callback, forceReload)
}

/** Get API surveys that should be enabled for the current user. */
getActiveMatchingAPISurveys(callback: SurveyCallback, forceReload = false): void {
this.surveys.getActiveMatchingAPISurveys(callback, forceReload)
}

/**
* Identify a user with a unique ID instead of a PostHog
* randomly generated distinct_id. If the method is never called,
Expand Down
75 changes: 44 additions & 31 deletions src/posthog-surveys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,25 @@ import { SURVEYS } from './posthog-persistence'
* See https://github.com/PostHog/posthog-js/issues/698
*/
export interface SurveyAppearance {
background_color?: string
button_color?: string
text_color?: string
backgroundColor?: string
submitButtonColor?: string
textColor?: string
submitButtonText?: string
descriptionTextColor?: string
}

export enum SurveyType {
Popover = 'Popover',
Button = 'Button',
Email = 'Email',
FullScreen = 'Fullscreen',
API = 'API',
}

export interface SurveyQuestion {
type: SurveyQuestionType
question: string
description?: string | null
required?: boolean
link?: boolean
choices?: string[]
Expand Down Expand Up @@ -81,37 +85,46 @@ export class PostHogSurveys {
}
}

getAllSurveyMatches(surveys: Survey[]) {
const activeSurveys = surveys.filter((survey) => {
return !!(survey.start_date && !survey.end_date)
})
const conditionMatchedSurveys = activeSurveys.filter((survey) => {
if (!survey.conditions) {
return true
}
const urlCheck = survey.conditions?.url ? window.location.href.indexOf(survey.conditions.url) > -1 : true
const selectorCheck = survey.conditions?.selector
? document.querySelector(survey.conditions.selector)
: true
return urlCheck && selectorCheck
})
const targetingMatchedSurveys = conditionMatchedSurveys.filter((survey) => {
if (!survey.linked_flag_key && !survey.targeting_flag_key) {
return true
}
const linkedFlagCheck = survey.linked_flag_key
? this.instance.featureFlags.isFeatureEnabled(survey.linked_flag_key)
: true
const targetingFlagCheck = survey.targeting_flag_key
? this.instance.featureFlags.isFeatureEnabled(survey.targeting_flag_key)
: true
return linkedFlagCheck && targetingFlagCheck
})
return targetingMatchedSurveys
}

getActiveMatchingSurveys(callback: SurveyCallback, forceReload = false) {
this.getSurveys((surveys) => {
const activeSurveys = surveys.filter((survey) => {
return !!(survey.start_date && !survey.end_date)
})
const conditionMatchedSurveys = activeSurveys.filter((survey) => {
if (!survey.conditions) {
return true
}
const urlCheck = survey.conditions?.url
? window.location.href.indexOf(survey.conditions.url) > -1
: true
const selectorCheck = survey.conditions?.selector
? document.querySelector(survey.conditions.selector)
: true
return urlCheck && selectorCheck
})
const targetingMatchedSurveys = conditionMatchedSurveys.filter((survey) => {
if (!survey.linked_flag_key && !survey.targeting_flag_key) {
return true
}
const linkedFlagCheck = survey.linked_flag_key
? this.instance.featureFlags.isFeatureEnabled(survey.linked_flag_key)
: true
const targetingFlagCheck = survey.targeting_flag_key
? this.instance.featureFlags.isFeatureEnabled(survey.targeting_flag_key)
: true
return linkedFlagCheck && targetingFlagCheck
})
const nonAPISurveys = this.getAllSurveyMatches(surveys).filter((survey) => survey.type !== SurveyType.API)
return callback(nonAPISurveys)
}, forceReload)
}

return callback(targetingMatchedSurveys)
getActiveMatchingAPISurveys(callback: SurveyCallback, forceReload = false) {
this.getSurveys((surveys) => {
const APISurveys = this.getAllSurveyMatches(surveys).filter((survey) => survey.type === SurveyType.API)
return callback(APISurveys)
}, forceReload)
}
}
Loading