From 8aad7eb49d7578edef1c155ca8b5f7c23c73d12a Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Mon, 28 Sep 2020 10:47:18 +0100 Subject: [PATCH 01/12] 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 69292c353c46b2c4b3a839ed7d537ebcd242f02f Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Tue, 29 Sep 2020 15:26:56 +0100 Subject: [PATCH 02/12] 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 f2a7ec83d792d9c7141e1c409bb4b832e041442f Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Tue, 29 Sep 2020 15:35:51 +0100 Subject: [PATCH 03/12] 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 838904d14e2762b56d1523c03bd848db95454678 Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Tue, 29 Sep 2020 16:23:34 +0100 Subject: [PATCH 04/12] 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 a1db3bfecf5af0861104aa3b122d9603cc8de4e3 Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Tue, 29 Sep 2020 16:35:47 +0100 Subject: [PATCH 05/12] 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 e4ea9e608dd1b994733d054d766c747098bda70d Mon Sep 17 00:00:00 2001 From: Michael Shanks Date: Tue, 29 Sep 2020 17:28:24 +0100 Subject: [PATCH 06/12] 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") } From a35c4bca2af18471db690127544cb84a5e094a9d Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Wed, 30 Sep 2020 11:57:42 +0100 Subject: [PATCH 07/12] fix select elements --- .../DataTable/popovers/Calculate.svelte | 4 +- .../database/DataTable/popovers/Filter.svelte | 3 + .../DataTable/popovers/GroupBy.svelte | 2 +- .../src/components/settings/UserRow.svelte | 1 + .../src/components/settings/tabs/Users.svelte | 1 + .../src/components/start/Steps/User.svelte | 1 + .../EventsEditor/HandlerSelector.svelte | 139 ------------------ 7 files changed, 9 insertions(+), 142 deletions(-) delete mode 100644 packages/builder/src/components/userInterface/EventsEditor/HandlerSelector.svelte diff --git a/packages/builder/src/components/database/DataTable/popovers/Calculate.svelte b/packages/builder/src/components/database/DataTable/popovers/Calculate.svelte index e86e7fc922..a736ff61e8 100644 --- a/packages/builder/src/components/database/DataTable/popovers/Calculate.svelte +++ b/packages/builder/src/components/database/DataTable/popovers/Calculate.svelte @@ -52,14 +52,14 @@

The

of

+ {#each CONJUNCTIONS as conjunction} {/each} {/if} - {#each fields as field} {/each} diff --git a/packages/builder/src/components/settings/UserRow.svelte b/packages/builder/src/components/settings/UserRow.svelte index 88477f02a8..43ffc33fe2 100644 --- a/packages/builder/src/components/settings/UserRow.svelte +++ b/packages/builder/src/components/settings/UserRow.svelte @@ -15,6 +15,7 @@ name="Name" placeholder="Username" /> diff --git a/packages/builder/src/components/settings/tabs/Users.svelte b/packages/builder/src/components/settings/tabs/Users.svelte index 2b92e08445..aced8bc0a2 100644 --- a/packages/builder/src/components/settings/tabs/Users.svelte +++ b/packages/builder/src/components/settings/tabs/Users.svelte @@ -62,6 +62,7 @@ name="Password" placeholder="Password" /> diff --git a/packages/builder/src/components/start/Steps/User.svelte b/packages/builder/src/components/start/Steps/User.svelte index 90f4045cb4..5b49ed67dd 100644 --- a/packages/builder/src/components/start/Steps/User.svelte +++ b/packages/builder/src/components/start/Steps/User.svelte @@ -22,6 +22,7 @@ type="pasword" error={blurred.password && validationErrors.password} /> diff --git a/packages/builder/src/components/userInterface/EventsEditor/HandlerSelector.svelte b/packages/builder/src/components/userInterface/EventsEditor/HandlerSelector.svelte deleted file mode 100644 index 2876973219..0000000000 --- a/packages/builder/src/components/userInterface/EventsEditor/HandlerSelector.svelte +++ /dev/null @@ -1,139 +0,0 @@ - - -
-
-
- Action - -
- {#if parameters} -
- {#each parameters as parameter, idx} - - {/each} - {/if} - {#if parameters.length > 0} -
- {#if newHandler} - - {:else} - - {/if} -
- {/if} -
-
- - From 19a391f349ec1ddeaeb22704f76756103afd9aa0 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Wed, 30 Sep 2020 15:20:26 +0100 Subject: [PATCH 08/12] user creation CI --- packages/builder/src/components/start/Steps/User.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/builder/src/components/start/Steps/User.svelte b/packages/builder/src/components/start/Steps/User.svelte index 5b49ed67dd..90f4045cb4 100644 --- a/packages/builder/src/components/start/Steps/User.svelte +++ b/packages/builder/src/components/start/Steps/User.svelte @@ -22,7 +22,6 @@ type="pasword" error={blurred.password && validationErrors.password} /> From 18a56b029041f86091e693a6713ffdfddad9aeda Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Wed, 30 Sep 2020 16:53:11 +0100 Subject: [PATCH 09/12] fixing export styling --- .../src/components/database/DataTable/popovers/Export.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/components/database/DataTable/popovers/Export.svelte b/packages/builder/src/components/database/DataTable/popovers/Export.svelte index b8f8847a0a..00514edb9c 100644 --- a/packages/builder/src/components/database/DataTable/popovers/Export.svelte +++ b/packages/builder/src/components/database/DataTable/popovers/Export.svelte @@ -47,6 +47,7 @@
Export Format