From 1019078f793607f373618f532f879a14d3f5e285 Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Mon, 28 Sep 2020 10:47:18 +0100 Subject: [PATCH 1/6] Fetching analytics userId, when api_key entered --- packages/builder/src/analytics.js | 27 +++++++++++++++++++ .../components/settings/tabs/APIKeys.svelte | 12 ++++++++- .../components/start/CreateAppModal.svelte | 22 ++++++++++++++- packages/builder/src/pages/index.svelte | 2 ++ 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/packages/builder/src/analytics.js b/packages/builder/src/analytics.js index 43b51eb5fb..fc79589e9c 100644 --- a/packages/builder/src/analytics.js +++ b/packages/builder/src/analytics.js @@ -1,5 +1,6 @@ import * as Sentry from "@sentry/browser" import posthog from "posthog-js" +import api from "builderStore/api" function activate() { Sentry.init({ dsn: process.env.SENTRY_DSN }) @@ -9,6 +10,30 @@ function activate() { }) } +function identify(id) { + if (!id) return + posthog.identify(id) + Sentry.configureScope(scope => { + scope.setUser({ id: id }) + }) +} + +async function identifyByApiKey(apiKey) { + const response = await fetch( + `https://03gaine137.execute-api.eu-west-1.amazonaws.com/prod/account/id?api_key=${apiKey.trim()}` + ) + + if (response.status === 200) { + const id = await response.json() + + await api.put("/api/keys/userId", { value: id }) + identify(id) + return true + } + + return false +} + function captureException(err) { Sentry.captureException(err) } @@ -20,6 +45,8 @@ function captureEvent(event) { export default { activate, + identify, + identifyByApiKey, captureException, captureEvent, } diff --git a/packages/builder/src/components/settings/tabs/APIKeys.svelte b/packages/builder/src/components/settings/tabs/APIKeys.svelte index 508eeb3696..6710c0810e 100644 --- a/packages/builder/src/components/settings/tabs/APIKeys.svelte +++ b/packages/builder/src/components/settings/tabs/APIKeys.svelte @@ -3,13 +3,21 @@ import { store } from "builderStore" import api from "builderStore/api" import posthog from "posthog-js" + import analytics from "../../../analytics" let keys = { budibase: "", sendGrid: "" } async function updateKey([key, value]) { + if (key === "budibase") { + const isValid = await analytics.identifyByApiKey(value) + if (!isValid) { + // TODO: add validation message + keys = { ...keys } + return + } + } const response = await api.put(`/api/keys/${key}`, { value }) const res = await response.json() - if (key === "budibase") posthog.identify(value) keys = { ...keys, ...res } } @@ -17,6 +25,8 @@ async function fetchKeys() { const response = await api.get(`/api/keys/`) const res = await response.json() + // dont want this to ever be editable, as its fetched based on Api Key + if (res.userId) delete res.userId keys = res } diff --git a/packages/builder/src/components/start/CreateAppModal.svelte b/packages/builder/src/components/start/CreateAppModal.svelte index dbd0eaadb2..20fd48bdb9 100644 --- a/packages/builder/src/components/start/CreateAppModal.svelte +++ b/packages/builder/src/components/start/CreateAppModal.svelte @@ -22,12 +22,31 @@ export let hasKey + let isApiKeyValid + let lastApiKey + let fetchApiKeyPromise + const validateApiKey = async apiKey => { + if (!apiKey) return false + + // make sure we only fetch once + if (isApiKeyValid === undefined || apiKey !== lastApiKey) { + if (!fetchApiKeyPromise) { + fetchApiKeyPromise = analytics.identifyByApiKey(apiKey) + } + isApiKeyValid = await fetchApiKeyPromise + fetchApiKeyPromise = undefined + } + return isApiKeyValid + } + let submitting = false let errors = {} let validationErrors = {} let validationSchemas = [ { - apiKey: string().required("Please enter your API key."), + apiKey: string() + .required("Please enter your API key.") + .test("valid-apikey", "This API key is invalid", validateApiKey), }, { applicationName: string().required("Your application must have a name."), @@ -160,6 +179,7 @@ } function extractErrors({ inner }) { + if (!inner) return {} return inner.reduce((acc, err) => { return { ...acc, [err.path]: err.message } }, {}) diff --git a/packages/builder/src/pages/index.svelte b/packages/builder/src/pages/index.svelte index 102b3e7b65..82bb083e60 100644 --- a/packages/builder/src/pages/index.svelte +++ b/packages/builder/src/pages/index.svelte @@ -9,6 +9,7 @@ import Spinner from "components/common/Spinner.svelte" import CreateAppModal from "components/start/CreateAppModal.svelte" import { Button } from "@budibase/bbui" + import analytics from "../analytics" let promise = getApps() @@ -36,6 +37,7 @@ const apps = await getApps() if (key) { hasKey = true + analytics.identify(key.userId) } else { showCreateAppModal() } From 83261aeadfd9e6e411f73b07b9780810f6cfeddc Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Tue, 29 Sep 2020 15:26:56 +0100 Subject: [PATCH 2/6] analytics - identify user + extra actions added --- packages/builder/package.json | 2 +- packages/builder/rollup.config.js | 4 ++++ packages/builder/src/analytics.js | 14 +++++++++++--- packages/builder/src/builderStore/index.js | 7 +++---- packages/builder/src/builderStore/store/index.js | 5 ++++- .../AutomationList/CreateAutomationModal.svelte | 2 ++ .../BlockList/AutomationBlock.svelte | 4 ++++ .../database/DataTable/popovers/Calculate.svelte | 2 ++ .../database/DataTable/popovers/Filter.svelte | 2 ++ .../database/DataTable/popovers/View.svelte | 2 ++ .../nav/ModelNavigator/CreateTable.svelte | 2 ++ .../src/components/settings/tabs/APIKeys.svelte | 2 +- .../src/components/start/CreateAppModal.svelte | 4 ++-- .../src/pages/[application]/deploy/index.svelte | 7 +++++-- packages/builder/src/pages/index.svelte | 11 +++++------ packages/builder/yarn.lock | 7 ++++--- packages/server/src/api/controllers/apikeys.js | 1 + 17 files changed, 55 insertions(+), 23 deletions(-) diff --git a/packages/builder/package.json b/packages/builder/package.json index d46504f918..6f2cdd569a 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -75,7 +75,7 @@ "fast-sort": "^2.2.0", "lodash": "^4.17.13", "mustache": "^4.0.1", - "posthog-js": "1.3.1", + "posthog-js": "1.4.5", "shortid": "^2.2.15", "svelte-loading-spinners": "^0.1.1", "svelte-portal": "^0.1.0", diff --git a/packages/builder/rollup.config.js b/packages/builder/rollup.config.js index ffeacf9e52..af51739200 100644 --- a/packages/builder/rollup.config.js +++ b/packages/builder/rollup.config.js @@ -158,6 +158,10 @@ export default { find: "constants", replacement: path.resolve(projectRootDir, "src/constants"), }, + { + find: "analytics", + replacement: path.resolve(projectRootDir, "src/analytics"), + }, ], customResolver, }), diff --git a/packages/builder/src/analytics.js b/packages/builder/src/analytics.js index fc79589e9c..e51bb79875 100644 --- a/packages/builder/src/analytics.js +++ b/packages/builder/src/analytics.js @@ -2,15 +2,20 @@ import * as Sentry from "@sentry/browser" import posthog from "posthog-js" import api from "builderStore/api" +const analyticsEnabled = process.env.NODE_ENV === "production" + function activate() { + if (!analyticsEnabled) return Sentry.init({ dsn: process.env.SENTRY_DSN }) if (!process.env.POSTHOG_TOKEN) return posthog.init(process.env.POSTHOG_TOKEN, { api_host: process.env.POSTHOG_URL, }) + posthog.set_config({ persistence: "cookie" }) } function identify(id) { + if (!analyticsEnabled) return if (!id) return posthog.identify(id) Sentry.configureScope(scope => { @@ -19,6 +24,7 @@ function identify(id) { } async function identifyByApiKey(apiKey) { + if (!analyticsEnabled) return true const response = await fetch( `https://03gaine137.execute-api.eu-west-1.amazonaws.com/prod/account/id?api_key=${apiKey.trim()}` ) @@ -35,12 +41,14 @@ async function identifyByApiKey(apiKey) { } function captureException(err) { + if (!analyticsEnabled) return Sentry.captureException(err) } -function captureEvent(event) { - if (!process.env.POSTHOG_TOKEN) return - posthog.capture(event) +function captureEvent(eventName, props = {}) { + if (!analyticsEnabled || !process.env.POSTHOG_TOKEN) return + props.sourceApp = "builder" + posthog.capture(eventName, props) } export default { diff --git a/packages/builder/src/builderStore/index.js b/packages/builder/src/builderStore/index.js index fff862703e..c040403592 100644 --- a/packages/builder/src/builderStore/index.js +++ b/packages/builder/src/builderStore/index.js @@ -1,7 +1,7 @@ import { getStore } from "./store" import { getBackendUiStore } from "./store/backend" import { getAutomationStore } from "./store/automation/" -import analytics from "../analytics" +import analytics from "analytics" export const store = getStore() export const backendUiStore = getBackendUiStore() @@ -9,9 +9,8 @@ export const automationStore = getAutomationStore() export const initialise = async () => { try { - if (process.env.NODE_ENV === "production") { - analytics.activate() - } + analytics.activate() + analytics.captureEvent("Builder Started") } catch (err) { console.log(err) } diff --git a/packages/builder/src/builderStore/store/index.js b/packages/builder/src/builderStore/store/index.js index b64bf78624..70b88eb778 100644 --- a/packages/builder/src/builderStore/store/index.js +++ b/packages/builder/src/builderStore/store/index.js @@ -14,6 +14,7 @@ import { fetchComponentLibDefinitions } from "../loadComponentLibraries" import { buildCodeForScreens } from "../buildCodeForScreens" import { generate_screen_css } from "../generate_css" import { insertCodeMetadata } from "../insertCodeMetadata" +import analytics from "analytics" import { uuid } from "../uuid" import { selectComponent as _selectComponent, @@ -308,7 +309,9 @@ const addChildComponent = store => (componentToAdd, presetProps = {}) => { state.currentView = "component" state.currentComponentInfo = newComponent.props - + analytics.captureEvent("Added Component", { + name: newComponent.props._component, + }) return state }) } diff --git a/packages/builder/src/components/automation/AutomationPanel/AutomationList/CreateAutomationModal.svelte b/packages/builder/src/components/automation/AutomationPanel/AutomationList/CreateAutomationModal.svelte index 6f07c97f4d..fe50896279 100644 --- a/packages/builder/src/components/automation/AutomationPanel/AutomationList/CreateAutomationModal.svelte +++ b/packages/builder/src/components/automation/AutomationPanel/AutomationList/CreateAutomationModal.svelte @@ -3,6 +3,7 @@ import { notifier } from "builderStore/store/notifications" import ActionButton from "components/common/ActionButton.svelte" import { Input } from "@budibase/bbui" + import analytics from "analytics" export let onClosed @@ -19,6 +20,7 @@ }) onClosed() notifier.success(`Automation ${name} created.`) + analytics.captureEvent("Automation Created", { name }) } diff --git a/packages/builder/src/components/automation/AutomationPanel/BlockList/AutomationBlock.svelte b/packages/builder/src/components/automation/AutomationPanel/BlockList/AutomationBlock.svelte index 884a109de5..b8ac6638ae 100644 --- a/packages/builder/src/components/automation/AutomationPanel/BlockList/AutomationBlock.svelte +++ b/packages/builder/src/components/automation/AutomationPanel/BlockList/AutomationBlock.svelte @@ -1,5 +1,6 @@ diff --git a/packages/builder/src/components/database/DataTable/popovers/Calculate.svelte b/packages/builder/src/components/database/DataTable/popovers/Calculate.svelte index f43fec7ccb..6cba46ed11 100644 --- a/packages/builder/src/components/database/DataTable/popovers/Calculate.svelte +++ b/packages/builder/src/components/database/DataTable/popovers/Calculate.svelte @@ -10,6 +10,7 @@ import { backendUiStore } from "builderStore" import { notifier } from "builderStore/store/notifications" import CreateEditRecord from "../modals/CreateEditRecord.svelte" + import analytics from "analytics" const CALCULATIONS = [ { @@ -35,6 +36,7 @@ function saveView() { backendUiStore.actions.views.save(view) notifier.success(`View ${view.name} saved.`) + analytics.captureEvent("Added View Calculate") dropdown.hide() } diff --git a/packages/builder/src/components/database/DataTable/popovers/Filter.svelte b/packages/builder/src/components/database/DataTable/popovers/Filter.svelte index be40a66291..f11bef7dcb 100644 --- a/packages/builder/src/components/database/DataTable/popovers/Filter.svelte +++ b/packages/builder/src/components/database/DataTable/popovers/Filter.svelte @@ -10,6 +10,7 @@ import { backendUiStore } from "builderStore" import { notifier } from "builderStore/store/notifications" import CreateEditRecord from "../modals/CreateEditRecord.svelte" + import analytics from "analytics" const CONDITIONS = [ { @@ -63,6 +64,7 @@ backendUiStore.actions.views.save(view) notifier.success(`View ${view.name} saved.`) dropdown.hide() + analytics.captureEvent("Added View Filter") } function removeFilter(idx) { diff --git a/packages/builder/src/components/database/DataTable/popovers/View.svelte b/packages/builder/src/components/database/DataTable/popovers/View.svelte index 052c9fdf77..dcd4db51fe 100644 --- a/packages/builder/src/components/database/DataTable/popovers/View.svelte +++ b/packages/builder/src/components/database/DataTable/popovers/View.svelte @@ -11,6 +11,7 @@ import { backendUiStore } from "builderStore" import { notifier } from "builderStore/store/notifications" import CreateEditRecord from "../modals/CreateEditRecord.svelte" + import analytics from "analytics" let anchor let dropdown @@ -37,6 +38,7 @@ }) notifier.success(`View ${name} created`) dropdown.hide() + analytics.captureEvent("View Created", { name }) $goto(`../../../view/${name}`) } diff --git a/packages/builder/src/components/nav/ModelNavigator/CreateTable.svelte b/packages/builder/src/components/nav/ModelNavigator/CreateTable.svelte index 79612ea3ff..3c05b0de00 100644 --- a/packages/builder/src/components/nav/ModelNavigator/CreateTable.svelte +++ b/packages/builder/src/components/nav/ModelNavigator/CreateTable.svelte @@ -3,6 +3,7 @@ import { backendUiStore } from "builderStore" import { notifier } from "builderStore/store/notifications" import { DropdownMenu, Button, Icon, Input, Select } from "@budibase/bbui" + import analytics from "analytics" export let table @@ -19,6 +20,7 @@ $goto(`./model/${model._id}`) name = "" dropdown.hide() + analytics.captureEvent("Table Created", { name }) } const onClosed = () => { diff --git a/packages/builder/src/components/settings/tabs/APIKeys.svelte b/packages/builder/src/components/settings/tabs/APIKeys.svelte index 6710c0810e..c99a867e33 100644 --- a/packages/builder/src/components/settings/tabs/APIKeys.svelte +++ b/packages/builder/src/components/settings/tabs/APIKeys.svelte @@ -3,7 +3,7 @@ import { store } from "builderStore" import api from "builderStore/api" import posthog from "posthog-js" - import analytics from "../../../analytics" + import analytics from "analytics" let keys = { budibase: "", sendGrid: "" } diff --git a/packages/builder/src/components/start/CreateAppModal.svelte b/packages/builder/src/components/start/CreateAppModal.svelte index 20fd48bdb9..fee827b9d4 100644 --- a/packages/builder/src/components/start/CreateAppModal.svelte +++ b/packages/builder/src/components/start/CreateAppModal.svelte @@ -14,7 +14,7 @@ import { getContext } from "svelte" import { fade } from "svelte/transition" import { post } from "builderStore/api" - import analytics from "../../analytics" + import analytics from "analytics" const { open, close } = getContext("simple-modal") //Move this to context="module" once svelte-forms is updated so that it can bind to stores correctly @@ -141,7 +141,7 @@ name: $createAppStore.values.applicationName, }) const appJson = await appResp.json() - analytics.captureEvent("web_app_created", { + analytics.captureEvent("App Created", { name, appId: appJson._id, }) diff --git a/packages/builder/src/pages/[application]/deploy/index.svelte b/packages/builder/src/pages/[application]/deploy/index.svelte index 209ad4726b..de649a8dd2 100644 --- a/packages/builder/src/pages/[application]/deploy/index.svelte +++ b/packages/builder/src/pages/[application]/deploy/index.svelte @@ -4,7 +4,7 @@ import { notifier } from "builderStore/store/notifications" import api from "builderStore/api" import Spinner from "components/common/Spinner.svelte" - import analytics from "../../../analytics" + import analytics from "analytics" let deployed = false let loading = false @@ -26,10 +26,13 @@ notifier.success(`Your Deployment is Complete.`) deployed = true loading = false - analytics.captureEvent("web_app_deployment", { + analytics.captureEvent("Deployed App", { appId, }) } catch (err) { + analytics.captureEvent("Deploy App Failed", { + appId, + }) analytics.captureException(err) notifier.danger("Deployment unsuccessful. Please try again later.") loading = false diff --git a/packages/builder/src/pages/index.svelte b/packages/builder/src/pages/index.svelte index 82bb083e60..d0c471d30e 100644 --- a/packages/builder/src/pages/index.svelte +++ b/packages/builder/src/pages/index.svelte @@ -9,7 +9,7 @@ import Spinner from "components/common/Spinner.svelte" import CreateAppModal from "components/start/CreateAppModal.svelte" import { Button } from "@budibase/bbui" - import analytics from "../analytics" + import analytics from "analytics" let promise = getApps() @@ -28,16 +28,15 @@ async function fetchKeys() { const response = await api.get(`/api/keys/`) - const res = await response.json() - return res.budibase + return await response.json() } async function checkIfKeysAndApps() { - const key = await fetchKeys() + const keys = await fetchKeys() const apps = await getApps() - if (key) { + if (keys.userId) { hasKey = true - analytics.identify(key.userId) + analytics.identify(keys.userId) } else { showCreateAppModal() } diff --git a/packages/builder/yarn.lock b/packages/builder/yarn.lock index d2a2624160..c7ab89dcd6 100644 --- a/packages/builder/yarn.lock +++ b/packages/builder/yarn.lock @@ -4847,9 +4847,10 @@ posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" -posthog-js@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.3.1.tgz#970acec1423eaa5dba0d2603410c9c70294e16da" +posthog-js@1.4.5: + version "1.4.5" + resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.4.5.tgz#b16235afe47938bd71eaed4ede3790c8b910ed71" + integrity sha512-Rzc5/DpuX55BqwNEbZB0tLav1gEinnr5H+82cbLiMtXLADlxmCwZiEaVXcC3XOqW0x8bcAEehicx1TbpfBamzA== prelude-ls@~1.1.2: version "1.1.2" diff --git a/packages/server/src/api/controllers/apikeys.js b/packages/server/src/api/controllers/apikeys.js index 35fc29e37e..0fa2a7feda 100644 --- a/packages/server/src/api/controllers/apikeys.js +++ b/packages/server/src/api/controllers/apikeys.js @@ -8,6 +8,7 @@ exports.fetch = async function(ctx) { ctx.body = { budibase: process.env.BUDIBASE_API_KEY, sendgrid: process.env.SENDGRID_API_KEY, + userId: process.env.USERID_API_KEY, } } From c3366055293bb4af986482b792eeea358af9db33 Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Tue, 29 Sep 2020 15:35:51 +0100 Subject: [PATCH 3/6] added comment --- packages/builder/src/components/start/CreateAppModal.svelte | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/builder/src/components/start/CreateAppModal.svelte b/packages/builder/src/components/start/CreateAppModal.svelte index fee827b9d4..7f23e73230 100644 --- a/packages/builder/src/components/start/CreateAppModal.svelte +++ b/packages/builder/src/components/start/CreateAppModal.svelte @@ -28,8 +28,10 @@ const validateApiKey = async apiKey => { if (!apiKey) return false - // make sure we only fetch once + // make sure we only fetch once, unless API Key is changed if (isApiKeyValid === undefined || apiKey !== lastApiKey) { + // svelte reactivity was causing a requst to get fired mutiple times + // so, we make everything await the same promise, if one exists if (!fetchApiKeyPromise) { fetchApiKeyPromise = analytics.identifyByApiKey(apiKey) } From 701c82cb1ff4f636025625bba9ee16af67b284c2 Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Tue, 29 Sep 2020 16:23:34 +0100 Subject: [PATCH 4/6] serve determines whether analytics are enabled --- packages/builder/src/analytics.js | 11 +++++++++-- packages/server/src/api/controllers/analytics.js | 3 +++ packages/server/src/api/index.js | 4 ++++ packages/server/src/api/routes/analytics.js | 10 ++++++++++ packages/server/src/api/routes/index.js | 2 ++ 5 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 packages/server/src/api/controllers/analytics.js create mode 100644 packages/server/src/api/routes/analytics.js diff --git a/packages/builder/src/analytics.js b/packages/builder/src/analytics.js index e51bb79875..64998efd61 100644 --- a/packages/builder/src/analytics.js +++ b/packages/builder/src/analytics.js @@ -2,9 +2,16 @@ import * as Sentry from "@sentry/browser" import posthog from "posthog-js" import api from "builderStore/api" -const analyticsEnabled = process.env.NODE_ENV === "production" +let analyticsEnabled -function activate() { +async function activate() { + if (analyticsEnabled === undefined) { + // only the server knows the true NODE_ENV + // this was an issue as NODE_ENV = 'cypress' on the server, + // but 'production' on the client + const response = await api.get("/api/analytics") + analyticsEnabled = (await response.json()) === true + } if (!analyticsEnabled) return Sentry.init({ dsn: process.env.SENTRY_DSN }) if (!process.env.POSTHOG_TOKEN) return diff --git a/packages/server/src/api/controllers/analytics.js b/packages/server/src/api/controllers/analytics.js new file mode 100644 index 0000000000..827f83c8ff --- /dev/null +++ b/packages/server/src/api/controllers/analytics.js @@ -0,0 +1,3 @@ +exports.isEnabled = async function(ctx) { + ctx.body = JSON.stringify(process.env.NODE_ENV === "production") +} diff --git a/packages/server/src/api/index.js b/packages/server/src/api/index.js index b7f156fb6a..0ab10e3e4d 100644 --- a/packages/server/src/api/index.js +++ b/packages/server/src/api/index.js @@ -19,6 +19,7 @@ const { automationRoutes, accesslevelRoutes, apiKeysRoutes, + analyticsRoutes, } = require("./routes") const router = new Router() @@ -109,6 +110,9 @@ router.use(accesslevelRoutes.allowedMethods()) router.use(apiKeysRoutes.routes()) router.use(apiKeysRoutes.allowedMethods()) +router.use(analyticsRoutes.routes()) +router.use(analyticsRoutes.allowedMethods()) + router.use(staticRoutes.routes()) router.use(staticRoutes.allowedMethods()) diff --git a/packages/server/src/api/routes/analytics.js b/packages/server/src/api/routes/analytics.js new file mode 100644 index 0000000000..626e3c2994 --- /dev/null +++ b/packages/server/src/api/routes/analytics.js @@ -0,0 +1,10 @@ +const Router = require("@koa/router") +const authorized = require("../../middleware/authorized") +const { BUILDER } = require("../../utilities/accessLevels") +const controller = require("../controllers/analytics") + +const router = Router() + +router.get("/api/analytics", authorized(BUILDER), controller.isEnabled) + +module.exports = router diff --git a/packages/server/src/api/routes/index.js b/packages/server/src/api/routes/index.js index a2b8d3bb6c..0a5b0b1934 100644 --- a/packages/server/src/api/routes/index.js +++ b/packages/server/src/api/routes/index.js @@ -13,6 +13,7 @@ const automationRoutes = require("./automation") const accesslevelRoutes = require("./accesslevel") const deployRoutes = require("./deploy") const apiKeysRoutes = require("./apikeys") +const analyticsRoutes = require("./analytics") module.exports = { deployRoutes, @@ -30,4 +31,5 @@ module.exports = { automationRoutes, accesslevelRoutes, apiKeysRoutes, + analyticsRoutes, } From a6653997855d722f5fb7f105f2b5f0d2e27f51ae Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Tue, 29 Sep 2020 16:35:47 +0100 Subject: [PATCH 5/6] Extra analytics logging from code review --- packages/builder/src/analytics.js | 1 + .../components/database/DataTable/popovers/Calculate.svelte | 2 +- .../src/components/database/DataTable/popovers/Filter.svelte | 4 +++- packages/builder/src/components/start/CreateAppModal.svelte | 1 + 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/builder/src/analytics.js b/packages/builder/src/analytics.js index 64998efd61..8761d463c6 100644 --- a/packages/builder/src/analytics.js +++ b/packages/builder/src/analytics.js @@ -50,6 +50,7 @@ async function identifyByApiKey(apiKey) { function captureException(err) { if (!analyticsEnabled) return Sentry.captureException(err) + captureEvent("Error", { error: err.message ? err.message : err }) } function captureEvent(eventName, props = {}) { diff --git a/packages/builder/src/components/database/DataTable/popovers/Calculate.svelte b/packages/builder/src/components/database/DataTable/popovers/Calculate.svelte index 6cba46ed11..e86e7fc922 100644 --- a/packages/builder/src/components/database/DataTable/popovers/Calculate.svelte +++ b/packages/builder/src/components/database/DataTable/popovers/Calculate.svelte @@ -36,7 +36,7 @@ function saveView() { backendUiStore.actions.views.save(view) notifier.success(`View ${view.name} saved.`) - analytics.captureEvent("Added View Calculate") + analytics.captureEvent("Added View Calculate", { field: view.field }) dropdown.hide() } diff --git a/packages/builder/src/components/database/DataTable/popovers/Filter.svelte b/packages/builder/src/components/database/DataTable/popovers/Filter.svelte index f11bef7dcb..8f1b972538 100644 --- a/packages/builder/src/components/database/DataTable/popovers/Filter.svelte +++ b/packages/builder/src/components/database/DataTable/popovers/Filter.svelte @@ -64,7 +64,9 @@ backendUiStore.actions.views.save(view) notifier.success(`View ${view.name} saved.`) dropdown.hide() - analytics.captureEvent("Added View Filter") + analytics.captureEvent("Added View Filter", { + filters: JSON.stringify(view.filters), + }) } function removeFilter(idx) { diff --git a/packages/builder/src/components/start/CreateAppModal.svelte b/packages/builder/src/components/start/CreateAppModal.svelte index 7f23e73230..84ddd9f8ec 100644 --- a/packages/builder/src/components/start/CreateAppModal.svelte +++ b/packages/builder/src/components/start/CreateAppModal.svelte @@ -30,6 +30,7 @@ // make sure we only fetch once, unless API Key is changed if (isApiKeyValid === undefined || apiKey !== lastApiKey) { + lastApiKey = apiKey // svelte reactivity was causing a requst to get fired mutiple times // so, we make everything await the same promise, if one exists if (!fetchApiKeyPromise) { From f97369afd2a28bd0f8be05030b6f90f4219bbf34 Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Tue, 29 Sep 2020 17:28:24 +0100 Subject: [PATCH 6/6] Analytics enabled/disabled via specific ENV variable --- packages/builder/cypress/setup.js | 1 + packages/server/.env.template | 3 ++- packages/server/src/api/controllers/analytics.js | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/builder/cypress/setup.js b/packages/builder/cypress/setup.js index 1003e6e422..a6dab69583 100644 --- a/packages/builder/cypress/setup.js +++ b/packages/builder/cypress/setup.js @@ -14,6 +14,7 @@ rimraf.sync(homedir) process.env.BUDIBASE_API_KEY = "6BE826CB-6B30-4AEC-8777-2E90464633DE" process.env.NODE_ENV = "cypress" +process.env.ENABLE_ANALYTICS = "false" initialiseBudibase({ dir: homedir, clientId: "cypress-test" }) .then(() => { diff --git a/packages/server/.env.template b/packages/server/.env.template index 165e317b07..4895d0309c 100644 --- a/packages/server/.env.template +++ b/packages/server/.env.template @@ -16,4 +16,5 @@ LOG_LEVEL=error DEPLOYMENT_CREDENTIALS_URL="https://dt4mpwwap8.execute-api.eu-west-1.amazonaws.com/prod/" DEPLOYMENT_DB_URL="https://couchdb.budi.live:5984" -SENTRY_DSN=https://a34ae347621946bf8acded18e5b7d4b8@o420233.ingest.sentry.io/5338131 \ No newline at end of file +SENTRY_DSN=https://a34ae347621946bf8acded18e5b7d4b8@o420233.ingest.sentry.io/5338131 +ENABLE_ANALYTICS="true" \ No newline at end of file diff --git a/packages/server/src/api/controllers/analytics.js b/packages/server/src/api/controllers/analytics.js index 827f83c8ff..025775ac2e 100644 --- a/packages/server/src/api/controllers/analytics.js +++ b/packages/server/src/api/controllers/analytics.js @@ -1,3 +1,3 @@ exports.isEnabled = async function(ctx) { - ctx.body = JSON.stringify(process.env.NODE_ENV === "production") + ctx.body = JSON.stringify(process.env.ENABLE_ANALYTICS === "true") }