Merge branch 'master' into test-speedup

This commit is contained in:
Sam Rose 2025-03-05 10:50:07 +00:00 committed by GitHub
commit 5da775c2b8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 89 additions and 25 deletions

View File

@ -1,9 +1,12 @@
import posthog from "posthog-js" import posthog from "posthog-js"
import { Events } from "./constants"
export default class PosthogClient { export default class PosthogClient {
constructor(token) { token: string
initialised: boolean
constructor(token: string) {
this.token = token this.token = token
this.initialised = false
} }
init() { init() {
@ -12,6 +15,8 @@ export default class PosthogClient {
posthog.init(this.token, { posthog.init(this.token, {
autocapture: false, autocapture: false,
capture_pageview: false, capture_pageview: false,
// disable by default
disable_session_recording: true,
}) })
posthog.set_config({ persistence: "cookie" }) posthog.set_config({ persistence: "cookie" })
@ -22,7 +27,7 @@ export default class PosthogClient {
* Set the posthog context to the current user * Set the posthog context to the current user
* @param {String} id - unique user id * @param {String} id - unique user id
*/ */
identify(id) { identify(id: string) {
if (!this.initialised) return if (!this.initialised) return
posthog.identify(id) posthog.identify(id)
@ -32,7 +37,7 @@ export default class PosthogClient {
* Update user metadata associated with current user in posthog * Update user metadata associated with current user in posthog
* @param {Object} meta - user fields * @param {Object} meta - user fields
*/ */
updateUser(meta) { updateUser(meta: Record<string, any>) {
if (!this.initialised) return if (!this.initialised) return
posthog.people.set(meta) posthog.people.set(meta)
@ -43,28 +48,22 @@ export default class PosthogClient {
* @param {String} event - event identifier * @param {String} event - event identifier
* @param {Object} props - properties for the event * @param {Object} props - properties for the event
*/ */
captureEvent(eventName, props) { captureEvent(event: string, props: Record<string, any>) {
if (!this.initialised) return if (!this.initialised) {
return
}
props.sourceApp = "builder" props.sourceApp = "builder"
posthog.capture(eventName, props) posthog.capture(event, props)
} }
/** enableSessionRecording() {
* Submit NPS feedback to posthog. if (!this.initialised) {
* @param {Object} values - NPS Values return
*/
npsFeedback(values) {
if (!this.initialised) return
localStorage.setItem(Events.NPS.SUBMITTED, Date.now())
const prefixedFeedback = {}
for (let key in values) {
prefixedFeedback[`feedback_${key}`] = values[key]
} }
posthog.set_config({
posthog.capture(Events.NPS.SUBMITTED, prefixedFeedback) disable_session_recording: false,
})
} }
/** /**

View File

@ -31,6 +31,10 @@ class AnalyticsHub {
posthog.captureEvent(eventName, props) posthog.captureEvent(eventName, props)
} }
enableSessionRecording() {
posthog.enableSessionRecording()
}
async logout() { async logout() {
posthog.logout() posthog.logout()
} }

View File

@ -8,6 +8,7 @@ import {
SystemStatusResponse, SystemStatusResponse,
} from "@budibase/types" } from "@budibase/types"
import { BudiStore } from "../BudiStore" import { BudiStore } from "../BudiStore"
import Analytics from "../../analytics"
interface AdminState extends GetEnvironmentResponse { interface AdminState extends GetEnvironmentResponse {
loaded: boolean loaded: boolean
@ -33,6 +34,8 @@ export class AdminStore extends BudiStore<AdminState> {
await this.getEnvironment() await this.getEnvironment()
// enable system status checks in the cloud // enable system status checks in the cloud
if (get(this.store).cloud) { if (get(this.store).cloud) {
// in cloud allow this
Analytics.enableSessionRecording()
await this.getSystemStatus() await this.getSystemStatus()
this.checkStatus() this.checkStatus()
} }

View File

@ -36,6 +36,7 @@ export const HelperFunctionNames = {
ALL: "all", ALL: "all",
LITERAL: "literal", LITERAL: "literal",
JS: "js", JS: "js",
DECODE_ID: "decodeId",
} }
export const LITERAL_MARKER = "%LITERAL%" export const LITERAL_MARKER = "%LITERAL%"

View File

@ -25,13 +25,29 @@ function isObject(value: string | any[]) {
) )
} }
const HELPERS = [ export const HELPERS = [
// external helpers // external helpers
new Helper(HelperFunctionNames.OBJECT, (value: any) => { new Helper(HelperFunctionNames.OBJECT, (value: any) => {
return new Handlebars.SafeString(JSON.stringify(value)) return new Handlebars.SafeString(JSON.stringify(value))
}), }),
// javascript helper // javascript helper
new Helper(HelperFunctionNames.JS, processJS, false), new Helper(HelperFunctionNames.JS, processJS, false),
new Helper(HelperFunctionNames.DECODE_ID, (_id: string | { _id: string }) => {
if (!_id) {
return []
}
// have to replace on the way back as we swapped out the double quotes
// when encoding, but JSON can't handle the single quotes
const id = typeof _id === "string" ? _id : _id._id
const decoded: string = decodeURIComponent(id).replace(/'/g, '"')
try {
const parsed = JSON.parse(decoded)
return Array.isArray(parsed) ? parsed : [parsed]
} catch (err) {
// wasn't json - likely was handlebars for a many to many
return [_id]
}
}),
// this help is applied to all statements // this help is applied to all statements
new Helper( new Helper(
HelperFunctionNames.ALL, HelperFunctionNames.ALL,

View File

@ -517,3 +517,44 @@ describe("helper overlap", () => {
expect(output).toEqual("a") expect(output).toEqual("a")
}) })
}) })
describe("Test the decodeId helper", () => {
it("should decode a valid encoded ID", async () => {
const encodedId = encodeURIComponent("[42]") // "%5B42%5D"
const output = await processString("{{ decodeId id }}", { id: encodedId })
expect(output).toBe("42")
})
it("Should return an unchanged string if the string isn't encoded", async () => {
const unencodedId = "forty-two"
const output = await processString("{{ decodeId id }}", { id: unencodedId })
expect(output).toBe("forty-two")
})
it("Should return a string of comma-separated IDs when passed multiple IDs in a URI encoded array", async () => {
const encodedIds = encodeURIComponent("[1,2,3]") // "%5B1%2C2%2C3%5D"
const output = await processString("{{ decodeId id }}", { id: encodedIds })
expect(output).toBe("1,2,3")
})
it("Handles empty array gracefully", async () => {
const output = await processString("{{ decodeId value }}", {
value: [],
})
expect(output).toBe("[[]]")
})
it("Handles undefined gracefully", async () => {
const output = await processString("{{ decodeId value }}", {
value: undefined,
})
expect(output).toBe("")
})
it("Handles null gracefully", async () => {
const output = await processString("{{ decodeId value }}", {
value: undefined,
})
expect(output).toBe("")
})
})