Merge pull request #7194 from Budibase/trigger-served-ping
Trigger served events on ping endpoint
This commit is contained in:
commit
d1abb4f7b4
|
@ -1,27 +0,0 @@
|
|||
import { EventEmitter } from "events"
|
||||
import * as context from "../../context"
|
||||
import { Identity, Event } from "@budibase/types"
|
||||
|
||||
export interface EmittedEvent {
|
||||
tenantId: string
|
||||
identity: Identity
|
||||
appId: string | undefined
|
||||
properties: any
|
||||
}
|
||||
|
||||
class BBEventEmitter extends EventEmitter {
|
||||
emitEvent(event: Event, properties: any, identity: Identity) {
|
||||
const tenantId = context.getTenantId()
|
||||
const appId = context.getAppId()
|
||||
|
||||
const emittedEvent: EmittedEvent = {
|
||||
tenantId,
|
||||
identity,
|
||||
appId,
|
||||
properties,
|
||||
}
|
||||
this.emit(event, emittedEvent)
|
||||
}
|
||||
}
|
||||
|
||||
export const emitter = new BBEventEmitter()
|
|
@ -1 +0,0 @@
|
|||
export * from "./BBEventEmitter"
|
|
@ -2,41 +2,6 @@ import { Event } from "@budibase/types"
|
|||
import { processors } from "./processors"
|
||||
import * as identification from "./identification"
|
||||
import * as backfill from "./backfill"
|
||||
import { emitter, EmittedEvent } from "./emit"
|
||||
import * as context from "../context"
|
||||
import * as logging from "../logging"
|
||||
|
||||
const USE_EMITTER: any[] = [
|
||||
Event.SERVED_BUILDER,
|
||||
Event.SERVED_APP,
|
||||
Event.SERVED_APP_PREVIEW,
|
||||
]
|
||||
|
||||
for (let event of USE_EMITTER) {
|
||||
emitter.on(event, async (props: EmittedEvent) => {
|
||||
try {
|
||||
await context.doInTenant(props.tenantId, async () => {
|
||||
if (props.appId) {
|
||||
await context.doInAppContext(props.appId, async () => {
|
||||
await processors.processEvent(
|
||||
event as Event,
|
||||
props.identity,
|
||||
props.properties
|
||||
)
|
||||
})
|
||||
} else {
|
||||
await processors.processEvent(
|
||||
event as Event,
|
||||
props.identity,
|
||||
props.properties
|
||||
)
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
logging.logAlert(`Unable to process async event ${event}`, e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const publishEvent = async (
|
||||
event: Event,
|
||||
|
@ -46,11 +11,6 @@ export const publishEvent = async (
|
|||
// in future this should use async events via a distributed queue.
|
||||
const identity = await identification.getCurrentIdentity()
|
||||
|
||||
if (USE_EMITTER.includes(event)) {
|
||||
emitter.emitEvent(event, properties, identity)
|
||||
return
|
||||
}
|
||||
|
||||
const backfilling = await backfill.isBackfillingEvent(event)
|
||||
// no backfill - send the event and exit
|
||||
if (!backfilling) {
|
||||
|
|
|
@ -7,22 +7,26 @@ import {
|
|||
AppServedEvent,
|
||||
} from "@budibase/types"
|
||||
|
||||
export async function servedBuilder() {
|
||||
const properties: BuilderServedEvent = {}
|
||||
export async function servedBuilder(timezone: string) {
|
||||
const properties: BuilderServedEvent = {
|
||||
timezone,
|
||||
}
|
||||
await publishEvent(Event.SERVED_BUILDER, properties)
|
||||
}
|
||||
|
||||
export async function servedApp(app: App) {
|
||||
export async function servedApp(app: App, timezone: string) {
|
||||
const properties: AppServedEvent = {
|
||||
appVersion: app.version,
|
||||
timezone,
|
||||
}
|
||||
await publishEvent(Event.SERVED_APP, properties)
|
||||
}
|
||||
|
||||
export async function servedAppPreview(app: App) {
|
||||
export async function servedAppPreview(app: App, timezone: string) {
|
||||
const properties: AppPreviewServedEvent = {
|
||||
appId: app.appId,
|
||||
appVersion: app.version,
|
||||
timezone,
|
||||
}
|
||||
await publishEvent(Event.SERVED_APP_PREVIEW, properties)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import { admin, auth } from "stores/portal"
|
||||
import { onMount } from "svelte"
|
||||
import { CookieUtils, Constants } from "@budibase/frontend-core"
|
||||
import { API } from "api"
|
||||
|
||||
let loaded = false
|
||||
|
||||
|
@ -53,6 +54,9 @@
|
|||
await auth.setOrganisation(urlTenantId)
|
||||
}
|
||||
}
|
||||
async function analyticsPing() {
|
||||
await API.analyticsPing({ source: "builder" })
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
|
@ -73,6 +77,9 @@
|
|||
// being logged in
|
||||
}
|
||||
loaded = true
|
||||
|
||||
// lastly
|
||||
await analyticsPing()
|
||||
})
|
||||
|
||||
$: {
|
||||
|
|
|
@ -83,6 +83,8 @@
|
|||
dataLoaded = true
|
||||
if (get(builderStore).inBuilder) {
|
||||
builderStore.actions.notifyLoaded()
|
||||
} else {
|
||||
builderStore.actions.analyticsPing({ source: "app" })
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { writable, get } from "svelte/store"
|
||||
import { API } from "api"
|
||||
import { devToolsStore } from "./devTools.js"
|
||||
|
||||
const dispatchEvent = (type, data = {}) => {
|
||||
|
@ -48,6 +49,13 @@ const createBuilderStore = () => {
|
|||
notifyLoaded: () => {
|
||||
dispatchEvent("preview-loaded")
|
||||
},
|
||||
analyticsPing: async () => {
|
||||
try {
|
||||
await API.analyticsPing({ source: "app" })
|
||||
} catch (error) {
|
||||
// Do nothing
|
||||
}
|
||||
},
|
||||
moveComponent: (componentId, destinationComponentId, mode) => {
|
||||
dispatchEvent("move-component", {
|
||||
componentId,
|
||||
|
|
|
@ -7,4 +7,11 @@ export const buildAnalyticsEndpoints = API => ({
|
|||
url: "/api/bbtel",
|
||||
})
|
||||
},
|
||||
analyticsPing: async ({ source }) => {
|
||||
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||
return await API.post({
|
||||
url: "/api/bbtel/ping",
|
||||
body: { source, timezone },
|
||||
})
|
||||
},
|
||||
})
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import { events } from "@budibase/backend-core"
|
||||
import { AnalyticsPingRequest, PingSource } from "@budibase/types"
|
||||
import { DocumentTypes, isDevAppID } from "../../db/utils"
|
||||
import { context } from "@budibase/backend-core"
|
||||
|
||||
export const isEnabled = async (ctx: any) => {
|
||||
const enabled = await events.analytics.enabled()
|
||||
|
@ -6,3 +9,27 @@ export const isEnabled = async (ctx: any) => {
|
|||
enabled,
|
||||
}
|
||||
}
|
||||
|
||||
export const ping = async (ctx: any) => {
|
||||
const body = ctx.request.body as AnalyticsPingRequest
|
||||
switch (body.source) {
|
||||
case PingSource.APP: {
|
||||
const db = context.getAppDB({ skip_setup: true })
|
||||
const appInfo = await db.get(DocumentTypes.APP_METADATA)
|
||||
let appId = context.getAppId()
|
||||
|
||||
if (isDevAppID(appId)) {
|
||||
await events.serve.servedAppPreview(appInfo, body.timezone)
|
||||
} else {
|
||||
await events.serve.servedApp(appInfo, body.timezone)
|
||||
}
|
||||
break
|
||||
}
|
||||
case PingSource.BUILDER: {
|
||||
await events.serve.servedBuilder(body.timezone)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
ctx.status = 200
|
||||
}
|
||||
|
|
|
@ -14,11 +14,10 @@ const env = require("../../../environment")
|
|||
const { clientLibraryPath } = require("../../../utilities")
|
||||
const { upload } = require("../../../utilities/fileSystem")
|
||||
const { attachmentsRelativeURL } = require("../../../utilities")
|
||||
const { DocumentTypes, isDevAppID } = require("../../../db/utils")
|
||||
const { DocumentTypes } = require("../../../db/utils")
|
||||
const { getAppDB, getAppId } = require("@budibase/backend-core/context")
|
||||
const { setCookie, clearCookie } = require("@budibase/backend-core/utils")
|
||||
const AWS = require("aws-sdk")
|
||||
import { events } from "@budibase/backend-core"
|
||||
|
||||
const fs = require("fs")
|
||||
const {
|
||||
|
@ -75,9 +74,6 @@ export const toggleBetaUiFeature = async function (ctx: any) {
|
|||
export const serveBuilder = async function (ctx: any) {
|
||||
const builderPath = resolve(TOP_LEVEL_PATH, "builder")
|
||||
await send(ctx, ctx.file, { root: builderPath })
|
||||
if (ctx.file === "index.html") {
|
||||
await events.serve.servedBuilder()
|
||||
}
|
||||
}
|
||||
|
||||
export const uploadFile = async function (ctx: any) {
|
||||
|
@ -126,12 +122,6 @@ export const serveApp = async function (ctx: any) {
|
|||
// just return the app info for jest to assert on
|
||||
ctx.body = appInfo
|
||||
}
|
||||
|
||||
if (isDevAppID(appInfo.appId)) {
|
||||
await events.serve.servedAppPreview(appInfo)
|
||||
} else {
|
||||
await events.serve.servedApp(appInfo)
|
||||
}
|
||||
}
|
||||
|
||||
export const serveClientLibrary = async function (ctx: any) {
|
||||
|
|
|
@ -4,5 +4,6 @@ const controller = require("../controllers/analytics")
|
|||
const router = Router()
|
||||
|
||||
router.get("/api/bbtel", controller.isEnabled)
|
||||
router.post("/api/bbtel/ping", controller.ping)
|
||||
|
||||
module.exports = router
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
const setup = require("./utilities")
|
||||
const { events, constants, db } = require("@budibase/backend-core")
|
||||
|
||||
describe("/static", () => {
|
||||
let request = setup.getRequest()
|
||||
let config = setup.getConfig()
|
||||
let app
|
||||
|
||||
const timezone = "Europe/London"
|
||||
|
||||
afterAll(setup.afterAll)
|
||||
|
||||
beforeEach(async () => {
|
||||
app = await config.init()
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe("/ping", () => {
|
||||
it("should ping from builder", async () => {
|
||||
await request
|
||||
.post("/api/bbtel/ping")
|
||||
.send({source: "builder", timezone})
|
||||
.set(config.defaultHeaders())
|
||||
.expect(200)
|
||||
|
||||
expect(events.serve.servedBuilder).toBeCalledTimes(1)
|
||||
expect(events.serve.servedBuilder).toBeCalledWith(timezone)
|
||||
expect(events.serve.servedApp).not.toBeCalled()
|
||||
expect(events.serve.servedAppPreview).not.toBeCalled()
|
||||
})
|
||||
|
||||
it("should ping from app preview", async () => {
|
||||
await request
|
||||
.post("/api/bbtel/ping")
|
||||
.send({source: "app", timezone})
|
||||
.set(config.defaultHeaders())
|
||||
.expect(200)
|
||||
|
||||
expect(events.serve.servedAppPreview).toBeCalledTimes(1)
|
||||
expect(events.serve.servedAppPreview).toBeCalledWith(config.getApp(), timezone)
|
||||
expect(events.serve.servedApp).not.toBeCalled()
|
||||
})
|
||||
|
||||
it("should ping from app", async () => {
|
||||
const headers = config.defaultHeaders()
|
||||
headers[constants.Headers.APP_ID] = config.prodAppId
|
||||
|
||||
await request
|
||||
.post("/api/bbtel/ping")
|
||||
.send({source: "app", timezone})
|
||||
.set(headers)
|
||||
.expect(200)
|
||||
|
||||
expect(events.serve.servedApp).toBeCalledTimes(1)
|
||||
expect(events.serve.servedApp).toBeCalledWith(config.getProdApp(), timezone)
|
||||
expect(events.serve.servedAppPreview).not.toBeCalled()
|
||||
})
|
||||
})
|
||||
})
|
|
@ -36,7 +36,6 @@ describe("/static", () => {
|
|||
.expect(200)
|
||||
|
||||
expect(res.text).toContain("<title>Budibase</title>")
|
||||
expect(events.serve.servedBuilder).toBeCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -56,9 +55,6 @@ describe("/static", () => {
|
|||
.expect(200)
|
||||
|
||||
expect(res.body.appId).toBe(config.prodAppId)
|
||||
expect(events.serve.servedApp).toBeCalledTimes(1)
|
||||
expect(events.serve.servedApp).toBeCalledWith(res.body)
|
||||
expect(events.serve.servedAppPreview).not.toBeCalled()
|
||||
})
|
||||
|
||||
it("should serve the app by url", async () => {
|
||||
|
@ -71,9 +67,6 @@ describe("/static", () => {
|
|||
.expect(200)
|
||||
|
||||
expect(res.body.appId).toBe(config.prodAppId)
|
||||
expect(events.serve.servedApp).toBeCalledTimes(1)
|
||||
expect(events.serve.servedApp).toBeCalledWith(res.body)
|
||||
expect(events.serve.servedAppPreview).not.toBeCalled()
|
||||
})
|
||||
|
||||
it("should serve the app preview by id", async () => {
|
||||
|
@ -83,9 +76,6 @@ describe("/static", () => {
|
|||
.expect(200)
|
||||
|
||||
expect(res.body.appId).toBe(config.appId)
|
||||
expect(events.serve.servedAppPreview).toBeCalledTimes(1)
|
||||
expect(events.serve.servedAppPreview).toBeCalledWith(res.body)
|
||||
expect(events.serve.servedApp).not.toBeCalled()
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ const newid = require("../../db/newid")
|
|||
const context = require("@budibase/backend-core/context")
|
||||
const { generateDevInfoID, SEPARATOR } = require("@budibase/backend-core/db")
|
||||
const { encrypt } = require("@budibase/backend-core/encryption")
|
||||
const { DocumentTypes } = require("../../db/utils")
|
||||
|
||||
const GLOBAL_USER_ID = "us_uuid1"
|
||||
const EMAIL = "babs@babs.com"
|
||||
|
@ -53,6 +54,10 @@ class TestConfiguration {
|
|||
return this.app
|
||||
}
|
||||
|
||||
getProdApp() {
|
||||
return this.prodApp
|
||||
}
|
||||
|
||||
getAppId() {
|
||||
return this.appId
|
||||
}
|
||||
|
@ -106,19 +111,11 @@ class TestConfiguration {
|
|||
|
||||
// UTILS
|
||||
|
||||
async _req(body, params, controlFunc, opts = { prodApp: false }) {
|
||||
async _req(body, params, controlFunc) {
|
||||
// create a fake request ctx
|
||||
const request = {}
|
||||
|
||||
// set the app id
|
||||
let appId
|
||||
if (opts.prodApp) {
|
||||
appId = this.prodAppId
|
||||
} else {
|
||||
appId = this.appId
|
||||
}
|
||||
const appId = this.appId
|
||||
request.appId = appId
|
||||
|
||||
// fake cookies, we don't need them
|
||||
request.cookies = { set: () => {}, get: () => {} }
|
||||
request.config = { jwtSecret: env.JWT_SECRET }
|
||||
|
@ -344,14 +341,10 @@ class TestConfiguration {
|
|||
await this._req(null, null, controllers.deploy.deployApp)
|
||||
const prodAppId = this.getAppId().replace("_dev", "")
|
||||
this.prodAppId = prodAppId
|
||||
|
||||
return context.doInAppContext(prodAppId, async () => {
|
||||
const appPackage = await this._req(
|
||||
null,
|
||||
{ appId: prodAppId },
|
||||
controllers.app.fetchAppPackage,
|
||||
{ prodApp: true }
|
||||
)
|
||||
return appPackage.application
|
||||
const db = context.getProdAppDB()
|
||||
return await db.get(DocumentTypes.APP_METADATA)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export * from "./web"
|
|
@ -0,0 +1,9 @@
|
|||
export enum PingSource {
|
||||
BUILDER = "builder",
|
||||
APP = "app",
|
||||
}
|
||||
|
||||
export interface AnalyticsPingRequest {
|
||||
source: PingSource
|
||||
timezone: string
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export * from "./analytics"
|
|
@ -2,3 +2,4 @@ export * from "./documents"
|
|||
export * from "./sdk/events"
|
||||
export * from "./sdk/licensing"
|
||||
export * from "./sdk"
|
||||
export * from "./api"
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import { BaseEvent } from "./event"
|
||||
|
||||
export interface BuilderServedEvent extends BaseEvent {}
|
||||
export interface BuilderServedEvent extends BaseEvent {
|
||||
timezone: string
|
||||
}
|
||||
|
||||
export interface AppServedEvent extends BaseEvent {
|
||||
appVersion: string
|
||||
timezone: string
|
||||
}
|
||||
|
||||
export interface AppPreviewServedEvent extends BaseEvent {
|
||||
appVersion: string
|
||||
timezone: string
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue