From 3e5e95a9c13ded9d8d56d10d2f91720e9e3b8720 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Fri, 25 Mar 2022 13:04:34 +0000 Subject: [PATCH 01/75] Add analytics to backend-core. Update server posthog usage --- packages/backend-core/package.json | 1 + .../backend-core/src/analytics/analytics.js | 33 +++++ .../backend-core/src/analytics/constants.js | 57 ++++++++ packages/backend-core/src/analytics/index.js | 6 + .../backend-core/src/analytics/posthog.js | 21 +++ packages/backend-core/src/environment.js | 10 ++ packages/backend-core/src/index.js | 1 + packages/backend-core/yarn.lock | 92 +++++++++++- .../builder/src/analytics/IntercomClient.js | 1 + .../builder/src/analytics/PosthogClient.js | 1 + packages/builder/src/analytics/constants.js | 1 + packages/builder/src/analytics/index.js | 1 + packages/builder/src/api.js | 2 +- .../builder/src/builderStore/datasource.js | 1 + .../builderStore/store/automation/index.js | 1 + .../src/builderStore/store/frontend.js | 1 + .../CreateAutomationModal.svelte | 1 + .../UpdateAutomationModal.svelte | 1 + .../DataTable/modals/CalculateModal.svelte | 1 + .../DataTable/modals/CreateViewModal.svelte | 1 + .../DataTable/modals/FilterModal.svelte | 1 + .../modals/ImportRestQueriesModal.svelte | 1 + .../modals/UpdateDatasourceModal.svelte | 1 + .../modals/CreateTableModal.svelte | 1 + .../src/components/deploy/DeployModal.svelte | 2 + .../ScreenDropdownMenu.svelte | 1 + .../NavigationPanel/ScreenWizard.svelte | 1 + .../components/start/CreateAppModal.svelte | 1 + .../pages/builder/portal/apps/index.svelte | 1 + .../builder/portal/manage/auth/index.svelte | 1 + .../builder/portal/manage/email/index.svelte | 1 + .../users/_components/AddUserModal.svelte | 1 + packages/builder/src/stores/portal/auth.js | 1 + packages/cli/src/analytics/Client.js | 1 + packages/cli/src/analytics/index.js | 2 + packages/cli/src/constants.js | 1 + packages/cli/src/hosting/index.js | 1 + packages/client/src/api/api.js | 1 + packages/client/yarn.lock | 135 ++++++++---------- packages/frontend-core/src/api/analytics.js | 1 + packages/server/package.json | 1 - .../server/src/api/controllers/analytics.js | 30 ++-- packages/server/src/app.ts | 2 + packages/server/yarn.lock | 79 +--------- 44 files changed, 325 insertions(+), 177 deletions(-) create mode 100644 packages/backend-core/src/analytics/analytics.js create mode 100644 packages/backend-core/src/analytics/constants.js create mode 100644 packages/backend-core/src/analytics/index.js create mode 100644 packages/backend-core/src/analytics/posthog.js diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 9c06dfe9dc..a0c2ff2db1 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -24,6 +24,7 @@ "passport-google-oauth": "^2.0.0", "passport-jwt": "^4.0.0", "passport-local": "^1.0.0", + "posthog-node": "^1.3.0", "sanitize-s3-objectkey": "^0.0.1", "tar-fs": "^2.1.1", "uuid": "^8.3.2", diff --git a/packages/backend-core/src/analytics/analytics.js b/packages/backend-core/src/analytics/analytics.js new file mode 100644 index 0000000000..820309cea9 --- /dev/null +++ b/packages/backend-core/src/analytics/analytics.js @@ -0,0 +1,33 @@ +const PosthogClient = require("./posthog") +const env = require("../environment") + +class Analytics { + constructor() { + // check enabled before init + this.isEnabled = !!(!env.SELF_HOSTED && env.ENABLE_ANALYTICS) + if (!this.isEnabled) return + + this.posthog = new PosthogClient(process.env.POSTHOG_TOKEN) + } + + enabled() { + return this.isEnabled + } + + updateUser(userId, properties) { + if (!this.isEnabled) return + this.posthog.updateUser(userId, properties) + } + + captureEvent(userId, eventName, props = {}) { + if (!this.isEnabled) return + this.posthog.capture(userId, eventName, props) + } + + shutdown() { + if (!this.isEnabled) return + this.posthog.shutdown() + } +} + +module.exports = Analytics diff --git a/packages/backend-core/src/analytics/constants.js b/packages/backend-core/src/analytics/constants.js new file mode 100644 index 0000000000..4c5b69311f --- /dev/null +++ b/packages/backend-core/src/analytics/constants.js @@ -0,0 +1,57 @@ +const Events = { + // + BUILDER: { + STARTED: "Builder Started", + }, + COMPONENT: { + CREATED: "Added Component", + }, + DATASOURCE: { + CREATED: "Datasource Created", + UPDATED: "Datasource Updated", + }, + QUERIES: { + REST: "REST Queries Imported", + }, + TABLE: { + CREATED: "Table Created", + }, + VIEW: { + CREATED: "View Created", + ADDED_FILTER: "Added View Filter", + ADDED_CALCULATE: "Added View Calculate", + }, + SCREEN: { + CREATED: "Screen Created", + }, + AUTOMATION: { + CREATED: "Automation Created", + SAVED: "Automation Saved", + BLOCK_ADDED: "Added Automation Block", + }, + NPS: { + SUBMITTED: "budibase:feedback_submitted", + }, + APP: { + CREATED: "budibase:app_created", + PUBLISHED: "budibase:app_published", + UNPUBLISHED: "budibase:app_unpublished", + }, + ANALYTICS: { + OPT_IN: "budibase:analytics_opt_in", + OPT_OUT: "budibase:analytics_opt_out", + }, + USER: { + INVITE: "budibase:portal_user_invite", + }, + SMTP: { + SAVED: "budibase:smtp_saved", + }, + SSO: { + SAVED: "budibase:sso_saved", + }, +} + +module.exports = { + Events, +} diff --git a/packages/backend-core/src/analytics/index.js b/packages/backend-core/src/analytics/index.js new file mode 100644 index 0000000000..9aa688fae0 --- /dev/null +++ b/packages/backend-core/src/analytics/index.js @@ -0,0 +1,6 @@ +const Analytics = require("./analytics") +const { Events } = require("./constants") + +module.exports = new Analytics() +// exports = module.exports +exports.Events = Events diff --git a/packages/backend-core/src/analytics/posthog.js b/packages/backend-core/src/analytics/posthog.js new file mode 100644 index 0000000000..d37f1b0cdd --- /dev/null +++ b/packages/backend-core/src/analytics/posthog.js @@ -0,0 +1,21 @@ +const PostHog = require("posthog-node") + +class PosthogClient { + constructor(token) { + this.posthog = new PostHog(token) + } + + updateUser(userId, properties) { + this.posthog.identify({ distinctId: userId, properties }) + } + + capture(userId, event, properties) { + this.posthog.capture({ distinctId: userId, event, properties }) + } + + shutdown() { + this.posthog.shutdown() + } +} + +module.exports = PosthogClient diff --git a/packages/backend-core/src/environment.js b/packages/backend-core/src/environment.js index d112ad8599..ef1ecb211e 100644 --- a/packages/backend-core/src/environment.js +++ b/packages/backend-core/src/environment.js @@ -28,9 +28,19 @@ module.exports = { SELF_HOSTED: !!parseInt(process.env.SELF_HOSTED), COOKIE_DOMAIN: process.env.COOKIE_DOMAIN, PLATFORM_URL: process.env.PLATFORM_URL, + POSTHOG_TOKEN: process.env.POSTHOG_TOKEN, + ENABLE_ANALYTICS: process.env.ENABLE_ANALYTICS, isTest, _set(key, value) { process.env[key] = value module.exports[key] = value }, } + +// clean up any environment variable edge cases +for (let [key, value] of Object.entries(module.exports)) { + // handle the edge case of "0" to disable an environment variable + if (value === "0") { + module.exports[key] = 0 + } +} diff --git a/packages/backend-core/src/index.js b/packages/backend-core/src/index.js index b0bc524d9b..757814914b 100644 --- a/packages/backend-core/src/index.js +++ b/packages/backend-core/src/index.js @@ -15,4 +15,5 @@ module.exports = { auth: require("../auth"), constants: require("../constants"), migrations: require("../migrations"), + analytics: require("./analytics"), } diff --git a/packages/backend-core/yarn.lock b/packages/backend-core/yarn.lock index fc70e3d6a1..b072b163de 100644 --- a/packages/backend-core/yarn.lock +++ b/packages/backend-core/yarn.lock @@ -258,6 +258,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" +"@babel/runtime@^7.15.4": + version "7.17.8" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.8.tgz#3e56e4aff81befa55ac3ac6a0967349fd1c5bca2" + integrity sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.16.0", "@babel/template@^7.3.3": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.0.tgz#d16a35ebf4cd74e202083356fab21dd89363ddd6" @@ -857,6 +864,21 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== +axios-retry@^3.1.9: + version "3.2.4" + resolved "https://registry.yarnpkg.com/axios-retry/-/axios-retry-3.2.4.tgz#f447a53c3456f5bfeca18f20c3a3272207d082ae" + integrity sha512-Co3UXiv4npi6lM963mfnuH90/YFLKWWDmoBYfxkHT5xtkSSWNqK9zdG3fw5/CP/dsoKB5aMMJCsgab+tp1OxLQ== + dependencies: + "@babel/runtime" "^7.15.4" + is-retry-allowed "^2.2.0" + +axios@0.24.0: + version "0.24.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" + integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== + dependencies: + follow-redirects "^1.14.4" + babel-jest@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" @@ -1139,6 +1161,11 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +charenc@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= + chownr@^1.1.1: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" @@ -1273,6 +1300,11 @@ component-emitter@^1.2.1: resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== +component-type@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-type/-/component-type-1.2.1.tgz#8a47901700238e4fc32269771230226f24b415a9" + integrity sha1-ikeQFwAjjk/DIml3EjAibyS0Fak= + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -1315,6 +1347,11 @@ cross-spawn@^7.0.0: shebang-command "^2.0.0" which "^2.0.1" +crypt@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= + cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" @@ -1802,6 +1839,11 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +follow-redirects@^1.14.4: + version "1.14.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" + integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -2226,7 +2268,7 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= -is-buffer@^1.1.5: +is-buffer@^1.1.5, is-buffer@~1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== @@ -2328,6 +2370,11 @@ is-potential-custom-element-name@^1.0.1: resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== +is-retry-allowed@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz#88f34cbd236e043e71b6932d09b0c65fb7b4d71d" + integrity sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg== + is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -2824,6 +2871,11 @@ jodid25519@^1.0.0: dependencies: jsbn "~0.1.0" +join-component@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/join-component/-/join-component-1.1.0.tgz#b8417b750661a392bee2c2537c68b2a9d4977cd5" + integrity sha1-uEF7dQZho5K+4sJTfGiyqdSXfNU= + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -3252,6 +3304,15 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +md5@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" + integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== + dependencies: + charenc "0.0.2" + crypt "0.0.2" + is-buffer "~1.1.6" + memdown@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/memdown/-/memdown-1.4.1.tgz#b4e4e192174664ffbae41361aa500f3119efe215" @@ -3372,6 +3433,11 @@ ms@2.1.2, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -3769,6 +3835,20 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= +posthog-node@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/posthog-node/-/posthog-node-1.3.0.tgz#804ed2f213a2f05253f798bf9569d55a9cad94f7" + integrity sha512-2+VhqiY/rKIqKIXyvemBFHbeijHE25sP7eKltnqcFqAssUE6+sX6vusN9A4luzToOqHQkUZexiCKxvuGagh7JA== + dependencies: + axios "0.24.0" + axios-retry "^3.1.9" + component-type "^1.2.1" + join-component "^1.1.0" + md5 "^2.3.0" + ms "^2.1.3" + remove-trailing-slash "^0.1.1" + uuid "^8.3.2" + pouchdb-adapter-leveldb-core@7.2.2: version "7.2.2" resolved "https://registry.yarnpkg.com/pouchdb-adapter-leveldb-core/-/pouchdb-adapter-leveldb-core-7.2.2.tgz#e0aa6a476e2607d7ae89f4a803c9fba6e6d05a8a" @@ -4068,6 +4148,11 @@ redis-parser@^3.0.0: dependencies: redis-errors "^1.0.0" +regenerator-runtime@^0.13.4: + version "0.13.9" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" + integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== + regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -4081,6 +4166,11 @@ remove-trailing-separator@^1.0.1: resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= +remove-trailing-slash@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz#be2285a59f39c74d1bce4f825950061915e3780d" + integrity sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA== + repeat-element@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" diff --git a/packages/builder/src/analytics/IntercomClient.js b/packages/builder/src/analytics/IntercomClient.js index 8cc7e35bbf..d4835d979d 100644 --- a/packages/builder/src/analytics/IntercomClient.js +++ b/packages/builder/src/analytics/IntercomClient.js @@ -3,6 +3,7 @@ export default class IntercomClient { this.token = token } + // /** * Instantiate intercom using their provided script. */ diff --git a/packages/builder/src/analytics/PosthogClient.js b/packages/builder/src/analytics/PosthogClient.js index ab06fa6413..8252737325 100644 --- a/packages/builder/src/analytics/PosthogClient.js +++ b/packages/builder/src/analytics/PosthogClient.js @@ -1,6 +1,7 @@ import posthog from "posthog-js" import { Events } from "./constants" +// export default class PosthogClient { constructor(token, url) { this.token = token diff --git a/packages/builder/src/analytics/constants.js b/packages/builder/src/analytics/constants.js index 177d5320a5..8d6e94acbb 100644 --- a/packages/builder/src/analytics/constants.js +++ b/packages/builder/src/analytics/constants.js @@ -1,4 +1,5 @@ export const Events = { + // BUILDER: { STARTED: "Builder Started", }, diff --git a/packages/builder/src/analytics/index.js b/packages/builder/src/analytics/index.js index 3a4118347d..be77dae8f9 100644 --- a/packages/builder/src/analytics/index.js +++ b/packages/builder/src/analytics/index.js @@ -11,6 +11,7 @@ const posthog = new PosthogClient( const sentry = new SentryClient(process.env.SENTRY_DSN) const intercom = new IntercomClient(process.env.INTERCOM_TOKEN) +// class AnalyticsHub { constructor() { this.clients = [posthog, sentry, intercom] diff --git a/packages/builder/src/api.js b/packages/builder/src/api.js index 5604db5db8..808f434f1e 100644 --- a/packages/builder/src/api.js +++ b/packages/builder/src/api.js @@ -23,8 +23,8 @@ export const API = createAPIClient({ const { url, message, status, method, handled } = error || {} // Log all API errors to Sentry + // todo: move to api // analytics.captureException(error) - // Log any errors that we haven't manually handled if (!handled) { console.error("Unhandled error from API client", error) diff --git a/packages/builder/src/builderStore/datasource.js b/packages/builder/src/builderStore/datasource.js index cfdeeac23e..cae0116d54 100644 --- a/packages/builder/src/builderStore/datasource.js +++ b/packages/builder/src/builderStore/datasource.js @@ -31,6 +31,7 @@ export async function saveDatasource(config, skipFetch = false) { // update the tables incase data source plus await tables.fetch() await datasources.select(resp._id) + // todo: move to api analytics.captureEvent(Events.DATASOURCE.CREATED, { name: resp.name, source: resp.source, diff --git a/packages/builder/src/builderStore/store/automation/index.js b/packages/builder/src/builderStore/store/automation/index.js index 84e6033439..eac99335c9 100644 --- a/packages/builder/src/builderStore/store/automation/index.js +++ b/packages/builder/src/builderStore/store/automation/index.js @@ -127,6 +127,7 @@ const automationActions = store => ({ state.selectedBlock = newBlock return state }) + // todo: move to api analytics.captureEvent(Events.AUTOMATION.BLOCK_ADDED, { name: block.name, }) diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index fd29414e3c..3d824767b5 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -425,6 +425,7 @@ export const getFrontendStore = () => { }) // Log event + // todo: move to api analytics.captureEvent(Events.COMPONENT.CREATED, { name: componentInstance._component, }) diff --git a/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte b/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte index 4fb912939a..abf43a4691 100644 --- a/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte +++ b/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte @@ -47,6 +47,7 @@ notifications.success(`Automation ${name} created`) $goto(`./${$automationStore.selectedAutomation.automation._id}`) + // todo: move to api analytics.captureEvent(Events.AUTOMATION.CREATED, { name }) } catch (error) { notifications.error("Error creating automation") diff --git a/packages/builder/src/components/automation/AutomationPanel/UpdateAutomationModal.svelte b/packages/builder/src/components/automation/AutomationPanel/UpdateAutomationModal.svelte index bceff0ef2e..16e981657c 100644 --- a/packages/builder/src/components/automation/AutomationPanel/UpdateAutomationModal.svelte +++ b/packages/builder/src/components/automation/AutomationPanel/UpdateAutomationModal.svelte @@ -27,6 +27,7 @@ } await automationStore.actions.save(updatedAutomation) notifications.success(`Automation ${name} updated successfully`) + // todo: move to api analytics.captureEvent(Events.AUTOMATION.SAVED, { name }) hide() } catch (error) { diff --git a/packages/builder/src/components/backend/DataTable/modals/CalculateModal.svelte b/packages/builder/src/components/backend/DataTable/modals/CalculateModal.svelte index 81f54032f6..852b91a7a0 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CalculateModal.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CalculateModal.svelte @@ -41,6 +41,7 @@ try { views.save(view) notifications.success(`View ${view.name} saved`) + // todo: move to api analytics.captureEvent(Events.VIEW.ADDED_CALCULATE, { field: view.field }) } catch (error) { notifications.error("Error saving view") diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateViewModal.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateViewModal.svelte index 2ea0c1b63b..9ce7a283bf 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateViewModal.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateViewModal.svelte @@ -22,6 +22,7 @@ field, }) notifications.success(`View ${name} created`) + // todo: move to api analytics.captureEvent(Events.VIEW.CREATED, { name }) $goto(`../../view/${name}`) } catch (error) { diff --git a/packages/builder/src/components/backend/DataTable/modals/FilterModal.svelte b/packages/builder/src/components/backend/DataTable/modals/FilterModal.svelte index d7976cd387..2bfd6f0726 100644 --- a/packages/builder/src/components/backend/DataTable/modals/FilterModal.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/FilterModal.svelte @@ -75,6 +75,7 @@ try { views.save(view) notifications.success(`View ${view.name} saved`) + // todo: move to api analytics.captureEvent(Events.VIEW.ADDED_FILTER, { filters: JSON.stringify(view.filters), }) diff --git a/packages/builder/src/components/backend/DatasourceNavigator/modals/ImportRestQueriesModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/modals/ImportRestQueriesModal.svelte index cbf4274690..292325edfa 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/modals/ImportRestQueriesModal.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/modals/ImportRestQueriesModal.svelte @@ -72,6 +72,7 @@ } notifications.success(`Imported successfully.`) + // todo: move to api analytics.captureEvent(Events.QUERIES.REST.IMPORTED, { importType: lastTouched, newDatasource: createDatasource, diff --git a/packages/builder/src/components/backend/DatasourceNavigator/modals/UpdateDatasourceModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/modals/UpdateDatasourceModal.svelte index 28625aa86e..8dd39200d6 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/modals/UpdateDatasourceModal.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/modals/UpdateDatasourceModal.svelte @@ -35,6 +35,7 @@ } await datasources.save(updatedDatasource) notifications.success(`Datasource ${name} updated successfully.`) + // todo: move to api analytics.captureEvent(Events.DATASOURCE.UPDATED, updatedDatasource) hide() } diff --git a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte index a3b7ca81a6..556ce7d3fc 100644 --- a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte +++ b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte @@ -57,6 +57,7 @@ try { table = await tables.save(newTable) notifications.success(`Table ${name} created successfully.`) + // todo: move to api analytics.captureEvent(Events.TABLE.CREATED, { name }) // Navigate to new table diff --git a/packages/builder/src/components/deploy/DeployModal.svelte b/packages/builder/src/components/deploy/DeployModal.svelte index 9f2aae56c5..936bb6cc7e 100644 --- a/packages/builder/src/components/deploy/DeployModal.svelte +++ b/packages/builder/src/components/deploy/DeployModal.svelte @@ -10,11 +10,13 @@ async function deployApp() { try { await API.deployAppChanges() + // todo: move to api analytics.captureEvent(Events.APP.PUBLISHED, { appId: $store.appId, }) notifications.success("Application published successfully") } catch (error) { + // todo: move to api analytics.captureException(error) notifications.error("Error publishing app") } diff --git a/packages/builder/src/components/design/NavigationPanel/ComponentNavigationTree/ScreenDropdownMenu.svelte b/packages/builder/src/components/design/NavigationPanel/ComponentNavigationTree/ScreenDropdownMenu.svelte index 76bb48a26c..65f8309cda 100644 --- a/packages/builder/src/components/design/NavigationPanel/ComponentNavigationTree/ScreenDropdownMenu.svelte +++ b/packages/builder/src/components/design/NavigationPanel/ComponentNavigationTree/ScreenDropdownMenu.svelte @@ -43,6 +43,7 @@ // Analytics if (screen.template) { + // todo: move to api analytics.captureEvent(Events.SCREEN.CREATED, { template: "createFromScratch", }) diff --git a/packages/builder/src/components/design/NavigationPanel/ScreenWizard.svelte b/packages/builder/src/components/design/NavigationPanel/ScreenWizard.svelte index c30241868a..a5c1b7d5bd 100644 --- a/packages/builder/src/components/design/NavigationPanel/ScreenWizard.svelte +++ b/packages/builder/src/components/design/NavigationPanel/ScreenWizard.svelte @@ -53,6 +53,7 @@ // Analytics if (screen.template) { + // todo: move to api analytics.captureEvent(Events.SCREEN.CREATED, { template: screen.template, }) diff --git a/packages/builder/src/components/start/CreateAppModal.svelte b/packages/builder/src/components/start/CreateAppModal.svelte index 91c4807dc8..f319a14959 100644 --- a/packages/builder/src/components/start/CreateAppModal.svelte +++ b/packages/builder/src/components/start/CreateAppModal.svelte @@ -46,6 +46,7 @@ // Create App const createdApp = await API.createApp(data) + // todo: move to api analytics.captureEvent(Events.APP.CREATED, { name: $values.name, appId: createdApp.instance._id, diff --git a/packages/builder/src/pages/builder/portal/apps/index.svelte b/packages/builder/src/pages/builder/portal/apps/index.svelte index b05aa1b659..f582e80842 100644 --- a/packages/builder/src/pages/builder/portal/apps/index.svelte +++ b/packages/builder/src/pages/builder/portal/apps/index.svelte @@ -116,6 +116,7 @@ // Create App const createdApp = await API.createApp(data) + // todo: move to api analytics.captureEvent(Events.APP.CREATED, { name: appName, appId: createdApp.instance._id, diff --git a/packages/builder/src/pages/builder/portal/manage/auth/index.svelte b/packages/builder/src/pages/builder/portal/manage/auth/index.svelte index b001f02fe9..e3fdec2de3 100644 --- a/packages/builder/src/pages/builder/portal/manage/auth/index.svelte +++ b/packages/builder/src/pages/builder/portal/manage/auth/index.svelte @@ -205,6 +205,7 @@ providers[res.type]._id = res._id }) notifications.success(`Settings saved`) + // todo: move to api analytics.captureEvent(Events.SSO.SAVED) }) .catch(() => { diff --git a/packages/builder/src/pages/builder/portal/manage/email/index.svelte b/packages/builder/src/pages/builder/portal/manage/email/index.svelte index 4ef59d2daa..8c9d829d24 100644 --- a/packages/builder/src/pages/builder/portal/manage/email/index.svelte +++ b/packages/builder/src/pages/builder/portal/manage/email/index.svelte @@ -59,6 +59,7 @@ smtpConfig._rev = savedConfig._rev smtpConfig._id = savedConfig._id notifications.success(`Settings saved`) + // todo: move to api analytics.captureEvent(Events.SMTP.SAVED) } catch (error) { notifications.error( diff --git a/packages/builder/src/pages/builder/portal/manage/users/_components/AddUserModal.svelte b/packages/builder/src/pages/builder/portal/manage/users/_components/AddUserModal.svelte index 0255784a7b..6086c3b21b 100644 --- a/packages/builder/src/pages/builder/portal/manage/users/_components/AddUserModal.svelte +++ b/packages/builder/src/pages/builder/portal/manage/users/_components/AddUserModal.svelte @@ -24,6 +24,7 @@ try { const res = await users.invite({ email: $email, builder, admin }) notifications.success(res.message) + // todo: move to api analytics.captureEvent(Events.USER.INVITE, { type: selected }) } catch (error) { notifications.error("Error inviting user") diff --git a/packages/builder/src/stores/portal/auth.js b/packages/builder/src/stores/portal/auth.js index 4a90323144..b346b8d330 100644 --- a/packages/builder/src/stores/portal/auth.js +++ b/packages/builder/src/stores/portal/auth.js @@ -57,6 +57,7 @@ export function createAuthStore() { analytics .activate() .then(() => { + // todo: move to api analytics.identify(user._id, user) analytics.showChat({ email: user.email, diff --git a/packages/cli/src/analytics/Client.js b/packages/cli/src/analytics/Client.js index a3b43b5ea1..cdf345198e 100644 --- a/packages/cli/src/analytics/Client.js +++ b/packages/cli/src/analytics/Client.js @@ -6,6 +6,7 @@ const { } = require("../constants") const ConfigManager = require("../structures/ConfigManager") +// use backend core class AnalyticsClient { constructor() { this.client = new PostHog(BUDIBASE_POSTHOG_TOKEN, { diff --git a/packages/cli/src/analytics/index.js b/packages/cli/src/analytics/index.js index d85323d1d0..514cbf60fd 100644 --- a/packages/cli/src/analytics/index.js +++ b/packages/cli/src/analytics/index.js @@ -3,6 +3,8 @@ const { CommandWords } = require("../constants") const { success, error } = require("../utils") const AnalyticsClient = require("./Client") +// use backend core + const client = new AnalyticsClient() async function optOut() { diff --git a/packages/cli/src/constants.js b/packages/cli/src/constants.js index 7a3b1463ca..0fdd7560c7 100644 --- a/packages/cli/src/constants.js +++ b/packages/cli/src/constants.js @@ -9,6 +9,7 @@ exports.InitTypes = { DIGITAL_OCEAN: "do", } +// todo: use backend core exports.AnalyticsEvents = { OptOut: "analytics_opt_out", OptIn: "analytics_opt_in", diff --git a/packages/cli/src/hosting/index.js b/packages/cli/src/hosting/index.js index 2ebcee43a5..839dc7e6f2 100644 --- a/packages/cli/src/hosting/index.js +++ b/packages/cli/src/hosting/index.js @@ -15,6 +15,7 @@ const makeEnv = require("./makeEnv") const axios = require("axios") const AnalyticsClient = require("../analytics/Client") +// todo: use backend core const BUDIBASE_SERVICES = ["app-service", "worker-service", "proxy-service"] const ERROR_FILE = "docker-error.log" const FILE_URLS = [ diff --git a/packages/client/src/api/api.js b/packages/client/src/api/api.js index 591d4a6782..26e92d2018 100644 --- a/packages/client/src/api/api.js +++ b/packages/client/src/api/api.js @@ -39,6 +39,7 @@ export const API = createAPIClient({ if (message) { // Don't notify if the URL contains the word analytics as it may be // blocked by browser extensions + // todo: this can be removed now? if (!url?.includes("analytics")) { notificationStore.actions.error(message) } diff --git a/packages/client/yarn.lock b/packages/client/yarn.lock index 9ea91c7cd8..412e735854 100644 --- a/packages/client/yarn.lock +++ b/packages/client/yarn.lock @@ -462,11 +462,6 @@ domhandler@^4.2.0: dependencies: domelementtype "^2.2.0" -domino@^2.1.6: - version "2.1.6" - resolved "https://registry.yarnpkg.com/domino/-/domino-2.1.6.tgz#fe4ace4310526e5e7b9d12c7de01b7f485a57ffe" - integrity sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ== - domutils@^2.5.2, domutils@^2.6.0: version "2.8.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" @@ -501,11 +496,6 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== -entities@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" - integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== - escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -541,11 +531,6 @@ estree-walker@^2.0.1: resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== -eventemitter3@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-2.0.3.tgz#b5e1079b59fb5e1ba2771c0a993be060a58c99ba" - integrity sha1-teEHm1n7XhuidxwKmTvgYKWMmbo= - eventemitter3@^4.0.4: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -612,6 +597,16 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +htmlparser2@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + icss-replace-symbols@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" @@ -676,6 +671,11 @@ is-module@^1.0.0: resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + is-reference@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" @@ -683,14 +683,6 @@ is-reference@^1.2.1: dependencies: "@types/estree" "*" -is-regex@^1.0.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - is-resolvable@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" @@ -724,18 +716,16 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +leaflet@^1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.7.1.tgz#10d684916edfe1bf41d688a3b97127c0322a2a19" + integrity sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw== + lilconfig@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082" integrity sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA== -linkify-it@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.3.tgz#a98baf44ce45a550efb4d49c769d07524cc2fa2e" - integrity sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ== - dependencies: - uc.micro "^1.0.1" - loader-utils@^1.1.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" @@ -772,11 +762,6 @@ mdn-data@2.0.14: resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== -mdurl@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" - integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= - merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -804,6 +789,11 @@ nanoid@^3.1.30, nanoid@^3.1.32: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c" integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA== +nanoid@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" + integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== + node-releases@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" @@ -857,6 +847,11 @@ p-timeout@^3.2.0: dependencies: p-finally "^1.0.0" +parse-srcset@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1" + integrity sha1-8r0iH2zJcKk42IVWq8WJyqqiveE= + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -1159,32 +1154,20 @@ postcss@^8.2.10: picocolors "^1.0.0" source-map-js "^1.0.1" +postcss@^8.3.11: + version "8.4.12" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.12.tgz#1e7de78733b28970fa4743f7da6f3763648b1905" + integrity sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg== + dependencies: + nanoid "^3.3.1" + picocolors "^1.0.0" + source-map-js "^1.0.2" + promise.series@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/promise.series/-/promise.series-0.2.0.tgz#2cc7ebe959fc3a6619c04ab4dbdc9e452d864bbd" integrity sha1-LMfr6Vn8OmYZwEq029yeRS2GS70= -quill-delta@^3.6.2: - version "3.6.3" - resolved "https://registry.yarnpkg.com/quill-delta/-/quill-delta-3.6.3.tgz#b19fd2b89412301c60e1ff213d8d860eac0f1032" - integrity sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg== - dependencies: - deep-equal "^1.0.1" - extend "^3.0.2" - fast-diff "1.1.2" - -quill@^1.3.7: - version "1.3.7" - resolved "https://registry.yarnpkg.com/quill/-/quill-1.3.7.tgz#da5b2f3a2c470e932340cdbf3668c9f21f9286e8" - integrity sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g== - dependencies: - clone "^2.1.1" - deep-equal "^1.0.1" - eventemitter3 "^2.0.3" - extend "^3.0.2" - parchment "^1.1.4" - quill-delta "^3.6.2" - randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -1325,6 +1308,23 @@ safe-identifier@^0.4.2: resolved "https://registry.yarnpkg.com/safe-identifier/-/safe-identifier-0.4.2.tgz#cf6bfca31c2897c588092d1750d30ef501d59fcb" integrity sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w== +sanitize-html@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.7.0.tgz#e106205b468aca932e2f9baf241f24660d34e279" + integrity sha512-jfQelabOn5voO7FAfnQF7v+jsA6z9zC/O4ec0z3E35XPEtHYJT/OdUziVWlKW4irCr2kXaQAyXTXDHWAibg1tA== + dependencies: + deepmerge "^4.2.2" + escape-string-regexp "^4.0.0" + htmlparser2 "^6.0.0" + is-plain-object "^5.0.0" + parse-srcset "^1.0.2" + postcss "^8.3.11" + +screenfull@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-6.0.1.tgz#3b71e6f06b72d817a8d3be73c45ebe71fa8da1ce" + integrity sha512-yzQW+j4zMUBQC51xxWaoDYjxOtl8Kn+xvue3p6v/fv2pIi1jH4AldgVLU8TBfFVgH2x3VXlf3+YiA/AYIPlaew== + serialize-javascript@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" @@ -1339,33 +1339,12 @@ shortid@^2.2.15: dependencies: nanoid "^2.1.0" -sirv-cli@^0.4.6: - version "0.4.6" - resolved "https://registry.yarnpkg.com/sirv-cli/-/sirv-cli-0.4.6.tgz#c28ab20deb3b34637f5a60863dc350f055abca04" - integrity sha512-/Vj85/kBvPL+n9ibgX6FicLE8VjidC1BhlX67PYPBfbBAphzR6i0k0HtU5c2arejfU3uzq8l3SYPCwl1x7z6Ww== - dependencies: - console-clear "^1.1.0" - get-port "^3.2.0" - kleur "^3.0.0" - local-access "^1.0.1" - sade "^1.4.0" - sirv "^0.4.6" - tinydate "^1.0.0" - -sirv@^0.4.6: - version "0.4.6" - resolved "https://registry.yarnpkg.com/sirv/-/sirv-0.4.6.tgz#185e44eb93d24009dd183b7494285c5180b81f22" - integrity sha512-rYpOXlNbpHiY4nVXxuDf4mXPvKz1reZGap/LkWp9TvcZ84qD/nPBjjH/6GZsgIjVMbOslnY8YYULAyP8jMn1GQ== - dependencies: - "@polka/url" "^0.5.0" - mime "^2.3.1" - slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -source-map-js@^1.0.1: +source-map-js@^1.0.1, source-map-js@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== diff --git a/packages/frontend-core/src/api/analytics.js b/packages/frontend-core/src/api/analytics.js index c6efdbddb3..bb9687e54f 100644 --- a/packages/frontend-core/src/api/analytics.js +++ b/packages/frontend-core/src/api/analytics.js @@ -1,3 +1,4 @@ +// todo: audit export const buildAnalyticsEndpoints = API => ({ /** * Notifies that an end user client app has been loaded. diff --git a/packages/server/package.json b/packages/server/package.json index 1b6e21366f..d2e450e469 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -119,7 +119,6 @@ "open": "^8.4.0", "pg": "8.5.1", "pino-pretty": "4.0.0", - "posthog-node": "^1.1.4", "pouchdb": "7.2.1", "pouchdb-adapter-memory": "^7.2.1", "pouchdb-all-dbs": "1.0.2", diff --git a/packages/server/src/api/controllers/analytics.js b/packages/server/src/api/controllers/analytics.js index b88fcdebb6..dc96b28062 100644 --- a/packages/server/src/api/controllers/analytics.js +++ b/packages/server/src/api/controllers/analytics.js @@ -1,36 +1,26 @@ -const env = require("../../environment") -const PostHog = require("posthog-node") - -let posthogClient - -if (env.POSTHOG_TOKEN && env.ENABLE_ANALYTICS && !env.SELF_HOSTED) { - posthogClient = new PostHog(env.POSTHOG_TOKEN) -} +const { analytics } = require("@budibase/backend-core") exports.isEnabled = async ctx => { ctx.body = { - enabled: !env.SELF_HOSTED && env.ENABLE_ANALYTICS === "true", + enabled: analytics.enabled(), } } exports.endUserPing = async ctx => { - if (!posthogClient) { + if (!analytics.enabled()) { ctx.body = { ping: false, } return } - posthogClient.identify({ - distinctId: ctx.user && ctx.user._id, - properties: {}, - }) - posthogClient.capture({ - event: "budibase:end_user_ping", - distinctId: ctx.user && ctx.user._id, - properties: { - appId: ctx.appId, - }, + // posthogClient.identify({ + // distinctId: ctx.user && ctx.user._id, + // properties: {}, + // }) + + analytics.captureEvent(ctx.user._id, "budibase:end_user_ping", { + appId: ctx.appId, }) ctx.body = { diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts index 0c0ef68ad9..f234df3e8f 100644 --- a/packages/server/src/app.ts +++ b/packages/server/src/app.ts @@ -17,6 +17,7 @@ const fileSystem = require("./utilities/fileSystem") const bullboard = require("./automations/bullboard") const redis = require("./utilities/redis") import * as migrations from "./migrations" +import { analytics } from "@budibase/backend-core" const app = new Koa() @@ -74,6 +75,7 @@ server.on("close", async () => { console.log("Server Closed") } await redis.shutdown() + await analytics.shutdown() }) module.exports = server.listen(env.PORT || 0, async () => { diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index e88a540b93..8869913d2e 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -951,7 +951,7 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/runtime@^7.15.4", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4": +"@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4": version "7.16.3" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5" integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ== @@ -3358,21 +3358,6 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== -axios-retry@^3.1.9: - version "3.2.4" - resolved "https://registry.yarnpkg.com/axios-retry/-/axios-retry-3.2.4.tgz#f447a53c3456f5bfeca18f20c3a3272207d082ae" - integrity sha512-Co3UXiv4npi6lM963mfnuH90/YFLKWWDmoBYfxkHT5xtkSSWNqK9zdG3fw5/CP/dsoKB5aMMJCsgab+tp1OxLQ== - dependencies: - "@babel/runtime" "^7.15.4" - is-retry-allowed "^2.2.0" - -axios@0.24.0: - version "0.24.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" - integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA== - dependencies: - follow-redirects "^1.14.4" - axios@^0.21.1, axios@^0.21.4: version "0.21.4" resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" @@ -3922,11 +3907,6 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -charenc@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" - integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= - chmodr@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/chmodr/-/chmodr-1.2.0.tgz#720e96caa09b7f1cdbb01529b7d0ab6bc5e118b9" @@ -4181,11 +4161,6 @@ component-emitter@^1.2.0, component-emitter@^1.2.1: resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== -component-type@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/component-type/-/component-type-1.2.1.tgz#8a47901700238e4fc32269771230226f24b415a9" - integrity sha1-ikeQFwAjjk/DIml3EjAibyS0Fak= - compressible@^2.0.0: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" @@ -4358,11 +4333,6 @@ cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -crypt@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" - integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= - crypto-random-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" @@ -5774,7 +5744,7 @@ fn.name@1.x.x: resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== -follow-redirects@^1.14.0, follow-redirects@^1.14.4: +follow-redirects@^1.14.0: version "1.14.8" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc" integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA== @@ -6735,7 +6705,7 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-buffer@^1.1.5, is-buffer@~1.1.6: +is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== @@ -6959,11 +6929,6 @@ is-retry-allowed@^1.1.0: resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== -is-retry-allowed@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz#88f34cbd236e043e71b6932d09b0c65fb7b4d71d" - integrity sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg== - is-shared-array-buffer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" @@ -7927,11 +7892,6 @@ joi@17.2.1: "@hapi/pinpoint" "^2.0.0" "@hapi/topo" "^5.0.0" -join-component@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/join-component/-/join-component-1.1.0.tgz#b8417b750661a392bee2c2537c68b2a9d4977cd5" - integrity sha1-uEF7dQZho5K+4sJTfGiyqdSXfNU= - joycon@^2.2.5: version "2.2.5" resolved "https://registry.yarnpkg.com/joycon/-/joycon-2.2.5.tgz#8d4cf4cbb2544d7b7583c216fcdfec19f6be1615" @@ -8905,15 +8865,6 @@ markdown-it@^12.2.0: mdurl "^1.0.1" uc.micro "^1.0.5" -md5@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" - integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== - dependencies: - charenc "0.0.2" - crypt "0.0.2" - is-buffer "~1.1.6" - mdurl@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" @@ -9128,11 +9079,6 @@ ms@2.1.2, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - mssql@6.2.3: version "6.2.3" resolved "https://registry.yarnpkg.com/mssql/-/mssql-6.2.3.tgz#1d15bbe8c3057e32ee6e98596b6c323b097a6cba" @@ -10034,20 +9980,6 @@ postgres-interval@^1.1.0: dependencies: xtend "^4.0.0" -posthog-node@^1.1.4: - version "1.2.0" - resolved "https://registry.yarnpkg.com/posthog-node/-/posthog-node-1.2.0.tgz#409d210e506742ebf22e918908fbf0970fa32b7d" - integrity sha512-hNYq343274y3TY4wlP2xktJW7ZdWDrdwcom5ZOMndZxUNjUaCEhihTmft99TsFCp13fr45BFpVN9k+QcXQqa5A== - dependencies: - axios "0.24.0" - axios-retry "^3.1.9" - component-type "^1.2.1" - join-component "^1.1.0" - md5 "^2.3.0" - ms "^2.1.3" - remove-trailing-slash "^0.1.1" - uuid "^8.3.2" - pouch-stream@^0.4.0: version "0.4.1" resolved "https://registry.yarnpkg.com/pouch-stream/-/pouch-stream-0.4.1.tgz#0c6d8475c9307677627991a2f079b301c3b89bdd" @@ -10779,11 +10711,6 @@ remove-trailing-separator@^1.0.1: resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= -remove-trailing-slash@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz#be2285a59f39c74d1bce4f825950061915e3780d" - integrity sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA== - repeat-element@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" From 4789e7f02bb8886c5ed5c51e5101851fb321b694 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Mon, 28 Mar 2022 09:50:52 +0100 Subject: [PATCH 02/75] update server lockfile --- packages/server/yarn.lock | 39 +++------------------------------------ 1 file changed, 3 insertions(+), 36 deletions(-) diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 3e188b62f4..9861fece01 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -969,7 +969,7 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/runtime@^7.15.4", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4": +"@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4": version "7.17.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.2.tgz#66f68591605e59da47523c631416b18508779941" integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw== @@ -2584,7 +2584,7 @@ dependencies: "@types/koa" "*" -"@types/lodash@^4.14.179": +"@types/lodash@4.14.180": version "4.14.180" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.180.tgz#4ab7c9ddfc92ec4a887886483bc14c79fb380670" integrity sha512-XOKXa1KIxtNXgASAnwj7cnttJxS4fksBRywK/9LzRV5YxrF80BXZIGeQSuoESQ/VkUj30Ae0+YcuHc15wJCB2g== @@ -2604,11 +2604,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.18.tgz#3b4fed5cfb58010e3a2be4b6e74615e4847f1074" integrity sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA== -"@types/node@>=12.12.47", "@types/node@>=13.7.0": - version "17.0.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.21.tgz#864b987c0c68d07b4345845c3e63b75edd143644" - integrity sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ== - "@types/node@>=12.12.47", "@types/node@>=13.7.0": version "17.0.23" resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.23.tgz#3b41a6e643589ac6442bdbd7a4a3ded62f33f7da" @@ -7045,11 +7040,6 @@ is-retry-allowed@^1.1.0: resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== -is-retry-allowed@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz#88f34cbd236e043e71b6932d09b0c65fb7b4d71d" - integrity sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg== - is-shared-array-buffer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" @@ -9006,15 +8996,6 @@ markdown-it@^12.2.0: mdurl "^1.0.1" uc.micro "^1.0.5" -md5@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" - integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== - dependencies: - charenc "0.0.2" - crypt "0.0.2" - is-buffer "~1.1.6" - mdurl@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" @@ -9217,7 +9198,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.1.1, ms@^2.1.3: +ms@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -10126,20 +10107,6 @@ postgres-interval@^1.1.0: dependencies: xtend "^4.0.0" -posthog-node@^1.1.4: - version "1.3.0" - resolved "https://registry.yarnpkg.com/posthog-node/-/posthog-node-1.3.0.tgz#804ed2f213a2f05253f798bf9569d55a9cad94f7" - integrity sha512-2+VhqiY/rKIqKIXyvemBFHbeijHE25sP7eKltnqcFqAssUE6+sX6vusN9A4luzToOqHQkUZexiCKxvuGagh7JA== - dependencies: - axios "0.24.0" - axios-retry "^3.1.9" - component-type "^1.2.1" - join-component "^1.1.0" - md5 "^2.3.0" - ms "^2.1.3" - remove-trailing-slash "^0.1.1" - uuid "^8.3.2" - pouch-stream@^0.4.0: version "0.4.1" resolved "https://registry.yarnpkg.com/pouch-stream/-/pouch-stream-0.4.1.tgz#0c6d8475c9307677627991a2f079b301c3b89bdd" From 15b676ce1c2a91a4423fcbf012bc2ba736220086 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 29 Mar 2022 16:03:44 +0100 Subject: [PATCH 03/75] Remove remaining direct usage of CouchDB. Add common pouch config to backend-core --- packages/backend-core/db.js | 1 + packages/backend-core/package.json | 2 + .../backend-core/src/cache/appMetadata.js | 14 +- packages/backend-core/src/context/index.js | 13 +- packages/backend-core/src/db/index.js | 37 ++++- packages/backend-core/src/db/pouch.js | 62 +++++++ packages/backend-core/src/db/utils.js | 32 +--- packages/backend-core/src/index.js | 7 +- packages/backend-core/src/migrations/index.js | 11 +- .../src/migrations/tests/index.spec.js | 6 +- .../backend-core/src/tests/utilities/db.js | 17 -- .../src/tests/utilities/dbConfig.js | 8 +- packages/backend-core/yarn.lock | 151 +++++++++++++++++- packages/server/scripts/exportAppTemplate.js | 5 +- packages/server/scripts/replicateApp.js | 11 +- .../query/import/tests/index.spec.js | 18 +-- packages/server/src/app.ts | 4 +- packages/server/src/automations/utils.ts | 11 +- packages/server/src/db/client.js | 31 ---- packages/server/src/db/inMemoryView.js | 11 +- packages/server/src/db/index.js | 17 +- .../server/src/db/tests/linkTests.spec.js | 4 +- .../src/middleware/tests/authorized.spec.js | 1 - .../functions/tests/appUrls.spec.js | 6 +- packages/server/src/migrations/index.ts | 3 +- packages/server/src/module.d.ts | 4 + .../src/tests/utilities/TestConfiguration.js | 8 +- packages/server/src/threads/utils.js | 5 +- .../server/src/utilities/fileSystem/index.js | 4 +- .../server/src/utilities/usageQuota/rows.js | 9 +- .../src/api/controllers/system/tenants.js | 7 +- .../tests/utilities/TestConfiguration.js | 6 +- packages/worker/src/db/index.js | 29 +--- packages/worker/src/index.ts | 4 +- 34 files changed, 349 insertions(+), 210 deletions(-) create mode 100644 packages/backend-core/src/db/pouch.js delete mode 100644 packages/backend-core/src/tests/utilities/db.js delete mode 100644 packages/server/src/db/client.js diff --git a/packages/backend-core/db.js b/packages/backend-core/db.js index d2adf6c092..0d2869d9f1 100644 --- a/packages/backend-core/db.js +++ b/packages/backend-core/db.js @@ -3,4 +3,5 @@ module.exports = { ...require("./src/db/constants"), ...require("./src/db"), ...require("./src/db/views"), + ...require("./src/db/pouch"), } diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index a0c2ff2db1..a271b33557 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -25,6 +25,8 @@ "passport-jwt": "^4.0.0", "passport-local": "^1.0.0", "posthog-node": "^1.3.0", + "pouchdb-find": "^7.2.2", + "pouchdb-replication-stream": "^1.2.9", "sanitize-s3-objectkey": "^0.0.1", "tar-fs": "^2.1.1", "uuid": "^8.3.2", diff --git a/packages/backend-core/src/cache/appMetadata.js b/packages/backend-core/src/cache/appMetadata.js index 9e2317ae37..15a25006ab 100644 --- a/packages/backend-core/src/cache/appMetadata.js +++ b/packages/backend-core/src/cache/appMetadata.js @@ -1,5 +1,5 @@ const redis = require("../redis/authRedis") -const { getCouch } = require("../db") +const { getDB } = require("../db") const { DocumentTypes } = require("../db/constants") const AppState = { @@ -10,11 +10,8 @@ const EXPIRY_SECONDS = 3600 /** * The default populate app metadata function */ -const populateFromDB = async (appId, CouchDB = null) => { - if (!CouchDB) { - CouchDB = getCouch() - } - const db = new CouchDB(appId, { skip_setup: true }) +const populateFromDB = async appId => { + const db = getDB(appId, { skip_setup: true }) return db.get(DocumentTypes.APP_METADATA) } @@ -27,17 +24,16 @@ const isInvalid = metadata => { * Use redis cache to first read the app metadata. * If not present fallback to loading the app metadata directly and re-caching. * @param {string} appId the id of the app to get metadata from. - * @param {object} CouchDB the database being passed * @returns {object} the app metadata. */ -exports.getAppMetadata = async (appId, CouchDB = null) => { +exports.getAppMetadata = async appId => { const client = await redis.getAppClient() // try cache let metadata = await client.get(appId) if (!metadata) { let expiry = EXPIRY_SECONDS try { - metadata = await populateFromDB(appId, CouchDB) + metadata = await populateFromDB(appId) } catch (err) { // app DB left around, but no metadata, it is invalid if (err && err.status === 404) { diff --git a/packages/backend-core/src/context/index.js b/packages/backend-core/src/context/index.js index 968ad4eefb..bf815120fa 100644 --- a/packages/backend-core/src/context/index.js +++ b/packages/backend-core/src/context/index.js @@ -1,7 +1,7 @@ const env = require("../environment") const { Headers } = require("../../constants") const cls = require("./FunctionContext") -const { getCouch } = require("../db") +const { getDB } = require("../db") const { getProdAppID, getDevelopmentAppID } = require("../db/conversions") const { isEqual } = require("lodash") @@ -135,7 +135,7 @@ exports.getAppId = () => { } } -function getDB(key, opts) { +function getContextDB(key, opts) { const dbOptsKey = `${key}${ContextKeys.DB_OPTS}` let storedOpts = cls.getFromContext(dbOptsKey) let db = cls.getFromContext(key) @@ -143,7 +143,6 @@ function getDB(key, opts) { return db } const appId = exports.getAppId() - const CouchDB = getCouch() let toUseAppId switch (key) { case ContextKeys.CURRENT_DB: @@ -156,7 +155,7 @@ function getDB(key, opts) { toUseAppId = getDevelopmentAppID(appId) break } - db = new CouchDB(toUseAppId, opts) + db = getDB(toUseAppId, opts) try { cls.setOnContext(key, db) if (opts) { @@ -175,7 +174,7 @@ function getDB(key, opts) { * contained, dev or prod. */ exports.getAppDB = opts => { - return getDB(ContextKeys.CURRENT_DB, opts) + return getContextDB(ContextKeys.CURRENT_DB, opts) } /** @@ -183,7 +182,7 @@ exports.getAppDB = opts => { * contained a development app ID, this will open the prod one. */ exports.getProdAppDB = opts => { - return getDB(ContextKeys.PROD_DB, opts) + return getContextDB(ContextKeys.PROD_DB, opts) } /** @@ -191,5 +190,5 @@ exports.getProdAppDB = opts => { * contained a prod app ID, this will open the dev one. */ exports.getDevAppDB = opts => { - return getDB(ContextKeys.DEV_DB, opts) + return getContextDB(ContextKeys.DEV_DB, opts) } diff --git a/packages/backend-core/src/db/index.js b/packages/backend-core/src/db/index.js index 163364dbf3..cc5eaaf9c5 100644 --- a/packages/backend-core/src/db/index.js +++ b/packages/backend-core/src/db/index.js @@ -1,13 +1,36 @@ -let Pouch +const pouch = require("./pouch") -module.exports.setDB = pouch => { - Pouch = pouch +let PouchDB +let initialised = false + +const put = + dbPut => + async (doc, options = {}) => { + const response = await dbPut(doc, options) + // TODO: add created / updated + return response + } + +const checkInitialised = () => { + if (!initialised) { + throw new Error("init has not been called") + } } -module.exports.getDB = dbName => { - return new Pouch(dbName) +exports.init = opts => { + PouchDB = pouch.getPouch(opts) + initialised = true } -module.exports.getCouch = () => { - return Pouch +exports.getDB = (dbName, opts) => { + checkInitialised() + const db = new PouchDB(dbName, opts) + const dbPut = db.put + db.put = put(dbPut) + return db +} + +exports.allDbs = () => { + checkInitialised() + return PouchDB.allDbs() } diff --git a/packages/backend-core/src/db/pouch.js b/packages/backend-core/src/db/pouch.js new file mode 100644 index 0000000000..0e3315b43c --- /dev/null +++ b/packages/backend-core/src/db/pouch.js @@ -0,0 +1,62 @@ +const PouchDB = require("pouchdb") +const env = require("../environment") + +exports.getCouchUrl = () => { + if (!env.COUCH_DB_URL) return + + // username and password already exist in URL + if (env.COUCH_DB_URL.includes("@")) { + return env.COUCH_DB_URL + } + + const [protocol, ...rest] = env.COUCH_DB_URL.split("://") + + if (!env.COUCH_DB_USERNAME || !env.COUCH_DB_PASSWORD) { + throw new Error( + "CouchDB configuration invalid. You must provide a fully qualified CouchDB url, or the COUCH_DB_USER and COUCH_DB_PASSWORD environment variables." + ) + } + + return `${protocol}://${env.COUCH_DB_USERNAME}:${env.COUCH_DB_PASSWORD}@${rest}` +} + +/** + * Return a constructor for PouchDB. + * This should be rarely used outside of the main application config. + * Exposed for exceptional cases such as in-memory views. + */ +exports.getPouch = (opts = {}) => { + const COUCH_DB_URL = exports.getCouchUrl() || "http://localhost:4005" + + let POUCH_DB_DEFAULTS = { + prefix: COUCH_DB_URL, + } + + if (opts.inMemory) { + const inMemory = require("pouchdb-adapter-memory") + PouchDB.plugin(inMemory) + POUCH_DB_DEFAULTS = { + prefix: undefined, + adapter: "memory", + } + } + + if (opts.replication) { + const replicationStream = require("pouchdb-replication-stream") + PouchDB.plugin(replicationStream.plugin) + PouchDB.adapter("writableStream", replicationStream.adapters.writableStream) + } + + if (opts.find) { + const find = require("pouchdb-find") + PouchDB.plugin(find) + } + + const Pouch = PouchDB.defaults(POUCH_DB_DEFAULTS) + if (opts.allDbs) { + const allDbs = require("pouchdb-all-dbs") + allDbs(Pouch) + } + + return Pouch +} diff --git a/packages/backend-core/src/db/utils.js b/packages/backend-core/src/db/utils.js index f22273ccd5..e70f3fae79 100644 --- a/packages/backend-core/src/db/utils.js +++ b/packages/backend-core/src/db/utils.js @@ -15,7 +15,8 @@ const { getGlobalDBName, } = require("../tenancy") const fetch = require("node-fetch") -const { getCouch } = require("./index") +const { getDB, allDbs } = require("./index") +const { getCouchUrl } = require("./pouch") const { getAppMetadata } = require("../cache/appMetadata") const { checkSlashesInUrl } = require("../helpers") const { @@ -155,25 +156,6 @@ exports.getRoleParams = (roleId = null, otherProps = {}) => { return getDocParams(DocumentTypes.ROLE, roleId, otherProps) } -exports.getCouchUrl = () => { - if (!env.COUCH_DB_URL) return - - // username and password already exist in URL - if (env.COUCH_DB_URL.includes("@")) { - return env.COUCH_DB_URL - } - - const [protocol, ...rest] = env.COUCH_DB_URL.split("://") - - if (!env.COUCH_DB_USERNAME || !env.COUCH_DB_PASSWORD) { - throw new Error( - "CouchDB configuration invalid. You must provide a fully qualified CouchDB url, or the COUCH_DB_USER and COUCH_DB_PASSWORD environment variables." - ) - } - - return `${protocol}://${env.COUCH_DB_USERNAME}:${env.COUCH_DB_PASSWORD}@${rest}` -} - exports.getStartEndKeyURL = (base, baseKey, tenantId = null) => { const tenancy = tenantId ? `${SEPARATOR}${tenantId}` : "" return `${base}?startkey="${baseKey}${tenancy}"&endkey="${baseKey}${tenancy}${UNICODE_MAX}"` @@ -189,7 +171,7 @@ exports.getAllDbs = async (opts = { efficient: false }) => { const efficient = opts && opts.efficient // specifically for testing we use the pouch package for this if (env.isTest()) { - return getCouch().allDbs() + return allDbs() } let dbs = [] async function addDbs(url) { @@ -201,7 +183,7 @@ exports.getAllDbs = async (opts = { efficient: false }) => { throw "Cannot connect to CouchDB instance" } } - let couchUrl = `${exports.getCouchUrl()}/_all_dbs` + let couchUrl = `${getCouchUrl()}/_all_dbs` let tenantId = getTenantId() if (!env.MULTI_TENANCY || (!efficient && tenantId === DEFAULT_TENANT_ID)) { // just get all DBs when: @@ -232,7 +214,6 @@ exports.getAllDbs = async (opts = { efficient: false }) => { * @return {Promise} returns the app information document stored in each app database. */ exports.getAllApps = async ({ dev, all, idsOnly, efficient } = {}) => { - const CouchDB = getCouch() let tenantId = getTenantId() if (!env.MULTI_TENANCY && !tenantId) { tenantId = DEFAULT_TENANT_ID @@ -260,7 +241,7 @@ exports.getAllApps = async ({ dev, all, idsOnly, efficient } = {}) => { } const appPromises = appDbNames.map(app => // skip setup otherwise databases could be re-created - getAppMetadata(app, CouchDB) + getAppMetadata(app) ) if (appPromises.length === 0) { return [] @@ -304,10 +285,9 @@ exports.getDevAppIDs = async () => { } exports.dbExists = async dbName => { - const CouchDB = getCouch() let exists = false try { - const db = CouchDB(dbName, { skip_setup: true }) + const db = getDB(dbName, { skip_setup: true }) // check if database exists const info = await db.info() if (info && !info.error) { diff --git a/packages/backend-core/src/index.js b/packages/backend-core/src/index.js index 8f71580162..f403b44a3d 100644 --- a/packages/backend-core/src/index.js +++ b/packages/backend-core/src/index.js @@ -1,8 +1,8 @@ -const { setDB } = require("./db") +const db = require("./db") module.exports = { - init(pouch) { - setDB(pouch) + init(opts = {}) { + db.init(opts.db) }, // some default exports from the library, however these ideally shouldn't // be used, instead the syntax require("@budibase/backend-core/db") should be used @@ -20,4 +20,5 @@ module.exports = { accounts: require("./cloud/accounts"), tenancy: require("./tenancy"), featureFlags: require("./featureFlags"), + analytics: require("./analytics"), } diff --git a/packages/backend-core/src/migrations/index.js b/packages/backend-core/src/migrations/index.js index db0fe6b8ce..1974a52463 100644 --- a/packages/backend-core/src/migrations/index.js +++ b/packages/backend-core/src/migrations/index.js @@ -1,4 +1,5 @@ const { DEFAULT_TENANT_ID } = require("../constants") +const { getDB } = require("../db") const { DocumentTypes } = require("../db/constants") const { getAllApps } = require("../db/utils") const environment = require("../environment") @@ -26,7 +27,7 @@ exports.getMigrationsDoc = async db => { } } -const runMigration = async (CouchDB, migration, options = {}) => { +const runMigration = async (migration, options = {}) => { const tenantId = getTenantId() const migrationType = migration.type const migrationName = migration.name @@ -46,7 +47,7 @@ const runMigration = async (CouchDB, migration, options = {}) => { // run the migration against each db for (const dbName of dbNames) { - const db = new CouchDB(dbName) + const db = getDB(dbName) try { const doc = await exports.getMigrationsDoc(db) @@ -88,7 +89,7 @@ const runMigration = async (CouchDB, migration, options = {}) => { } } -exports.runMigrations = async (CouchDB, migrations, options = {}) => { +exports.runMigrations = async (migrations, options = {}) => { console.log("Running migrations") let tenantIds if (environment.MULTI_TENANCY) { @@ -108,9 +109,7 @@ exports.runMigrations = async (CouchDB, migrations, options = {}) => { // for all migrations for (const migration of migrations) { // run the migration - await doInTenant(tenantId, () => - runMigration(CouchDB, migration, options) - ) + await doInTenant(tenantId, () => runMigration(migration, options)) } } console.log("Migrations complete") diff --git a/packages/backend-core/src/migrations/tests/index.spec.js b/packages/backend-core/src/migrations/tests/index.spec.js index 12a2e54cb3..0a7659e279 100644 --- a/packages/backend-core/src/migrations/tests/index.spec.js +++ b/packages/backend-core/src/migrations/tests/index.spec.js @@ -1,7 +1,7 @@ require("../../tests/utilities/dbConfig") const { runMigrations, getMigrationsDoc } = require("../index") -const CouchDB = require("../../db").getCouch() +const { getDB } = require("../../db") const { StaticDatabases, } = require("../../db/utils") @@ -20,7 +20,7 @@ describe("migrations", () => { }] beforeEach(() => { - db = new CouchDB(StaticDatabases.GLOBAL.name) + db = getDB(StaticDatabases.GLOBAL.name) }) afterEach(async () => { @@ -29,7 +29,7 @@ describe("migrations", () => { }) const migrate = () => { - return runMigrations(CouchDB, MIGRATIONS) + return runMigrations(MIGRATIONS) } it("should run a new migration", async () => { diff --git a/packages/backend-core/src/tests/utilities/db.js b/packages/backend-core/src/tests/utilities/db.js deleted file mode 100644 index bb99592d1c..0000000000 --- a/packages/backend-core/src/tests/utilities/db.js +++ /dev/null @@ -1,17 +0,0 @@ -const PouchDB = require("pouchdb") -const env = require("../../environment") - -let POUCH_DB_DEFAULTS - -// should always be test but good to do the sanity check -if (env.isTest()) { - PouchDB.plugin(require("pouchdb-adapter-memory")) - POUCH_DB_DEFAULTS = { - prefix: undefined, - adapter: "memory", - } -} - -const Pouch = PouchDB.defaults(POUCH_DB_DEFAULTS) - -module.exports = Pouch diff --git a/packages/backend-core/src/tests/utilities/dbConfig.js b/packages/backend-core/src/tests/utilities/dbConfig.js index 45b9ff33f9..acd692df40 100644 --- a/packages/backend-core/src/tests/utilities/dbConfig.js +++ b/packages/backend-core/src/tests/utilities/dbConfig.js @@ -1,3 +1,5 @@ -const packageConfiguration = require("../../index") -const CouchDB = require("./db") -packageConfiguration.init(CouchDB) +const core = require("../../index") +const dbConfig = { + inMemory: true, +} +core.init({ db: dbConfig }) diff --git a/packages/backend-core/yarn.lock b/packages/backend-core/yarn.lock index b072b163de..ad9769202b 100644 --- a/packages/backend-core/yarn.lock +++ b/packages/backend-core/yarn.lock @@ -2217,7 +2217,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -2407,7 +2407,7 @@ isarray@0.0.1: resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= -isarray@1.0.0, isarray@^1.0.0: +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= @@ -2954,7 +2954,7 @@ json-stable-stringify@^1.0.1: dependencies: jsonify "~0.0.0" -json-stringify-safe@~5.0.1: +json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= @@ -3256,6 +3256,11 @@ lodash.once@^4.0.0: resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= +lodash.pick@^4.0.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" + integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= + lodash@^4.14.0: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -3465,6 +3470,16 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +ndjson@^1.4.3: + version "1.5.0" + resolved "https://registry.yarnpkg.com/ndjson/-/ndjson-1.5.0.tgz#ae603b36b134bcec347b452422b0bf98d5832ec8" + integrity sha1-rmA7NrE0vOw0e0UkIrC/mNWDLsg= + dependencies: + json-stringify-safe "^5.0.1" + minimist "^1.2.0" + split2 "^2.1.0" + through2 "^2.0.3" + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -3849,6 +3864,28 @@ posthog-node@^1.3.0: remove-trailing-slash "^0.1.1" uuid "^8.3.2" +pouch-stream@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/pouch-stream/-/pouch-stream-0.4.1.tgz#0c6d8475c9307677627991a2f079b301c3b89bdd" + integrity sha1-DG2EdckwdndieZGi8HmzAcO4m90= + dependencies: + inherits "^2.0.1" + readable-stream "^1.0.27-1" + +pouchdb-abstract-mapreduce@7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-abstract-mapreduce/-/pouchdb-abstract-mapreduce-7.2.2.tgz#dd1b10a83f8d24361dce9aaaab054614b39f766f" + integrity sha512-7HWN/2yV2JkwMnGnlp84lGvFtnm0Q55NiBUdbBcaT810+clCGKvhssBCrXnmwShD1SXTwT83aszsgiSfW+SnBA== + dependencies: + pouchdb-binary-utils "7.2.2" + pouchdb-collate "7.2.2" + pouchdb-collections "7.2.2" + pouchdb-errors "7.2.2" + pouchdb-fetch "7.2.2" + pouchdb-mapreduce-utils "7.2.2" + pouchdb-md5 "7.2.2" + pouchdb-utils "7.2.2" + pouchdb-adapter-leveldb-core@7.2.2: version "7.2.2" resolved "https://registry.yarnpkg.com/pouchdb-adapter-leveldb-core/-/pouchdb-adapter-leveldb-core-7.2.2.tgz#e0aa6a476e2607d7ae89f4a803c9fba6e6d05a8a" @@ -3908,6 +3945,11 @@ pouchdb-binary-utils@7.2.2: dependencies: buffer-from "1.1.1" +pouchdb-collate@7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-collate/-/pouchdb-collate-7.2.2.tgz#fc261f5ef837c437e3445fb0abc3f125d982c37c" + integrity sha512-/SMY9GGasslknivWlCVwXMRMnQ8myKHs4WryQ5535nq1Wj/ehpqWloMwxEQGvZE1Sda3LOm7/5HwLTcB8Our+w== + pouchdb-collections@7.2.2: version "7.2.2" resolved "https://registry.yarnpkg.com/pouchdb-collections/-/pouchdb-collections-7.2.2.tgz#aeed77f33322429e3f59d59ea233b48ff0e68572" @@ -3920,6 +3962,28 @@ pouchdb-errors@7.2.2: dependencies: inherits "2.0.4" +pouchdb-fetch@7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-fetch/-/pouchdb-fetch-7.2.2.tgz#492791236d60c899d7e9973f9aca0d7b9cc02230" + integrity sha512-lUHmaG6U3zjdMkh8Vob9GvEiRGwJfXKE02aZfjiVQgew+9SLkuOxNw3y2q4d1B6mBd273y1k2Lm0IAziRNxQnA== + dependencies: + abort-controller "3.0.0" + fetch-cookie "0.10.1" + node-fetch "2.6.0" + +pouchdb-find@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-find/-/pouchdb-find-7.2.2.tgz#1227afdd761812d508fe0794b3e904518a721089" + integrity sha512-BmFeFVQ0kHmDehvJxNZl9OmIztCjPlZlVSdpijuFbk/Fi1EFPU1BAv3kLC+6DhZuOqU/BCoaUBY9sn66pPY2ag== + dependencies: + pouchdb-abstract-mapreduce "7.2.2" + pouchdb-collate "7.2.2" + pouchdb-errors "7.2.2" + pouchdb-fetch "7.2.2" + pouchdb-md5 "7.2.2" + pouchdb-selector-core "7.2.2" + pouchdb-utils "7.2.2" + pouchdb-json@7.2.2: version "7.2.2" resolved "https://registry.yarnpkg.com/pouchdb-json/-/pouchdb-json-7.2.2.tgz#b939be24b91a7322e9a24b8880a6e21514ec5e1f" @@ -3927,6 +3991,16 @@ pouchdb-json@7.2.2: dependencies: vuvuzela "1.0.3" +pouchdb-mapreduce-utils@7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-mapreduce-utils/-/pouchdb-mapreduce-utils-7.2.2.tgz#13a46a3cc2a3f3b8e24861da26966904f2963146" + integrity sha512-rAllb73hIkU8rU2LJNbzlcj91KuulpwQu804/F6xF3fhZKC/4JQMClahk+N/+VATkpmLxp1zWmvmgdlwVU4HtQ== + dependencies: + argsarray "0.0.1" + inherits "2.0.4" + pouchdb-collections "7.2.2" + pouchdb-utils "7.2.2" + pouchdb-md5@7.2.2: version "7.2.2" resolved "https://registry.yarnpkg.com/pouchdb-md5/-/pouchdb-md5-7.2.2.tgz#415401acc5a844112d765bd1fb4e5d9f38fb0838" @@ -3940,13 +4014,34 @@ pouchdb-merge@7.2.2: resolved "https://registry.yarnpkg.com/pouchdb-merge/-/pouchdb-merge-7.2.2.tgz#940d85a2b532d6a93a6cab4b250f5648511bcc16" integrity sha512-6yzKJfjIchBaS7Tusuk8280WJdESzFfQ0sb4jeMUNnrqs4Cx3b0DIEOYTRRD9EJDM+je7D3AZZ4AT0tFw8gb4A== -pouchdb-promise@6.4.3: +pouchdb-promise@6.4.3, pouchdb-promise@^6.0.4: version "6.4.3" resolved "https://registry.yarnpkg.com/pouchdb-promise/-/pouchdb-promise-6.4.3.tgz#74516f4acf74957b54debd0fb2c0e5b5a68ca7b3" integrity sha512-ruJaSFXwzsxRHQfwNHjQfsj58LBOY1RzGzde4PM5CWINZwFjCQAhZwfMrch2o/0oZT6d+Xtt0HTWhq35p3b0qw== dependencies: lie "3.1.1" +pouchdb-replication-stream@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/pouchdb-replication-stream/-/pouchdb-replication-stream-1.2.9.tgz#aa4fa5d8f52df4825392f18e07c7e11acffc650a" + integrity sha1-qk+l2PUt9IJTkvGOB8fhGs/8ZQo= + dependencies: + argsarray "0.0.1" + inherits "^2.0.3" + lodash.pick "^4.0.0" + ndjson "^1.4.3" + pouch-stream "^0.4.0" + pouchdb-promise "^6.0.4" + through2 "^2.0.0" + +pouchdb-selector-core@7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/pouchdb-selector-core/-/pouchdb-selector-core-7.2.2.tgz#264d7436a8c8ac3801f39960e79875ef7f3879a0" + integrity sha512-XYKCNv9oiNmSXV5+CgR9pkEkTFqxQGWplnVhO3W9P154H08lU0ZoNH02+uf+NjZ2kjse7Q1fxV4r401LEcGMMg== + dependencies: + pouchdb-collate "7.2.2" + pouchdb-utils "7.2.2" + pouchdb-utils@7.2.2: version "7.2.2" resolved "https://registry.yarnpkg.com/pouchdb-utils/-/pouchdb-utils-7.2.2.tgz#c17c4788f1d052b0daf4ef8797bbc4aaa3945aa4" @@ -4007,6 +4102,11 @@ private@^0.1.6, private@~0.1.5: resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + prompts@^2.0.1: version "2.4.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" @@ -4092,7 +4192,7 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -readable-stream@1.1.14: +readable-stream@1.1.14, readable-stream@^1.0.27-1: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= @@ -4116,6 +4216,19 @@ readable-stream@~0.0.2: resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-0.0.4.tgz#f32d76e3fb863344a548d79923007173665b3b8d" integrity sha1-8y124/uGM0SlSNeZIwBxc2ZbO40= +readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + readline-sync@^1.4.9: version "1.4.10" resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.10.tgz#41df7fbb4b6312d673011594145705bf56d8873b" @@ -4292,7 +4405,7 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.1: +safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== @@ -4548,6 +4661,13 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" +split2@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/split2/-/split2-2.2.0.tgz#186b2575bcf83e85b7d18465756238ee4ee42493" + integrity sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw== + dependencies: + through2 "^2.0.2" + sprintf-js@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" @@ -4638,6 +4758,13 @@ string_decoder@~0.10.x: resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + stringstream@~0.0.4: version "0.0.6" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.6.tgz#7880225b0d4ad10e30927d167a1d6f2fd3b33a72" @@ -4753,6 +4880,14 @@ through2@3.0.2: inherits "^2.0.4" readable-stream "2 || 3" +through2@^2.0.0, through2@^2.0.2, through2@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + through@~2.3.4: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -4947,7 +5082,7 @@ use@^3.1.0: resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== -util-deprecate@^1.0.1: +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= @@ -5174,7 +5309,7 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xtend@^4.0.2, xtend@~4.0.0: +xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== diff --git a/packages/server/scripts/exportAppTemplate.js b/packages/server/scripts/exportAppTemplate.js index 8b641f7c92..905102722e 100755 --- a/packages/server/scripts/exportAppTemplate.js +++ b/packages/server/scripts/exportAppTemplate.js @@ -2,7 +2,8 @@ const yargs = require("yargs") const fs = require("fs") const { join } = require("path") -const CouchDB = require("../src/db") +require("../src/db").init() +const { getDB } = require("@budibase/backend-core/db") // load environment const env = require("../src/environment") const { @@ -47,7 +48,7 @@ yargs const writeStream = fs.createWriteStream(join(exportPath, "dump.text")) // perform couch dump - const instanceDb = new CouchDB(appId) + const instanceDb = getDB(appId) await instanceDb.dump(writeStream, { filter: doc => !( diff --git a/packages/server/scripts/replicateApp.js b/packages/server/scripts/replicateApp.js index 6730fe42d3..455c78b66e 100644 --- a/packages/server/scripts/replicateApp.js +++ b/packages/server/scripts/replicateApp.js @@ -5,10 +5,9 @@ * e.g. node scripts/replicateApp Mike http://admin:password@127.0.0.1:5984 */ -const CouchDB = require("../src/db") +require("../src/db").init() const { DocumentTypes } = require("../src/db/utils") -const { getAllDbs } = require("@budibase/backend-core/db") - +const { getAllDbs, getDB } = require("@budibase/backend-core/db") const appName = process.argv[2].toLowerCase() const remoteUrl = process.argv[3] @@ -19,7 +18,7 @@ const run = async () => { const appDbNames = dbs.filter(dbName => dbName.startsWith("inst_app")) let apps = [] for (let dbName of appDbNames) { - const db = new CouchDB(dbName) + const db = getDB(dbName) apps.push(db.get(DocumentTypes.APP_METADATA)) } apps = await Promise.all(apps) @@ -34,8 +33,8 @@ const run = async () => { return } - const instanceDb = new CouchDB(app.appId) - const remoteDb = new CouchDB(`${remoteUrl}/${appName}`) + const instanceDb = getDB(app.appId) + const remoteDb = getDB(`${remoteUrl}/${appName}`) instanceDb.replicate .to(remoteDb) diff --git a/packages/server/src/api/controllers/query/import/tests/index.spec.js b/packages/server/src/api/controllers/query/import/tests/index.spec.js index 0cfb4f4f19..f207213587 100644 --- a/packages/server/src/api/controllers/query/import/tests/index.spec.js +++ b/packages/server/src/api/controllers/query/import/tests/index.spec.js @@ -1,12 +1,4 @@ - -const bulkDocs = jest.fn() -const db = jest.fn(() => { - return { - bulkDocs - } -}) -jest.mock("../../../../../db", () => db) -require("@budibase/backend-core").init(require("../../../../../db")) +const TestConfig = require("../../../../../tests/utilities/TestConfiguration") const { RestImporter } = require("../index") @@ -48,6 +40,12 @@ const datasets = { } describe("Rest Importer", () => { + const config = new TestConfig(false) + + beforeEach(async () => { + await config.init() + }) + let restImporter const init = async (data) => { @@ -105,11 +103,9 @@ describe("Rest Importer", () => { const testImportQueries = async (key, data, assertions) => { await init(data) - bulkDocs.mockReturnValue([]) const importResult = await restImporter.importQueries("datasourceId") expect(importResult.errorQueries.length).toBe(0) expect(importResult.queries.length).toBe(assertions[key].count) - expect(bulkDocs).toHaveBeenCalledTimes(1) jest.clearAllMocks() } diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts index 93b32d353f..4060749629 100644 --- a/packages/server/src/app.ts +++ b/packages/server/src/app.ts @@ -1,8 +1,8 @@ // need to load environment first import { ExtendableContext } from "koa" import * as env from "./environment" -const CouchDB = require("./db") -require("@budibase/backend-core").init(CouchDB) +import db from "./db" +db.init() const Koa = require("koa") const destroyable = require("server-destroy") const koaBody = require("koa-body") diff --git a/packages/server/src/automations/utils.ts b/packages/server/src/automations/utils.ts index b0a891f42b..ac73ea932b 100644 --- a/packages/server/src/automations/utils.ts +++ b/packages/server/src/automations/utils.ts @@ -1,12 +1,11 @@ import { Thread, ThreadType } from "../threads" import { definitions } from "./triggerInfo" import * as webhooks from "../api/controllers/webhook" -import CouchDB from "../db" import { queue } from "./bullboard" import newid from "../db/newid" import { updateEntityMetadata } from "../utilities" import { MetadataTypes, WebhookType } from "../constants" -import { getProdAppID } from "@budibase/backend-core/db" +import { getProdAppID, getDB } from "@budibase/backend-core/db" import { cloneDeep } from "lodash/fp" import { getAppDB, getAppId } from "@budibase/backend-core/context" import { tenancy } from "@budibase/backend-core" @@ -20,13 +19,17 @@ export async function processEvent(job: any) { try { const tenantId = tenancy.getTenantIDFromAppID(job.data.event.appId) return await tenancy.doInTenant(tenantId, async () => { + console.log( + `${job.data.automation.appId} automation ${job.data.automation._id} running` + ) // need to actually await these so that an error can be captured properly const runFn = () => Runner.run(job) return quotas.addAutomation(runFn) }) } catch (err) { + const errJson = JSON.stringify(err) console.error( - `${job.data.automation.appId} automation ${job.data.automation._id} was unable to run - ${err}` + `${job.data.automation.appId} automation ${job.data.automation._id} was unable to run - ${errJson}` ) return { err } } @@ -108,7 +111,7 @@ export async function enableCronTrigger(appId: any, automation: any) { // can't use getAppDB here as this is likely to be called from dev app, // but this call could be for dev app or prod app, need to just use what // was passed in - const db = new CouchDB(appId) + const db = getDB(appId) const response = await db.put(automation) automation._id = response.id automation._rev = response.rev diff --git a/packages/server/src/db/client.js b/packages/server/src/db/client.js deleted file mode 100644 index 9e90163fff..0000000000 --- a/packages/server/src/db/client.js +++ /dev/null @@ -1,31 +0,0 @@ -const PouchDB = require("pouchdb") -const { getCouchUrl } = require("@budibase/backend-core/db") -const replicationStream = require("pouchdb-replication-stream") -const allDbs = require("pouchdb-all-dbs") -const find = require("pouchdb-find") -const env = require("../environment") - -const COUCH_DB_URL = getCouchUrl() || "http://localhost:4005" - -PouchDB.plugin(replicationStream.plugin) -PouchDB.plugin(find) -PouchDB.adapter("writableStream", replicationStream.adapters.writableStream) - -let POUCH_DB_DEFAULTS = { - prefix: COUCH_DB_URL, -} - -if (env.isTest()) { - PouchDB.plugin(require("pouchdb-adapter-memory")) - POUCH_DB_DEFAULTS = { - prefix: undefined, - adapter: "memory", - } -} - -const Pouch = PouchDB.defaults(POUCH_DB_DEFAULTS) - -// have to still have pouch alldbs for testing -allDbs(Pouch) - -module.exports = Pouch diff --git a/packages/server/src/db/inMemoryView.js b/packages/server/src/db/inMemoryView.js index 892617e068..df154485c2 100644 --- a/packages/server/src/db/inMemoryView.js +++ b/packages/server/src/db/inMemoryView.js @@ -1,12 +1,9 @@ -const PouchDB = require("pouchdb") -const memory = require("pouchdb-adapter-memory") const newid = require("./newid") -PouchDB.plugin(memory) -const Pouch = PouchDB.defaults({ - prefix: undefined, - adapter: "memory", -}) +// bypass the main application db config +// use in memory pouchdb directly +const { getPouch } = require("@budibase/backend-core/db") +const Pouch = getPouch({ inMemory: true }) exports.runView = async (view, calculation, group, data) => { // use a different ID each time for the DB, make sure they diff --git a/packages/server/src/db/index.js b/packages/server/src/db/index.js index f03b5b1fb0..75ad19b87f 100644 --- a/packages/server/src/db/index.js +++ b/packages/server/src/db/index.js @@ -1,3 +1,16 @@ -const client = require("./client") +const core = require("@budibase/backend-core") +const env = require("../environment") -module.exports = client +exports.init = () => { + const dbConfig = { + replication: true, + find: true, + } + + if (env.isTest()) { + dbConfig.inMemory = true + dbConfig.allDbs = true + } + + core.init({ db: dbConfig }) +} diff --git a/packages/server/src/db/tests/linkTests.spec.js b/packages/server/src/db/tests/linkTests.spec.js index 9a309df70a..aaa95febd4 100644 --- a/packages/server/src/db/tests/linkTests.spec.js +++ b/packages/server/src/db/tests/linkTests.spec.js @@ -1,8 +1,8 @@ const TestConfig = require("../../tests/utilities/TestConfiguration") const { basicTable } = require("../../tests/utilities/structures") const linkUtils = require("../linkedRows/linkUtils") -const CouchDB = require("../index") const { getAppDB } = require("@budibase/backend-core/context") +const { getDB } = require("@budibase/backend-core/db") describe("test link functionality", () => { const config = new TestConfig(false) @@ -48,7 +48,7 @@ describe("test link functionality", () => { describe("getLinkDocuments", () => { it("should create the link view when it doesn't exist", async () => { // create the DB and a very basic app design DB - const db = new CouchDB("test") + const db = getDB("test") await db.put({ _id: "_design/database", views: {} }) const output = await linkUtils.getLinkDocuments({ tableId: "test", diff --git a/packages/server/src/middleware/tests/authorized.spec.js b/packages/server/src/middleware/tests/authorized.spec.js index 9cfa9d368f..f461a6d907 100644 --- a/packages/server/src/middleware/tests/authorized.spec.js +++ b/packages/server/src/middleware/tests/authorized.spec.js @@ -10,7 +10,6 @@ jest.mock("../../environment", () => ({ const authorizedMiddleware = require("../authorized") const env = require("../../environment") const { PermissionTypes, PermissionLevels } = require("@budibase/backend-core/permissions") -require("@budibase/backend-core").init(require("../../db")) const { doInAppContext } = require("@budibase/backend-core/context") const APP_ID = "" diff --git a/packages/server/src/migrations/functions/tests/appUrls.spec.js b/packages/server/src/migrations/functions/tests/appUrls.spec.js index d3f080dfd4..fe87863e56 100644 --- a/packages/server/src/migrations/functions/tests/appUrls.spec.js +++ b/packages/server/src/migrations/functions/tests/appUrls.spec.js @@ -1,12 +1,10 @@ -const { DocumentTypes } = require("@budibase/backend-core/db") -const env = require("../../../environment") +const { DocumentTypes, getDB } = require("@budibase/backend-core/db") const TestConfig = require("../../../tests/utilities/TestConfiguration") const migration = require("../appUrls") describe("run", () => { let config = new TestConfig(false) - const CouchDB = config.getCouch() beforeEach(async () => { await config.init() @@ -16,7 +14,7 @@ describe("run", () => { it("runs successfully", async () => { const app = await config.createApp("testApp") - const appDb = new CouchDB(app.appId) + const appDb = getDB(app.appId) let metadata = await appDb.get(DocumentTypes.APP_METADATA) delete metadata.url await appDb.put(metadata) diff --git a/packages/server/src/migrations/index.ts b/packages/server/src/migrations/index.ts index d8383fcb39..6d63d81544 100644 --- a/packages/server/src/migrations/index.ts +++ b/packages/server/src/migrations/index.ts @@ -1,4 +1,3 @@ -import CouchDB from "../db" const { MIGRATION_TYPES, runMigrations, @@ -64,5 +63,5 @@ export const MIGRATIONS: Migration[] = [ ] export const migrate = async (options?: MigrationOptions) => { - await runMigrations(CouchDB, MIGRATIONS, options) + await runMigrations(MIGRATIONS, options) } diff --git a/packages/server/src/module.d.ts b/packages/server/src/module.d.ts index 0d1dc8d938..0419a40a14 100644 --- a/packages/server/src/module.d.ts +++ b/packages/server/src/module.d.ts @@ -5,3 +5,7 @@ declare module "@budibase/backend-core/context" declare module "@budibase/backend-core/cache" declare module "@budibase/backend-core/permissions" declare module "@budibase/backend-core/roles" +declare module "@budibase/backend-core/constants" +declare module "@budibase/backend-core/auth" +declare module "@budibase/backend-core/sessions" +declare module "@budibase/backend-core/encryption" diff --git a/packages/server/src/tests/utilities/TestConfiguration.js b/packages/server/src/tests/utilities/TestConfiguration.js index 1d280fdd4b..5d240d02d6 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.js +++ b/packages/server/src/tests/utilities/TestConfiguration.js @@ -1,6 +1,4 @@ -const core = require("@budibase/backend-core") -const CouchDB = require("../../db") -core.init(CouchDB) +require("../../db").init() const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles") const env = require("../../environment") const { @@ -57,10 +55,6 @@ class TestConfiguration { return this.appId } - getCouch() { - return CouchDB - } - async _req(config, params, controlFunc) { const request = {} // fake cookies, we don't need them diff --git a/packages/server/src/threads/utils.js b/packages/server/src/threads/utils.js index bf89791874..f56bd25c2d 100644 --- a/packages/server/src/threads/utils.js +++ b/packages/server/src/threads/utils.js @@ -1,6 +1,5 @@ const env = require("../environment") -const CouchDB = require("../db") -const { init } = require("@budibase/backend-core") +const db = require("../db") const redis = require("@budibase/backend-core/redis") const { SEPARATOR } = require("@budibase/backend-core/db") @@ -25,7 +24,7 @@ exports.threadSetup = () => { } // when thread starts, make sure it is recorded env.setInThread() - init(CouchDB) + db.init() } function makeVariableKey(queryId, variable) { diff --git a/packages/server/src/utilities/fileSystem/index.js b/packages/server/src/utilities/fileSystem/index.js index 904b4ced18..3822dbc751 100644 --- a/packages/server/src/utilities/fileSystem/index.js +++ b/packages/server/src/utilities/fileSystem/index.js @@ -2,7 +2,7 @@ const { budibaseTempDir } = require("../budibaseDir") const fs = require("fs") const { join } = require("path") const uuid = require("uuid/v4") -const CouchDB = require("../../db") +const { getDB } = require("@budibase/backend-core/db") const { ObjectStoreBuckets } = require("../../constants") const { upload, @@ -151,7 +151,7 @@ exports.streamBackup = async appId => { * @return {*} either a readable stream or a string */ exports.exportDB = async (dbName, { stream, filter, exportName } = {}) => { - const instanceDb = new CouchDB(dbName) + const instanceDb = getDB(dbName) // Stream the dump if required if (stream) { diff --git a/packages/server/src/utilities/usageQuota/rows.js b/packages/server/src/utilities/usageQuota/rows.js index 378caffc46..51e64eb930 100644 --- a/packages/server/src/utilities/usageQuota/rows.js +++ b/packages/server/src/utilities/usageQuota/rows.js @@ -1,6 +1,9 @@ const { getRowParams, USER_METDATA_PREFIX } = require("../../db/utils") -const CouchDB = require("../../db") -const { isDevAppID, getDevelopmentAppID } = require("@budibase/backend-core/db") +const { + isDevAppID, + getDevelopmentAppID, + getDB, +} = require("@budibase/backend-core/db") const ROW_EXCLUSIONS = [USER_METDATA_PREFIX] @@ -24,7 +27,7 @@ const getAppPairs = appIds => { const getAppRows = async appId => { // need to specify the app ID, as this is used for different apps in one call - const appDb = new CouchDB(appId) + const appDb = getDB(appId) const response = await appDb.allDocs( getRowParams(null, null, { include_docs: false, diff --git a/packages/worker/src/api/controllers/system/tenants.js b/packages/worker/src/api/controllers/system/tenants.js index 4d41ce894c..c310b3da62 100644 --- a/packages/worker/src/api/controllers/system/tenants.js +++ b/packages/worker/src/api/controllers/system/tenants.js @@ -1,11 +1,10 @@ -const CouchDB = require("../../../db") -const { StaticDatabases } = require("@budibase/backend-core/db") +const { StaticDatabases, getDB } = require("@budibase/backend-core/db") const { getTenantId } = require("@budibase/backend-core/tenancy") const { deleteTenant } = require("@budibase/backend-core/deprovision") exports.exists = async ctx => { const tenantId = ctx.request.params - const db = new CouchDB(StaticDatabases.PLATFORM_INFO.name) + const db = getDB(StaticDatabases.PLATFORM_INFO.name) let exists = false try { const tenantsDoc = await db.get(StaticDatabases.PLATFORM_INFO.docs.tenants) @@ -21,7 +20,7 @@ exports.exists = async ctx => { } exports.fetch = async ctx => { - const db = new CouchDB(StaticDatabases.PLATFORM_INFO.name) + const db = getDB(StaticDatabases.PLATFORM_INFO.name) let tenants = [] try { const tenantsDoc = await db.get(StaticDatabases.PLATFORM_INFO.docs.tenants) diff --git a/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js b/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js index 6b6c0e24b3..d297d818bd 100644 --- a/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js +++ b/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js @@ -1,3 +1,4 @@ +require("../../../../db").init() const env = require("../../../../environment") const controllers = require("./controllers") const supertest = require("supertest") @@ -8,15 +9,12 @@ const { getGlobalUserByEmail } = require("@budibase/backend-core/utils") const { createASession } = require("@budibase/backend-core/sessions") const { newid } = require("@budibase/backend-core/src/hashing") const { TENANT_ID, CSRF_TOKEN } = require("./structures") -const core = require("@budibase/backend-core") -const CouchDB = require("../../../../db") const { doInTenant } = require("@budibase/backend-core/tenancy") -core.init(CouchDB) class TestConfiguration { constructor(openServer = true) { if (openServer) { - env.PORT = 4003 + env.PORT = 4012 this.server = require("../../../../index") // we need the request for logging in, involves cookies, hard to fake this.request = supertest(this.server) diff --git a/packages/worker/src/db/index.js b/packages/worker/src/db/index.js index 770aabd95b..25dc02962b 100644 --- a/packages/worker/src/db/index.js +++ b/packages/worker/src/db/index.js @@ -1,26 +1,11 @@ -const PouchDB = require("pouchdb") -const allDbs = require("pouchdb-all-dbs") +const core = require("@budibase/backend-core") const env = require("../environment") -const { getCouchUrl } = require("@budibase/backend-core/db") -// level option is purely for testing (development) -const COUCH_DB_URL = getCouchUrl() || "http://localhost:4005" - -let POUCH_DB_DEFAULTS = { - prefix: COUCH_DB_URL, -} - -if (env.isTest()) { - PouchDB.plugin(require("pouchdb-adapter-memory")) - POUCH_DB_DEFAULTS = { - prefix: undefined, - adapter: "memory", +exports.init = () => { + const dbConfig = {} + if (env.isTest()) { + dbConfig.inMemory = true + dbConfig.allDbs = true } + core.init({ db: dbConfig }) } - -const Pouch = PouchDB.defaults(POUCH_DB_DEFAULTS) - -// have to still have pouch alldbs for testing -allDbs(Pouch) - -module.exports = Pouch diff --git a/packages/worker/src/index.ts b/packages/worker/src/index.ts index 923355374e..75c4020088 100644 --- a/packages/worker/src/index.ts +++ b/packages/worker/src/index.ts @@ -4,8 +4,8 @@ import { Event } from "@sentry/types/dist/event" import Application from "koa" const env = require("./environment") -const CouchDB = require("./db") -require("@budibase/backend-core").init(CouchDB) +import db from "./db" +db.init() const Koa = require("koa") const destroyable = require("server-destroy") const koaBody = require("koa-body") From 2a5df40ffac648cca94fb7104cd1e2bc6685969c Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 30 Mar 2022 14:24:04 +0100 Subject: [PATCH 04/75] Add createdAt and updatedAt to all new put requests --- packages/backend-core/src/db/index.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/backend-core/src/db/index.js b/packages/backend-core/src/db/index.js index cc5eaaf9c5..eb837dc254 100644 --- a/packages/backend-core/src/db/index.js +++ b/packages/backend-core/src/db/index.js @@ -6,9 +6,11 @@ let initialised = false const put = dbPut => async (doc, options = {}) => { - const response = await dbPut(doc, options) - // TODO: add created / updated - return response + if (!doc.createdAt) { + doc.createdAt = new Date().toISOString() + } + doc.updatedAt = new Date().toISOString() + return dbPut(doc, options) } const checkInitialised = () => { From 0aa141e7c6527d1308f5296bcbc435c91e21227e Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 30 Mar 2022 15:43:18 +0100 Subject: [PATCH 05/75] Add tests for base db config --- .../backend-core/src/db/tests/index.spec.js | 41 +++++++++++++++++++ .../passport/tests/third-party-common.spec.js | 5 +-- .../tests/__snapshots__/index.spec.js.snap | 6 ++- .../src/migrations/tests/index.spec.js | 6 +-- .../src/tests/utilities/TestConfiguration.js | 4 ++ .../tests/utilities/{dbConfig.js => db.js} | 1 + 6 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 packages/backend-core/src/db/tests/index.spec.js create mode 100644 packages/backend-core/src/tests/utilities/TestConfiguration.js rename packages/backend-core/src/tests/utilities/{dbConfig.js => db.js} (86%) diff --git a/packages/backend-core/src/db/tests/index.spec.js b/packages/backend-core/src/db/tests/index.spec.js new file mode 100644 index 0000000000..25cb7134b5 --- /dev/null +++ b/packages/backend-core/src/db/tests/index.spec.js @@ -0,0 +1,41 @@ +const { MOCK_DATE } = require("../../tests/utilities/TestConfiguration") +const { getDB, allDbs } = require("../") + +Date = jest.fn(() => MOCK_DATE) + +describe("db", () => { + + describe("getDB", () => { + it("returns a db", async () => { + const db = getDB("test") + expect(db).toBeDefined() + expect(db._adapter).toBe("memory") + expect(db.prefix).toBe("_pouch_") + expect(db.name).toBe("test") + }) + + it("uses the custom put function", async () => { + const db = getDB("test") + let doc = { _id: "test" } + await db.put(doc) + doc = await db.get(doc._id) + expect(doc.createdAt).toBe(MOCK_DATE.toISOString()) + expect(doc.updatedAt).toBe(MOCK_DATE.toISOString()) + await db.destroy() + }) + }) + + describe("allDbs", () => { + it("returns all dbs", async () => { + let all = await allDbs() + expect(all).toStrictEqual([]) + const db1 = getDB("test1") + await db1.put({ _id: "test1" }) + const db2 = getDB("test2") + await db2.put({ _id: "test2" }) + all = await allDbs() + expect(all.length).toBe(2) + }) + }) +}) + diff --git a/packages/backend-core/src/middleware/passport/tests/third-party-common.spec.js b/packages/backend-core/src/middleware/passport/tests/third-party-common.spec.js index 3a3c55bfa0..8caed931ea 100644 --- a/packages/backend-core/src/middleware/passport/tests/third-party-common.spec.js +++ b/packages/backend-core/src/middleware/passport/tests/third-party-common.spec.js @@ -1,7 +1,4 @@ -// Mock data - -require("../../../tests/utilities/dbConfig") - +require("../../../tests/utilities/TestConfiguration") const database = require("../../../db") const { authenticateThirdParty } = require("../third-party-common") const { data } = require("./utilities/mock-data") diff --git a/packages/backend-core/src/migrations/tests/__snapshots__/index.spec.js.snap b/packages/backend-core/src/migrations/tests/__snapshots__/index.spec.js.snap index 222c3b1228..532b5a32db 100644 --- a/packages/backend-core/src/migrations/tests/__snapshots__/index.spec.js.snap +++ b/packages/backend-core/src/migrations/tests/__snapshots__/index.spec.js.snap @@ -3,7 +3,9 @@ exports[`migrations should match snapshot 1`] = ` Object { "_id": "migrations", - "_rev": "1-6277abc4e3db950221768e5a2618a059", - "test": 1487076708000, + "_rev": "1-a32b0b708e59eeb006ed5e063cfeb36a", + "createdAt": "2020-01-01T00:00:00.000Z", + "test": 1577836800000, + "updatedAt": "2020-01-01T00:00:00.000Z", } `; diff --git a/packages/backend-core/src/migrations/tests/index.spec.js b/packages/backend-core/src/migrations/tests/index.spec.js index 0a7659e279..7ad5f3ad92 100644 --- a/packages/backend-core/src/migrations/tests/index.spec.js +++ b/packages/backend-core/src/migrations/tests/index.spec.js @@ -1,12 +1,12 @@ -require("../../tests/utilities/dbConfig") - +const { MOCK_DATE_TIMESTAMP, MOCK_DATE } = require("../../tests/utilities/TestConfiguration") const { runMigrations, getMigrationsDoc } = require("../index") const { getDB } = require("../../db") const { StaticDatabases, } = require("../../db/utils") -Date.now = jest.fn(() => 1487076708000) +Date = jest.fn(() => MOCK_DATE) +Date.now = jest.fn(() => MOCK_DATE_TIMESTAMP) let db describe("migrations", () => { diff --git a/packages/backend-core/src/tests/utilities/TestConfiguration.js b/packages/backend-core/src/tests/utilities/TestConfiguration.js new file mode 100644 index 0000000000..5b9784eb4b --- /dev/null +++ b/packages/backend-core/src/tests/utilities/TestConfiguration.js @@ -0,0 +1,4 @@ +require("./db") + +exports.MOCK_DATE = new Date("2020-01-01T00:00:00.000Z") +exports.MOCK_DATE_TIMESTAMP = 1577836800000 diff --git a/packages/backend-core/src/tests/utilities/dbConfig.js b/packages/backend-core/src/tests/utilities/db.js similarity index 86% rename from packages/backend-core/src/tests/utilities/dbConfig.js rename to packages/backend-core/src/tests/utilities/db.js index acd692df40..14173e5ee0 100644 --- a/packages/backend-core/src/tests/utilities/dbConfig.js +++ b/packages/backend-core/src/tests/utilities/db.js @@ -1,5 +1,6 @@ const core = require("../../index") const dbConfig = { inMemory: true, + allDbs: true, } core.init({ db: dbConfig }) From de7d57558d326a206fa50f8a264f7d720de97c16 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Fri, 1 Apr 2022 21:29:44 +0100 Subject: [PATCH 06/75] Add event domain --- .../backend-core/src/analytics/constants.js | 57 -- packages/backend-core/src/analytics/index.js | 6 - .../src/{ => events}/analytics/analytics.js | 8 +- .../src/events/analytics/index.js | 7 + .../src/{ => events}/analytics/posthog.js | 0 packages/backend-core/src/events/constants.js | 170 ++++++ packages/backend-core/src/events/events.js | 494 ++++++++++++++++++ packages/backend-core/src/events/index.js | 2 + packages/backend-core/src/index.js | 2 +- .../server/src/api/controllers/datasource.js | 2 + 10 files changed, 681 insertions(+), 67 deletions(-) delete mode 100644 packages/backend-core/src/analytics/constants.js delete mode 100644 packages/backend-core/src/analytics/index.js rename packages/backend-core/src/{ => events}/analytics/analytics.js (73%) create mode 100644 packages/backend-core/src/events/analytics/index.js rename packages/backend-core/src/{ => events}/analytics/posthog.js (100%) create mode 100644 packages/backend-core/src/events/constants.js create mode 100644 packages/backend-core/src/events/events.js create mode 100644 packages/backend-core/src/events/index.js diff --git a/packages/backend-core/src/analytics/constants.js b/packages/backend-core/src/analytics/constants.js deleted file mode 100644 index 4c5b69311f..0000000000 --- a/packages/backend-core/src/analytics/constants.js +++ /dev/null @@ -1,57 +0,0 @@ -const Events = { - // - BUILDER: { - STARTED: "Builder Started", - }, - COMPONENT: { - CREATED: "Added Component", - }, - DATASOURCE: { - CREATED: "Datasource Created", - UPDATED: "Datasource Updated", - }, - QUERIES: { - REST: "REST Queries Imported", - }, - TABLE: { - CREATED: "Table Created", - }, - VIEW: { - CREATED: "View Created", - ADDED_FILTER: "Added View Filter", - ADDED_CALCULATE: "Added View Calculate", - }, - SCREEN: { - CREATED: "Screen Created", - }, - AUTOMATION: { - CREATED: "Automation Created", - SAVED: "Automation Saved", - BLOCK_ADDED: "Added Automation Block", - }, - NPS: { - SUBMITTED: "budibase:feedback_submitted", - }, - APP: { - CREATED: "budibase:app_created", - PUBLISHED: "budibase:app_published", - UNPUBLISHED: "budibase:app_unpublished", - }, - ANALYTICS: { - OPT_IN: "budibase:analytics_opt_in", - OPT_OUT: "budibase:analytics_opt_out", - }, - USER: { - INVITE: "budibase:portal_user_invite", - }, - SMTP: { - SAVED: "budibase:smtp_saved", - }, - SSO: { - SAVED: "budibase:sso_saved", - }, -} - -module.exports = { - Events, -} diff --git a/packages/backend-core/src/analytics/index.js b/packages/backend-core/src/analytics/index.js deleted file mode 100644 index 9aa688fae0..0000000000 --- a/packages/backend-core/src/analytics/index.js +++ /dev/null @@ -1,6 +0,0 @@ -const Analytics = require("./analytics") -const { Events } = require("./constants") - -module.exports = new Analytics() -// exports = module.exports -exports.Events = Events diff --git a/packages/backend-core/src/analytics/analytics.js b/packages/backend-core/src/events/analytics/analytics.js similarity index 73% rename from packages/backend-core/src/analytics/analytics.js rename to packages/backend-core/src/events/analytics/analytics.js index 820309cea9..03a17b24a4 100644 --- a/packages/backend-core/src/analytics/analytics.js +++ b/packages/backend-core/src/events/analytics/analytics.js @@ -1,5 +1,5 @@ const PosthogClient = require("./posthog") -const env = require("../environment") +const env = require("../../environment") class Analytics { constructor() { @@ -19,9 +19,11 @@ class Analytics { this.posthog.updateUser(userId, properties) } - captureEvent(userId, eventName, props = {}) { + captureEvent(eventName, properties) { if (!this.isEnabled) return - this.posthog.capture(userId, eventName, props) + // TODO: get the user id from context + const userId = "TESTING_USER_ID" + this.posthog.capture(userId, eventName, properties) } shutdown() { diff --git a/packages/backend-core/src/events/analytics/index.js b/packages/backend-core/src/events/analytics/index.js new file mode 100644 index 0000000000..0e20ad7d80 --- /dev/null +++ b/packages/backend-core/src/events/analytics/index.js @@ -0,0 +1,7 @@ +const Analytics = require("./analytics") + +const analytics = new Analytics() + +module.exports = { + analytics, +} diff --git a/packages/backend-core/src/analytics/posthog.js b/packages/backend-core/src/events/analytics/posthog.js similarity index 100% rename from packages/backend-core/src/analytics/posthog.js rename to packages/backend-core/src/events/analytics/posthog.js diff --git a/packages/backend-core/src/events/constants.js b/packages/backend-core/src/events/constants.js new file mode 100644 index 0000000000..b991c163df --- /dev/null +++ b/packages/backend-core/src/events/constants.js @@ -0,0 +1,170 @@ +exports.Events = { + /** + ------------------ + USER + ------------------ + */ + + USER_CREATED: "user:created", + USER_UPDATED: "user:updated", + USER_DELETED: "user:deleted", + USER_PASSWORD_RESET: "user:password:reset", + + // PERMISSIONS + USER_PERMISSION_ADMIN_ASSIGNED: "user:permission:admin:assigned", + USER_PERMISSION_ADMIN_REMOVED: "user:permission:admin:removed", + USER_PERMISSION_BUILDER_ASSIGNED: "user:permission:builder:assigned", + USER_PERMISSION_BUILDER_REMOVED: "user:permission:builder:removed", + + // INVITE + USER_INVITED: "user:invited", + USER_INVITED_ACCEPTED: "user:invite:accepted", + + // SELF + USER_SELF_UPDATED: "user:self:updated", + USER_SELF_PASSWORD_UPDATED: "user:self:password:updated", + USER_PASSWORD_RESET_REQUESTED: "user:password:reset:requested", + + /** + ------------------ + ADMIN + ------------------ + */ + + // EMAIL + EMAIL_SMTP_CREATED: "email:smtp:created", + EMAIL_SMTP_UPDATED: "email:smtp:updated", + + // AUTH + AUTH_SSO_CREATED: "auth:sso:created", + AUTH_SSO_UPDATED: "auth:sso:updated", + AUTH_SSO_ACTIVATED: "auth:sso:activated", + AUTH_SSO_DEACTIVATED: "auth:sso:deactivated", + + // ORG + ORG_NAME_UPDATED: "org:info:name:updated", + ORG_LOGO_UPDATED: "org:info:logo:updated", + ORG_PLATFORM_URL_UPDATED: "org:platformurl:updated", + + // UPDATE + UPDATE_VERSION_CHECKED: "version:checked", + + // ANALYTICS + ANALYTICS_OPT_OUT: "analytics:opt:out", + + /** + ------------------ + APP + ------------------ + */ + + // APP + APP_CREATED: "app:created", + APP_UPDATED: "app:updated", + APP_DELETED: "app:deleted", + APP_PUBLISHED: "app:published", + APP_UNPUBLISHED: "app:unpublished", + APP_IMPORTED: "app:imported", + APP_VERSION_UPDATED: "app:version:updated", + APP_REVERTED: "app:reverted", + APP_EXPORTED: "app:exported", + + // ROLE + APP_ROLE_CREATED: "app:role:created", + APP_ROLE_DELETED: "app:role:deleted", + APP_ROLE_ASSIGNED: "app:role:assigned", + + // CLIENT + CLIENT_SERVED: "client:served", + + /** + ------------------ + DATA + ------------------ + */ + + // DATASOURCE + DATASOURCE_CREATED: "datasource:created", + DATASOURCE_UPDATED: "datasource:updated", + DATASOURCE_DELETED: "datasource:deleted", + + // QUERY + QUERY_CREATED: "query:created", + QUERY_UPDATED: "query:updated", + QUERY_DELETED: "query:deleted", + QUERY_IMPORTED: "query:imported", + QUERY_RUN: "query:run", + QUERY_PREVIEW: "query:preview", + + // TABLE + TABLE_CREATED: "table:created", + TABLE_UPDATED: "table:updated", + TABLE_DELETED: "table:deleted", + TABLE_EXPORTED: "table:exported", + TABLE_IMPORTED: "table:imported", + TABLE_DATA_IMPORTED: "table:data:imported", + TABLE_PERMISSION_UPDATED: "table:permission:updated", + + // VIEW + VIEW_CREATED: "view:created", + VIEW_UPDATED: "view:updated", + VIEW_DELETED: "view:deleted", + VIEW_EXPORTED: "view:exported", + VIEW_FILTER_CREATED: "view:filter:created", + VIEW_FILTER_DELETED: "view:filter:created", + VIEW_CALCULATION_CREATED: "view:calculation:created", + VIEW_CALCULATION_DELETED: "view:calculation:created", + + // ROW + ROW_CREATED: "row:created", + ROW_UPDATED: "row:updated", + ROW_DELETED: "row:deleted", + ROW_IMPORTED: "row:imported", + + /** + ------------------ + DESIGN + ------------------ + */ + + // BUILDER + BUILDER_SERVED: "builder:served", + + // COMPONENT + COMPONENT_CREATED: "component:created", + COMPONENT_DELETED: "component:deleted", + + // SCREEN + SCREEN_CREATED: "screen:created", + SCREEN_DELETED: "screen:deleted", + + // LAYOUT + LAYOUT_CREATED: "layout:created", + LAYOUT_DELETED: "layout:deleted", + + /** + ------------------ + AUTOMATE + ------------------ + */ + + AUTOMATION_CREATED: "automation:created", + AUTOMATION_DELETED: "automation:deleted", + AUTOMATION_TESTED: "automation:tested", + AUTOMATION_RUN: "automation:run", + AUTOMATION_STEP_CREATED: "automation:step:created", + AUTOMATION_STEP_DELETED: "automation:step:deleted", + + /** + ------------------ + MISC + ------------------ + */ + + // NPS + NPS_SUBMITTED: "nps:submitted", + + // AUTH + AUTH_LOGIN: "auth:login", + AUTH_LOGOUT: "auth:logout", +} diff --git a/packages/backend-core/src/events/events.js b/packages/backend-core/src/events/events.js new file mode 100644 index 0000000000..e8b54910a6 --- /dev/null +++ b/packages/backend-core/src/events/events.js @@ -0,0 +1,494 @@ +const { getTenantId } = require("../context") +const { analytics } = require("./analytics") +const { Events } = require("./constants") + +const logEvent = messsage => { + const tenantId = getTenantId() + const userId = getTenantId() // TODO + console.log(`[tenant=${tenantId}] [user=${userId}] ${messsage}`) +} + +const processEvent = (event, properties) => { + // logging + logEvent(event) + + // analytics + analytics.captureEvent(event, properties) +} + +/** +------------------ + USER +------------------ +*/ + +exports.userCreated = () => { + const properties = {} + processEvent(Events.USER_CREATED, properties) +} + +exports.userUpdated = () => { + const properties = {} + processEvent(Events.USER_UPDATED, properties) +} + +exports.userDeleted = () => { + const properties = {} + processEvent(Events.USER_DELETED, properties) +} + +exports.userForcePasswordReset = () => { + const properties = {} + processEvent(Events.USER_PASSWORD_RESET, properties) +} + +// PERMISSIONS + +exports.userPermissionAdminAssigned = () => { + const properties = {} + processEvent(Events.USER_PERMISSION_ADMIN_ASSIGNED, properties) +} + +exports.userPermissionAdminRemoved = () => { + const properties = {} + processEvent(Events.USER_PERMISSION_ADMIN_REMOVED, properties) +} + +exports.userPermissionBuilderAssigned = () => { + const properties = {} + processEvent(Events.USER_PERMISSION_BUILDER_ASSIGNED, properties) +} + +exports.userPermissionBuilderRemoved = () => { + const properties = {} + processEvent(Events.USER_PERMISSION_BUILDER_REMOVED, properties) +} + +// INVITE + +exports.userInvited = () => { + const properties = {} + processEvent(Events.USER_INVITED, properties) +} + +exports.userInviteAccepted = () => { + const properties = {} + processEvent(Events.USER_INVITED_ACCEPTED, properties) +} + +// SELF + +exports.userSelfUpdated = () => { + const properties = {} + processEvent(Events.USER_SELF_UPDATED, properties) +} + +exports.userSelfPasswordUpdated = () => { + const properties = {} + processEvent(Events.USER_SELF_PASSWORD_UPDATED, properties) +} + +exports.userPasswordResetRequested = () => { + const properties = {} + processEvent(Events.USER_PASSWORD_RESET_REQUESTED, properties) +} + +/** + ------------------ + ADMIN + ------------------ +*/ + +// EMAIL + +exports.emailSMTPCreated = () => { + const properties = {} + processEvent(Events.EMAIL_SMTP_CREATED, properties) +} + +exports.emailSMTPUpdated = () => { + const properties = {} + processEvent(Events.EMAIL_SMTP_UPDATED, properties) +} + +// AUTH + +exports.authSSOCreated = () => { + const properties = {} + processEvent(Events.AUTH_SSO_CREATED, properties) +} + +exports.authSSOUpdated = () => { + const properties = {} + processEvent(Events.AUTH_SSO_UPDATED, properties) +} + +exports.authSSOActivated = () => { + const properties = {} + processEvent(Events.AUTH_SSO_ACTIVATED, properties) +} + +exports.authSSODeactivated = () => { + const properties = {} + processEvent(Events.AUTH_SSO_DEACTIVATED, properties) +} + +// ORG + +exports.orgNameUpdated = () => { + const properties = {} + processEvent(Events.ORG_NAME_UPDATED, properties) +} + +exports.orgLogoUpdated = () => { + const properties = {} + processEvent(Events.ORG_LOGO_UPDATED, properties) +} + +exports.orgPlatformURLUpdated = () => { + const properties = {} + processEvent(Events.ORG_PLATFORM_URL_UPDATED, properties) +} + +// UPDATE + +exports.updateVersionChecked = () => { + const properties = {} + processEvent(Events.UPDATE_VERSION_CHECKED, properties) +} + +// ANALYTICS + +exports.analyticsOptOut = () => { + const properties = {} + processEvent(Events.ANALYTICS_OPT_OUT, properties) +} + +/** + ------------------ + APP + ------------------ +*/ + +// APP + +exports.appCreated = () => { + const properties = {} + processEvent(Events.APP_CREATED, properties) +} + +exports.appUpdated = () => { + const properties = {} + processEvent(Events.APP_UPDATED, properties) +} + +exports.appDeleted = () => { + const properties = {} + processEvent(Events.APP_DELETED, properties) +} + +exports.appPublished = () => { + const properties = {} + processEvent(Events.APP_PUBLISHED, properties) +} + +exports.appUnpublished = () => { + const properties = {} + processEvent(Events.APP_UNPUBLISHED, properties) +} + +exports.appImported = () => { + const properties = {} + processEvent(Events.APP_IMPORTED, properties) +} + +exports.appVersionUpdated = () => { + const properties = {} + processEvent(Events.APP_VERSION_UPDATED, properties) +} + +exports.appReverted = () => { + const properties = {} + processEvent(Events.APP_REVERTED, properties) +} + +exports.appExported = () => { + const properties = {} + processEvent(Events.APP_EXPORTED, properties) +} + +// ROLE + +exports.appRoleCreated = () => { + const properties = {} + processEvent(Events.APP_ROLE_CREATED, properties) +} + +exports.appRoleDeleted = () => { + const properties = {} + processEvent(Events.APP_ROLE_DELETED, properties) +} + +exports.appRoleAssigned = () => { + const properties = {} + processEvent(Events.APP_ROLE_ASSIGNED, properties) +} + +// CLIENT + +exports.clientServed = () => { + const properties = {} + processEvent(Events.CLIENT_SERVED, properties) +} + +/** + ------------------ + DATA + ------------------ +*/ + +// DATASOURCE + +exports.datasourceCreated = () => { + const properties = {} + processEvent(Events.DATASOURCE_CREATED, properties) +} + +exports.datasourceUpdated = () => { + const properties = {} + processEvent(Events.DATASOURCE_UPDATED, properties) +} + +exports.datasourceDeleted = () => { + const properties = {} + processEvent(Events.DATASOURCE_DELETED, properties) +} + +// QUERY + +exports.queryCreated = () => { + const properties = {} + processEvent(Events.QUERY_CREATED, properties) +} + +exports.queryUpdated = () => { + const properties = {} + processEvent(Events.QUERY_UPDATED, properties) +} + +exports.queryDeleted = () => { + const properties = {} + processEvent(Events.QUERY_DELETED, properties) +} + +exports.queryImported = () => { + const properties = {} + processEvent(Events.QUERY_DELETED, properties) +} + +exports.queryRun = () => { + const properties = {} + processEvent(Events.QUERY_DELETED, properties) +} + +exports.queryPreview = () => { + const properties = {} + processEvent(Events.QUERY_DELETED, properties) +} + +// TABLE + +exports.tableCreated = () => { + const properties = {} + processEvent(Events.TABLE_CREATED, properties) +} + +exports.tableUpdated = () => { + const properties = {} + processEvent(Events.TABLE_UPDATED, properties) +} + +exports.tableDeleted = () => { + const properties = {} + processEvent(Events.TABLE_DELETED, properties) +} + +exports.tableExported = () => { + const properties = {} + processEvent(Events.TABLE_EXPORTED, properties) +} + +exports.tableImported = () => { + const properties = {} + processEvent(Events.TABLE_IMPORTED, properties) +} + +exports.tablePermissionUpdated = () => { + const properties = {} + processEvent(Events.TABLE_PERMISSION_UPDATED, properties) +} + +// VIEW + +exports.viewCreated = () => { + const properties = {} + processEvent(Events.VIEW_CREATED, properties) +} + +exports.viewUpdated = () => { + const properties = {} + processEvent(Events.VIEW_UPDATED, properties) +} + +exports.viewDeleted = () => { + const properties = {} + processEvent(Events.VIEW_DELETED, properties) +} + +exports.viewExported = () => { + const properties = {} + processEvent(Events.VIEW_EXPORTED, properties) +} + +exports.viewFilterCreated = () => { + const properties = {} + processEvent(Events.VIEW_FILTER_CREATED, properties) +} + +exports.viewFilterDeleted = () => { + const properties = {} + processEvent(Events.VIEW_FILTER_DELETED, properties) +} + +exports.viewCalculationCreated = () => { + const properties = {} + processEvent(Events.VIEW_CALCULATION_CREATED, properties) +} + +exports.viewCalculationDeleted = () => { + const properties = {} + processEvent(Events.VIEW_CALCULATION_DELETED, properties) +} + +// ROW + +exports.rowCreated = () => { + const properties = {} + processEvent(Events.ROW_CREATED, properties) +} + +exports.rowImported = () => { + const properties = {} + processEvent(Events.ROW_IMPORTED, properties) + exports.rowCreated() +} + +exports.rowUpdated = () => { + const properties = {} + processEvent(Events.ROW_UPDATED, properties) +} + +exports.rowDeleted = () => { + const properties = {} + processEvent(Events.ROW_DELETED, properties) +} + +/** + ------------------ + DESIGN + ------------------ +*/ + +// BUILDER + +exports.builderServed = () => { + const properties = {} + processEvent(Events.BUILDER_SERVED, properties) +} + +// COMPONENTS - are captured in the UI only + +// SCREEN + +exports.screenCreated = () => { + const properties = {} + processEvent(Events.SCREEN_CREATED, properties) +} + +exports.screenDeleted = () => { + const properties = {} + processEvent(Events.SCREEN_DELETED, properties) +} + +// LAYOUT + +exports.layoutCreated = () => { + const properties = {} + processEvent(Events.LAYOUT_CREATED, properties) +} + +exports.layoutDeleted = () => { + const properties = {} + processEvent(Events.LAYOUT_DELETED, properties) +} + +/** + ------------------ + AUTOMATE + ------------------ +*/ + +exports.automationCreated = () => { + const properties = {} + processEvent(Events.AUTOMATION_CREATED, properties) +} + +exports.automationDeleted = () => { + const properties = {} + processEvent(Events.AUTOMATION_DELETED, properties) +} + +exports.automationTested = () => { + const properties = {} + processEvent(Events.AUTOMATION_TESTED, properties) +} + +exports.automationRun = () => { + const properties = {} + processEvent(Events.AUTOMATION_RUN, properties) +} + +exports.automationStepCreated = () => { + const properties = {} + processEvent(Events.AUTOMATION_STEP_CREATED, properties) +} + +exports.automationStepDeleted = () => { + const properties = {} + processEvent(Events.AUTOMATION_STEP_DELETED, properties) +} + +/** + ------------------ + MISC + ------------------ +*/ + +// NPS + +exports.npsSubmitted = () => { + const properties = {} + processEvent(Events.NPS_SUBMITTED, properties) +} + +// AUTH + +exports.login = () => { + const properties = {} + processEvent(Events.AUTH_LOGIN, properties) +} + +exports.logout = () => { + const properties = {} + processEvent(Events.AUTH_LOGOUT, properties) +} diff --git a/packages/backend-core/src/events/index.js b/packages/backend-core/src/events/index.js new file mode 100644 index 0000000000..e91cc25703 --- /dev/null +++ b/packages/backend-core/src/events/index.js @@ -0,0 +1,2 @@ +const events = require("./events") +module.exports = events diff --git a/packages/backend-core/src/index.js b/packages/backend-core/src/index.js index f403b44a3d..2e5249a653 100644 --- a/packages/backend-core/src/index.js +++ b/packages/backend-core/src/index.js @@ -20,5 +20,5 @@ module.exports = { accounts: require("./cloud/accounts"), tenancy: require("./tenancy"), featureFlags: require("./featureFlags"), - analytics: require("./analytics"), + events: require("./events"), } diff --git a/packages/server/src/api/controllers/datasource.js b/packages/server/src/api/controllers/datasource.js index 999f322563..1117d15841 100644 --- a/packages/server/src/api/controllers/datasource.js +++ b/packages/server/src/api/controllers/datasource.js @@ -11,6 +11,7 @@ const { integrations } = require("../../integrations") const { getDatasourceAndQuery } = require("./row/utils") const { invalidateDynamicVariables } = require("../../threads/utils") const { getAppDB } = require("@budibase/backend-core/context") +const { events } = require("@budibase/backend-core") exports.fetch = async function (ctx) { // Get internal tables @@ -142,6 +143,7 @@ exports.save = async function (ctx) { } const dbResp = await db.put(datasource) + events.datasourceCreated(datasource) datasource._rev = dbResp.rev // Drain connection pools when configuration is changed From d0af6cc8cacc2b88a4d01d63dbe0f69923366886 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Mon, 4 Apr 2022 15:59:00 +0100 Subject: [PATCH 07/75] App events --- .../src/{events => }/analytics/analytics.js | 2 +- .../src/{events => }/analytics/index.js | 5 +- .../src/{events => }/analytics/posthog.js | 0 packages/backend-core/src/events/constants.js | 92 +--- packages/backend-core/src/events/events.js | 482 +----------------- .../backend-core/src/events/handlers/app.js | 60 +++ .../backend-core/src/events/handlers/auth.js | 40 ++ .../src/events/handlers/automation.js | 37 ++ .../src/events/handlers/datasource.js | 19 + .../backend-core/src/events/handlers/email.js | 12 + .../backend-core/src/events/handlers/index.js | 31 ++ .../src/events/handlers/layout.js | 16 + .../src/events/handlers/licensing.js | 10 + .../backend-core/src/events/handlers/org.js | 34 ++ .../backend-core/src/events/handlers/query.js | 37 ++ .../backend-core/src/events/handlers/role.js | 19 + .../backend-core/src/events/handlers/row.js | 26 + .../src/events/handlers/screen.js | 14 + .../backend-core/src/events/handlers/serve.js | 14 + .../backend-core/src/events/handlers/table.js | 37 ++ .../backend-core/src/events/handlers/user.js | 85 +++ .../backend-core/src/events/handlers/view.js | 49 ++ packages/backend-core/src/events/index.js | 7 +- packages/backend-core/src/index.js | 1 + .../server/src/api/controllers/application.ts | 33 +- packages/server/src/api/controllers/backup.js | 2 + .../src/api/controllers/deploy/index.ts | 2 + packages/server/src/api/controllers/dev.js | 11 +- .../src/api/routes/tests/application.spec.js | 63 ++- .../src/api/routes/tests/backup.spec.js | 1 + .../src/api/routes/tests/data/export.txt | 1 + .../src/api/routes/tests/deployment.spec.js | 24 + .../server/src/api/routes/tests/dev.spec.js | 24 + .../src/tests/utilities/TestConfiguration.js | 9 + .../server/src/tests/utilities/mockEvents.js | 22 + 35 files changed, 765 insertions(+), 556 deletions(-) rename packages/backend-core/src/{events => }/analytics/analytics.js (94%) rename packages/backend-core/src/{events => }/analytics/index.js (68%) rename packages/backend-core/src/{events => }/analytics/posthog.js (100%) create mode 100644 packages/backend-core/src/events/handlers/app.js create mode 100644 packages/backend-core/src/events/handlers/auth.js create mode 100644 packages/backend-core/src/events/handlers/automation.js create mode 100644 packages/backend-core/src/events/handlers/datasource.js create mode 100644 packages/backend-core/src/events/handlers/email.js create mode 100644 packages/backend-core/src/events/handlers/index.js create mode 100644 packages/backend-core/src/events/handlers/layout.js create mode 100644 packages/backend-core/src/events/handlers/licensing.js create mode 100644 packages/backend-core/src/events/handlers/org.js create mode 100644 packages/backend-core/src/events/handlers/query.js create mode 100644 packages/backend-core/src/events/handlers/role.js create mode 100644 packages/backend-core/src/events/handlers/row.js create mode 100644 packages/backend-core/src/events/handlers/screen.js create mode 100644 packages/backend-core/src/events/handlers/serve.js create mode 100644 packages/backend-core/src/events/handlers/table.js create mode 100644 packages/backend-core/src/events/handlers/user.js create mode 100644 packages/backend-core/src/events/handlers/view.js create mode 100644 packages/server/src/api/routes/tests/data/export.txt create mode 100644 packages/server/src/api/routes/tests/deployment.spec.js create mode 100644 packages/server/src/api/routes/tests/dev.spec.js create mode 100644 packages/server/src/tests/utilities/mockEvents.js diff --git a/packages/backend-core/src/events/analytics/analytics.js b/packages/backend-core/src/analytics/analytics.js similarity index 94% rename from packages/backend-core/src/events/analytics/analytics.js rename to packages/backend-core/src/analytics/analytics.js index 03a17b24a4..eb8688de51 100644 --- a/packages/backend-core/src/events/analytics/analytics.js +++ b/packages/backend-core/src/analytics/analytics.js @@ -1,5 +1,5 @@ const PosthogClient = require("./posthog") -const env = require("../../environment") +const env = require("../environment") class Analytics { constructor() { diff --git a/packages/backend-core/src/events/analytics/index.js b/packages/backend-core/src/analytics/index.js similarity index 68% rename from packages/backend-core/src/events/analytics/index.js rename to packages/backend-core/src/analytics/index.js index 0e20ad7d80..5a097552e9 100644 --- a/packages/backend-core/src/events/analytics/index.js +++ b/packages/backend-core/src/analytics/index.js @@ -1,7 +1,4 @@ const Analytics = require("./analytics") const analytics = new Analytics() - -module.exports = { - analytics, -} +module.exports = analytics diff --git a/packages/backend-core/src/events/analytics/posthog.js b/packages/backend-core/src/analytics/posthog.js similarity index 100% rename from packages/backend-core/src/events/analytics/posthog.js rename to packages/backend-core/src/analytics/posthog.js diff --git a/packages/backend-core/src/events/constants.js b/packages/backend-core/src/events/constants.js index b991c163df..63d601d0d1 100644 --- a/packages/backend-core/src/events/constants.js +++ b/packages/backend-core/src/events/constants.js @@ -1,35 +1,25 @@ exports.Events = { - /** - ------------------ - USER - ------------------ - */ - + // USER USER_CREATED: "user:created", USER_UPDATED: "user:updated", USER_DELETED: "user:deleted", - USER_PASSWORD_RESET: "user:password:reset", + USER_PASSWORD_FORCE_RESET: "user:password:force:reset", - // PERMISSIONS - USER_PERMISSION_ADMIN_ASSIGNED: "user:permission:admin:assigned", - USER_PERMISSION_ADMIN_REMOVED: "user:permission:admin:removed", - USER_PERMISSION_BUILDER_ASSIGNED: "user:permission:builder:assigned", - USER_PERMISSION_BUILDER_REMOVED: "user:permission:builder:removed", + // USER / PERMISSIONS + USER_PERMISSION_ADMIN_ASSIGNED: "user:admin:assigned", + USER_PERMISSION_ADMIN_REMOVED: "user:admin:removed", + USER_PERMISSION_BUILDER_ASSIGNED: "user:builder:assigned", + USER_PERMISSION_BUILDER_REMOVED: "userbuilder:removed", - // INVITE + // USER / INVITE USER_INVITED: "user:invited", USER_INVITED_ACCEPTED: "user:invite:accepted", - // SELF + // USER / SELF USER_SELF_UPDATED: "user:self:updated", USER_SELF_PASSWORD_UPDATED: "user:self:password:updated", USER_PASSWORD_RESET_REQUESTED: "user:password:reset:requested", - - /** - ------------------ - ADMIN - ------------------ - */ + USER_PASSWORD_RESET: "user:password:reset", // EMAIL EMAIL_SMTP_CREATED: "email:smtp:created", @@ -40,49 +30,44 @@ exports.Events = { AUTH_SSO_UPDATED: "auth:sso:updated", AUTH_SSO_ACTIVATED: "auth:sso:activated", AUTH_SSO_DEACTIVATED: "auth:sso:deactivated", + AUTH_LOGIN: "auth:login", + AUTH_LOGOUT: "auth:logout", // ORG ORG_NAME_UPDATED: "org:info:name:updated", ORG_LOGO_UPDATED: "org:info:logo:updated", ORG_PLATFORM_URL_UPDATED: "org:platformurl:updated", - // UPDATE + // ORG / NPS + NPS_SUBMITTED: "nps:submitted", + + // ORG / UPDATE UPDATE_VERSION_CHECKED: "version:checked", - // ANALYTICS + // ORG / ANALYTICS ANALYTICS_OPT_OUT: "analytics:opt:out", - /** - ------------------ - APP - ------------------ - */ - // APP APP_CREATED: "app:created", APP_UPDATED: "app:updated", APP_DELETED: "app:deleted", APP_PUBLISHED: "app:published", APP_UNPUBLISHED: "app:unpublished", - APP_IMPORTED: "app:imported", + APP_TEMPLATE_IMPORTED: "app:template:imported", + APP_FILE_IMPORTED: "app:file:imported", APP_VERSION_UPDATED: "app:version:updated", + APP_VERSION_REVERTED: "app:version:reverted", APP_REVERTED: "app:reverted", APP_EXPORTED: "app:exported", // ROLE - APP_ROLE_CREATED: "app:role:created", - APP_ROLE_DELETED: "app:role:deleted", - APP_ROLE_ASSIGNED: "app:role:assigned", + ROLE_CREATED: "role:created", + ROLE_DELETED: "role:deleted", + ROLE_ASSIGNED: "role:assigned", - // CLIENT + // APP / CLIENT CLIENT_SERVED: "client:served", - /** - ------------------ - DATA - ------------------ - */ - // DATASOURCE DATASOURCE_CREATED: "datasource:created", DATASOURCE_UPDATED: "datasource:updated", @@ -94,7 +79,7 @@ exports.Events = { QUERY_DELETED: "query:deleted", QUERY_IMPORTED: "query:imported", QUERY_RUN: "query:run", - QUERY_PREVIEW: "query:preview", + QUERY_PREVIEWED: "query:previewed", // TABLE TABLE_CREATED: "table:created", @@ -121,12 +106,6 @@ exports.Events = { ROW_DELETED: "row:deleted", ROW_IMPORTED: "row:imported", - /** - ------------------ - DESIGN - ------------------ - */ - // BUILDER BUILDER_SERVED: "builder:served", @@ -142,12 +121,7 @@ exports.Events = { LAYOUT_CREATED: "layout:created", LAYOUT_DELETED: "layout:deleted", - /** - ------------------ - AUTOMATE - ------------------ - */ - + // AUTOMATION AUTOMATION_CREATED: "automation:created", AUTOMATION_DELETED: "automation:deleted", AUTOMATION_TESTED: "automation:tested", @@ -155,16 +129,6 @@ exports.Events = { AUTOMATION_STEP_CREATED: "automation:step:created", AUTOMATION_STEP_DELETED: "automation:step:deleted", - /** - ------------------ - MISC - ------------------ - */ - - // NPS - NPS_SUBMITTED: "nps:submitted", - - // AUTH - AUTH_LOGIN: "auth:login", - AUTH_LOGOUT: "auth:logout", + // LICENSING + LICENSING_QUOTA_EXCEEDED: "licensing:quota:exceeded", } diff --git a/packages/backend-core/src/events/events.js b/packages/backend-core/src/events/events.js index e8b54910a6..8a51c602c7 100644 --- a/packages/backend-core/src/events/events.js +++ b/packages/backend-core/src/events/events.js @@ -1,6 +1,5 @@ const { getTenantId } = require("../context") -const { analytics } = require("./analytics") -const { Events } = require("./constants") +const analytics = require("../analytics") const logEvent = messsage => { const tenantId = getTenantId() @@ -8,487 +7,10 @@ const logEvent = messsage => { console.log(`[tenant=${tenantId}] [user=${userId}] ${messsage}`) } -const processEvent = (event, properties) => { +exports.processEvent = (event, properties) => { // logging logEvent(event) // analytics analytics.captureEvent(event, properties) } - -/** ------------------- - USER ------------------- -*/ - -exports.userCreated = () => { - const properties = {} - processEvent(Events.USER_CREATED, properties) -} - -exports.userUpdated = () => { - const properties = {} - processEvent(Events.USER_UPDATED, properties) -} - -exports.userDeleted = () => { - const properties = {} - processEvent(Events.USER_DELETED, properties) -} - -exports.userForcePasswordReset = () => { - const properties = {} - processEvent(Events.USER_PASSWORD_RESET, properties) -} - -// PERMISSIONS - -exports.userPermissionAdminAssigned = () => { - const properties = {} - processEvent(Events.USER_PERMISSION_ADMIN_ASSIGNED, properties) -} - -exports.userPermissionAdminRemoved = () => { - const properties = {} - processEvent(Events.USER_PERMISSION_ADMIN_REMOVED, properties) -} - -exports.userPermissionBuilderAssigned = () => { - const properties = {} - processEvent(Events.USER_PERMISSION_BUILDER_ASSIGNED, properties) -} - -exports.userPermissionBuilderRemoved = () => { - const properties = {} - processEvent(Events.USER_PERMISSION_BUILDER_REMOVED, properties) -} - -// INVITE - -exports.userInvited = () => { - const properties = {} - processEvent(Events.USER_INVITED, properties) -} - -exports.userInviteAccepted = () => { - const properties = {} - processEvent(Events.USER_INVITED_ACCEPTED, properties) -} - -// SELF - -exports.userSelfUpdated = () => { - const properties = {} - processEvent(Events.USER_SELF_UPDATED, properties) -} - -exports.userSelfPasswordUpdated = () => { - const properties = {} - processEvent(Events.USER_SELF_PASSWORD_UPDATED, properties) -} - -exports.userPasswordResetRequested = () => { - const properties = {} - processEvent(Events.USER_PASSWORD_RESET_REQUESTED, properties) -} - -/** - ------------------ - ADMIN - ------------------ -*/ - -// EMAIL - -exports.emailSMTPCreated = () => { - const properties = {} - processEvent(Events.EMAIL_SMTP_CREATED, properties) -} - -exports.emailSMTPUpdated = () => { - const properties = {} - processEvent(Events.EMAIL_SMTP_UPDATED, properties) -} - -// AUTH - -exports.authSSOCreated = () => { - const properties = {} - processEvent(Events.AUTH_SSO_CREATED, properties) -} - -exports.authSSOUpdated = () => { - const properties = {} - processEvent(Events.AUTH_SSO_UPDATED, properties) -} - -exports.authSSOActivated = () => { - const properties = {} - processEvent(Events.AUTH_SSO_ACTIVATED, properties) -} - -exports.authSSODeactivated = () => { - const properties = {} - processEvent(Events.AUTH_SSO_DEACTIVATED, properties) -} - -// ORG - -exports.orgNameUpdated = () => { - const properties = {} - processEvent(Events.ORG_NAME_UPDATED, properties) -} - -exports.orgLogoUpdated = () => { - const properties = {} - processEvent(Events.ORG_LOGO_UPDATED, properties) -} - -exports.orgPlatformURLUpdated = () => { - const properties = {} - processEvent(Events.ORG_PLATFORM_URL_UPDATED, properties) -} - -// UPDATE - -exports.updateVersionChecked = () => { - const properties = {} - processEvent(Events.UPDATE_VERSION_CHECKED, properties) -} - -// ANALYTICS - -exports.analyticsOptOut = () => { - const properties = {} - processEvent(Events.ANALYTICS_OPT_OUT, properties) -} - -/** - ------------------ - APP - ------------------ -*/ - -// APP - -exports.appCreated = () => { - const properties = {} - processEvent(Events.APP_CREATED, properties) -} - -exports.appUpdated = () => { - const properties = {} - processEvent(Events.APP_UPDATED, properties) -} - -exports.appDeleted = () => { - const properties = {} - processEvent(Events.APP_DELETED, properties) -} - -exports.appPublished = () => { - const properties = {} - processEvent(Events.APP_PUBLISHED, properties) -} - -exports.appUnpublished = () => { - const properties = {} - processEvent(Events.APP_UNPUBLISHED, properties) -} - -exports.appImported = () => { - const properties = {} - processEvent(Events.APP_IMPORTED, properties) -} - -exports.appVersionUpdated = () => { - const properties = {} - processEvent(Events.APP_VERSION_UPDATED, properties) -} - -exports.appReverted = () => { - const properties = {} - processEvent(Events.APP_REVERTED, properties) -} - -exports.appExported = () => { - const properties = {} - processEvent(Events.APP_EXPORTED, properties) -} - -// ROLE - -exports.appRoleCreated = () => { - const properties = {} - processEvent(Events.APP_ROLE_CREATED, properties) -} - -exports.appRoleDeleted = () => { - const properties = {} - processEvent(Events.APP_ROLE_DELETED, properties) -} - -exports.appRoleAssigned = () => { - const properties = {} - processEvent(Events.APP_ROLE_ASSIGNED, properties) -} - -// CLIENT - -exports.clientServed = () => { - const properties = {} - processEvent(Events.CLIENT_SERVED, properties) -} - -/** - ------------------ - DATA - ------------------ -*/ - -// DATASOURCE - -exports.datasourceCreated = () => { - const properties = {} - processEvent(Events.DATASOURCE_CREATED, properties) -} - -exports.datasourceUpdated = () => { - const properties = {} - processEvent(Events.DATASOURCE_UPDATED, properties) -} - -exports.datasourceDeleted = () => { - const properties = {} - processEvent(Events.DATASOURCE_DELETED, properties) -} - -// QUERY - -exports.queryCreated = () => { - const properties = {} - processEvent(Events.QUERY_CREATED, properties) -} - -exports.queryUpdated = () => { - const properties = {} - processEvent(Events.QUERY_UPDATED, properties) -} - -exports.queryDeleted = () => { - const properties = {} - processEvent(Events.QUERY_DELETED, properties) -} - -exports.queryImported = () => { - const properties = {} - processEvent(Events.QUERY_DELETED, properties) -} - -exports.queryRun = () => { - const properties = {} - processEvent(Events.QUERY_DELETED, properties) -} - -exports.queryPreview = () => { - const properties = {} - processEvent(Events.QUERY_DELETED, properties) -} - -// TABLE - -exports.tableCreated = () => { - const properties = {} - processEvent(Events.TABLE_CREATED, properties) -} - -exports.tableUpdated = () => { - const properties = {} - processEvent(Events.TABLE_UPDATED, properties) -} - -exports.tableDeleted = () => { - const properties = {} - processEvent(Events.TABLE_DELETED, properties) -} - -exports.tableExported = () => { - const properties = {} - processEvent(Events.TABLE_EXPORTED, properties) -} - -exports.tableImported = () => { - const properties = {} - processEvent(Events.TABLE_IMPORTED, properties) -} - -exports.tablePermissionUpdated = () => { - const properties = {} - processEvent(Events.TABLE_PERMISSION_UPDATED, properties) -} - -// VIEW - -exports.viewCreated = () => { - const properties = {} - processEvent(Events.VIEW_CREATED, properties) -} - -exports.viewUpdated = () => { - const properties = {} - processEvent(Events.VIEW_UPDATED, properties) -} - -exports.viewDeleted = () => { - const properties = {} - processEvent(Events.VIEW_DELETED, properties) -} - -exports.viewExported = () => { - const properties = {} - processEvent(Events.VIEW_EXPORTED, properties) -} - -exports.viewFilterCreated = () => { - const properties = {} - processEvent(Events.VIEW_FILTER_CREATED, properties) -} - -exports.viewFilterDeleted = () => { - const properties = {} - processEvent(Events.VIEW_FILTER_DELETED, properties) -} - -exports.viewCalculationCreated = () => { - const properties = {} - processEvent(Events.VIEW_CALCULATION_CREATED, properties) -} - -exports.viewCalculationDeleted = () => { - const properties = {} - processEvent(Events.VIEW_CALCULATION_DELETED, properties) -} - -// ROW - -exports.rowCreated = () => { - const properties = {} - processEvent(Events.ROW_CREATED, properties) -} - -exports.rowImported = () => { - const properties = {} - processEvent(Events.ROW_IMPORTED, properties) - exports.rowCreated() -} - -exports.rowUpdated = () => { - const properties = {} - processEvent(Events.ROW_UPDATED, properties) -} - -exports.rowDeleted = () => { - const properties = {} - processEvent(Events.ROW_DELETED, properties) -} - -/** - ------------------ - DESIGN - ------------------ -*/ - -// BUILDER - -exports.builderServed = () => { - const properties = {} - processEvent(Events.BUILDER_SERVED, properties) -} - -// COMPONENTS - are captured in the UI only - -// SCREEN - -exports.screenCreated = () => { - const properties = {} - processEvent(Events.SCREEN_CREATED, properties) -} - -exports.screenDeleted = () => { - const properties = {} - processEvent(Events.SCREEN_DELETED, properties) -} - -// LAYOUT - -exports.layoutCreated = () => { - const properties = {} - processEvent(Events.LAYOUT_CREATED, properties) -} - -exports.layoutDeleted = () => { - const properties = {} - processEvent(Events.LAYOUT_DELETED, properties) -} - -/** - ------------------ - AUTOMATE - ------------------ -*/ - -exports.automationCreated = () => { - const properties = {} - processEvent(Events.AUTOMATION_CREATED, properties) -} - -exports.automationDeleted = () => { - const properties = {} - processEvent(Events.AUTOMATION_DELETED, properties) -} - -exports.automationTested = () => { - const properties = {} - processEvent(Events.AUTOMATION_TESTED, properties) -} - -exports.automationRun = () => { - const properties = {} - processEvent(Events.AUTOMATION_RUN, properties) -} - -exports.automationStepCreated = () => { - const properties = {} - processEvent(Events.AUTOMATION_STEP_CREATED, properties) -} - -exports.automationStepDeleted = () => { - const properties = {} - processEvent(Events.AUTOMATION_STEP_DELETED, properties) -} - -/** - ------------------ - MISC - ------------------ -*/ - -// NPS - -exports.npsSubmitted = () => { - const properties = {} - processEvent(Events.NPS_SUBMITTED, properties) -} - -// AUTH - -exports.login = () => { - const properties = {} - processEvent(Events.AUTH_LOGIN, properties) -} - -exports.logout = () => { - const properties = {} - processEvent(Events.AUTH_LOGOUT, properties) -} diff --git a/packages/backend-core/src/events/handlers/app.js b/packages/backend-core/src/events/handlers/app.js new file mode 100644 index 0000000000..c55a5c15e9 --- /dev/null +++ b/packages/backend-core/src/events/handlers/app.js @@ -0,0 +1,60 @@ +const events = require("../events") +const { Events } = require("../constants") + +exports.created = () => { + const properties = {} + events.processEvent(Events.APP_CREATED, properties) +} + +exports.updated = () => { + const properties = {} + events.processEvent(Events.APP_UPDATED, properties) +} + +exports.deleted = () => { + const properties = {} + events.processEvent(Events.APP_DELETED, properties) +} + +exports.published = () => { + const properties = {} + events.processEvent(Events.APP_PUBLISHED, properties) +} + +exports.unpublished = () => { + const properties = {} + events.processEvent(Events.APP_UNPUBLISHED, properties) +} + +exports.fileImported = () => { + const properties = {} + events.processEvent(Events.APP_FILE_IMPORTED, properties) +} + +exports.templateImported = templateKey => { + const properties = { + templateKey, + } + events.processEvent(Events.APP_TEMPLATE_IMPORTED, properties) +} + +exports.versionUpdated = () => { + const properties = {} + events.processEvent(Events.APP_VERSION_UPDATED, properties) +} + +exports.versionReverted = () => { + const properties = {} + events.processEvent(Events.APP_VERSION_REVERTED, properties) +} + +exports.reverted = () => { + const properties = {} + events.processEvent(Events.APP_REVERTED, properties) +} + +// TODO +exports.exported = () => { + const properties = {} + events.processEvent(Events.APP_EXPORTED, properties) +} diff --git a/packages/backend-core/src/events/handlers/auth.js b/packages/backend-core/src/events/handlers/auth.js new file mode 100644 index 0000000000..eac957f2cb --- /dev/null +++ b/packages/backend-core/src/events/handlers/auth.js @@ -0,0 +1,40 @@ +const events = require("../events") +const { Events } = require("../constants") + +exports.login = () => { + const properties = {} + events.processEvent(Events.AUTH_LOGIN, properties) +} + +exports.logout = () => { + const properties = {} + events.processEvent(Events.AUTH_LOGOUT, properties) +} + +exports.SSOCreated = type => { + const properties = { + type, + } + events.processEvent(Events.AUTH_SSO_CREATED, properties) +} + +exports.SSOUpdated = type => { + const properties = { + type, + } + events.processEvent(Events.AUTH_SSO_UPDATED, properties) +} + +exports.SSOActivated = type => { + const properties = { + type, + } + events.processEvent(Events.AUTH_SSO_ACTIVATED, properties) +} + +exports.SSODeactivated = type => { + const properties = { + type, + } + events.processEvent(Events.AUTH_SSO_DEACTIVATED, properties) +} diff --git a/packages/backend-core/src/events/handlers/automation.js b/packages/backend-core/src/events/handlers/automation.js new file mode 100644 index 0000000000..72c2d73d56 --- /dev/null +++ b/packages/backend-core/src/events/handlers/automation.js @@ -0,0 +1,37 @@ +const events = require("../events") +const { Events } = require("../constants") + +exports.created = () => { + const properties = {} + events.processEvent(Events.AUTOMATION_CREATED, properties) +} + +// TODO +exports.deleted = () => { + const properties = {} + events.processEvent(Events.AUTOMATION_DELETED, properties) +} + +// TODO +exports.tested = () => { + const properties = {} + events.processEvent(Events.AUTOMATION_TESTED, properties) +} + +// TODO +exports.run = () => { + const properties = {} + events.processEvent(Events.AUTOMATION_RUN, properties) +} + +// TODO +exports.stepCreated = () => { + const properties = {} + events.processEvent(Events.AUTOMATION_STEP_CREATED, properties) +} + +// TODO +exports.stepDeleted = () => { + const properties = {} + events.processEvent(Events.AUTOMATION_STEP_DELETED, properties) +} diff --git a/packages/backend-core/src/events/handlers/datasource.js b/packages/backend-core/src/events/handlers/datasource.js new file mode 100644 index 0000000000..c946d19f51 --- /dev/null +++ b/packages/backend-core/src/events/handlers/datasource.js @@ -0,0 +1,19 @@ +const events = require("../events") +const { Events } = require("../constants") + +exports.created = () => { + const properties = {} + events.processEvent(Events.DATASOURCE_CREATED, properties) +} + +// TODO +exports.updated = () => { + const properties = {} + events.processEvent(Events.DATASOURCE_UPDATED, properties) +} + +// TODO +exports.deleted = () => { + const properties = {} + events.processEvent(Events.DATASOURCE_DELETED, properties) +} diff --git a/packages/backend-core/src/events/handlers/email.js b/packages/backend-core/src/events/handlers/email.js new file mode 100644 index 0000000000..189f82dec7 --- /dev/null +++ b/packages/backend-core/src/events/handlers/email.js @@ -0,0 +1,12 @@ +const events = require("../events") +const { Events } = require("../constants") + +exports.SMTPCreated = () => { + const properties = {} + events.processEvent(Events.EMAIL_SMTP_CREATED, properties) +} + +exports.SMTPUpdated = () => { + const properties = {} + events.processEvent(Events.EMAIL_SMTP_UPDATED, properties) +} diff --git a/packages/backend-core/src/events/handlers/index.js b/packages/backend-core/src/events/handlers/index.js new file mode 100644 index 0000000000..dc2fd4ea10 --- /dev/null +++ b/packages/backend-core/src/events/handlers/index.js @@ -0,0 +1,31 @@ +const app = require("./app") +const auth = require("./auth") +const automation = require("./automation") +const datasource = require("./datasource") +const email = require("./email") +const licensing = require("./licensing") +const layout = require("./layout") +const org = require("./org") +const query = require("./query") +const row = require("./screen") +const table = require("./table") +const serve = require("./serve") +const user = require("./user") +const view = require("./view") + +module.exports = { + app, + auth, + automation, + datasource, + email, + licensing, + layout, + org, + query, + row, + table, + serve, + user, + view, +} diff --git a/packages/backend-core/src/events/handlers/layout.js b/packages/backend-core/src/events/handlers/layout.js new file mode 100644 index 0000000000..682a0e6655 --- /dev/null +++ b/packages/backend-core/src/events/handlers/layout.js @@ -0,0 +1,16 @@ +const events = require("../events") +const { Events } = require("../constants") + +// LAYOUT + +// TODO +exports.created = () => { + const properties = {} + events.processEvent(Events.LAYOUT_CREATED, properties) +} + +// TODO +exports.deleted = () => { + const properties = {} + events.processEvent(Events.LAYOUT_DELETED, properties) +} diff --git a/packages/backend-core/src/events/handlers/licensing.js b/packages/backend-core/src/events/handlers/licensing.js new file mode 100644 index 0000000000..144a9abf78 --- /dev/null +++ b/packages/backend-core/src/events/handlers/licensing.js @@ -0,0 +1,10 @@ +const events = require("../events") +const { Events } = require("../constants") + +exports.quotaExceeded = (quotaName, value) => { + const properties = { + name: quotaName, + value, + } + events.processEvent(Events.LICENSING_QUOTA_EXCEEDED, properties) +} diff --git a/packages/backend-core/src/events/handlers/org.js b/packages/backend-core/src/events/handlers/org.js new file mode 100644 index 0000000000..f82ba3bae4 --- /dev/null +++ b/packages/backend-core/src/events/handlers/org.js @@ -0,0 +1,34 @@ +const events = require("../events") +const { Events } = require("../constants") + +exports.nameUpdated = () => { + const properties = {} + events.processEvent(Events.ORG_NAME_UPDATED, properties) +} + +exports.logoUpdated = () => { + const properties = {} + events.processEvent(Events.ORG_LOGO_UPDATED, properties) +} + +exports.platformURLUpdated = () => { + const properties = {} + events.processEvent(Events.ORG_PLATFORM_URL_UPDATED, properties) +} + +exports.versionChecked = version => { + const properties = { + version, + } + events.processEvent(Events.UPDATE_VERSION_CHECKED, properties) +} + +exports.analyticsOptOut = () => { + const properties = {} + events.processEvent(Events.ANALYTICS_OPT_OUT, properties) +} + +exports.npsSubmitted = () => { + const properties = {} + events.processEvent(Events.NPS_SUBMITTED, properties) +} diff --git a/packages/backend-core/src/events/handlers/query.js b/packages/backend-core/src/events/handlers/query.js new file mode 100644 index 0000000000..aa303bd06f --- /dev/null +++ b/packages/backend-core/src/events/handlers/query.js @@ -0,0 +1,37 @@ +const events = require("../events") +const { Events } = require("../constants") + +exports.created = () => { + const properties = {} + events.processEvent(Events.QUERY_CREATED, properties) +} + +// TODO +exports.updated = () => { + const properties = {} + events.processEvent(Events.QUERY_UPDATED, properties) +} + +// TODO +exports.deleted = () => { + const properties = {} + events.processEvent(Events.QUERY_DELETED, properties) +} + +// TODO +exports.imported = () => { + const properties = {} + events.processEvent(Events.QUERY_IMPORTED, properties) +} + +// TODO +exports.run = () => { + const properties = {} + events.processEvent(Events.QUERY_RUN, properties) +} + +// TODO +exports.previewed = () => { + const properties = {} + events.processEvent(Events.QUERY_PREVIEWED, properties) +} diff --git a/packages/backend-core/src/events/handlers/role.js b/packages/backend-core/src/events/handlers/role.js new file mode 100644 index 0000000000..16b342f214 --- /dev/null +++ b/packages/backend-core/src/events/handlers/role.js @@ -0,0 +1,19 @@ +const events = require("../events") +const { Events } = require("../constants") + +exports.created = () => { + const properties = {} + events.processEvent(Events.ROLE_CREATED, properties) +} + +// TODO +exports.deleted = () => { + const properties = {} + events.processEvent(Events.ROLE_DELETED, properties) +} + +// TODO +exports.assigned = () => { + const properties = {} + events.processEvent(Events.ROLE_ASSIGNED, properties) +} diff --git a/packages/backend-core/src/events/handlers/row.js b/packages/backend-core/src/events/handlers/row.js new file mode 100644 index 0000000000..3f5b30839a --- /dev/null +++ b/packages/backend-core/src/events/handlers/row.js @@ -0,0 +1,26 @@ +const events = require("../events") +const { Events } = require("../constants") + +exports.created = () => { + const properties = {} + events.processEvent(Events.ROW_CREATED, properties) +} + +// TODO +exports.imported = () => { + const properties = {} + events.processEvent(Events.ROW_IMPORTED, properties) + exports.rowCreated() +} + +// TODO +exports.updated = () => { + const properties = {} + events.processEvent(Events.ROW_UPDATED, properties) +} + +// TODO +exports.deleted = () => { + const properties = {} + events.processEvent(Events.ROW_DELETED, properties) +} diff --git a/packages/backend-core/src/events/handlers/screen.js b/packages/backend-core/src/events/handlers/screen.js new file mode 100644 index 0000000000..53bc2b4fc7 --- /dev/null +++ b/packages/backend-core/src/events/handlers/screen.js @@ -0,0 +1,14 @@ +const events = require("../events") +const { Events } = require("../constants") + +// TODO +exports.created = () => { + const properties = {} + events.processEvent(Events.SCREEN_CREATED, properties) +} + +// TODO +exports.deleted = () => { + const properties = {} + events.processEvent(Events.SCREEN_DELETED, properties) +} diff --git a/packages/backend-core/src/events/handlers/serve.js b/packages/backend-core/src/events/handlers/serve.js new file mode 100644 index 0000000000..29c67d00ab --- /dev/null +++ b/packages/backend-core/src/events/handlers/serve.js @@ -0,0 +1,14 @@ +const events = require("../events") +const { Events } = require("../constants") + +// TODO +exports.builderServed = () => { + const properties = {} + events.processEvent(Events.BUILDER_SERVED, properties) +} + +// TODO +exports.clientServed = () => { + const properties = {} + events.processEvent(Events.CLIENT_SERVED, properties) +} diff --git a/packages/backend-core/src/events/handlers/table.js b/packages/backend-core/src/events/handlers/table.js new file mode 100644 index 0000000000..b6ddb44fd1 --- /dev/null +++ b/packages/backend-core/src/events/handlers/table.js @@ -0,0 +1,37 @@ +const events = require("../events") +const { Events } = require("../constants") + +exports.created = () => { + const properties = {} + events.processEvent(Events.TABLE_CREATED, properties) +} + +// TODO +exports.updated = () => { + const properties = {} + events.processEvent(Events.TABLE_UPDATED, properties) +} + +// TODO +exports.deleted = () => { + const properties = {} + events.processEvent(Events.TABLE_DELETED, properties) +} + +// TODO +exports.exported = () => { + const properties = {} + events.processEvent(Events.TABLE_EXPORTED, properties) +} + +// TODO +exports.imported = () => { + const properties = {} + events.processEvent(Events.TABLE_IMPORTED, properties) +} + +// TODO +exports.permissionUpdated = () => { + const properties = {} + events.processEvent(Events.TABLE_PERMISSION_UPDATED, properties) +} diff --git a/packages/backend-core/src/events/handlers/user.js b/packages/backend-core/src/events/handlers/user.js new file mode 100644 index 0000000000..7d1bc592e8 --- /dev/null +++ b/packages/backend-core/src/events/handlers/user.js @@ -0,0 +1,85 @@ +const events = require("../events") +const { Events } = require("../constants") + +// TODO +exports.created = () => { + const properties = {} + events.processEvent(Events.USER_CREATED, properties) +} + +// TODO +exports.updated = () => { + const properties = {} + events.processEvent(Events.USER_UPDATED, properties) +} + +exports.deleted = () => { + const properties = {} + events.processEvent(Events.USER_DELETED, properties) +} + +// TODO +exports.passwordForceReset = () => { + const properties = {} + events.processEvent(Events.USER_PASSWORD_FORCE_RESET, properties) +} + +// PERMISSIONS + +// TODO +exports.permissionAdminAssigned = () => { + const properties = {} + events.processEvent(Events.USER_PERMISSION_ADMIN_ASSIGNED, properties) +} + +// TODO +exports.permissionAdminRemoved = () => { + const properties = {} + events.processEvent(Events.USER_PERMISSION_ADMIN_REMOVED, properties) +} + +// TODO +exports.permissionBuilderAssigned = () => { + const properties = {} + events.processEvent(Events.USER_PERMISSION_BUILDER_ASSIGNED, properties) +} + +// TODO +exports.permissionBuilderRemoved = () => { + const properties = {} + events.processEvent(Events.USER_PERMISSION_BUILDER_REMOVED, properties) +} + +// INVITE + +exports.invited = () => { + const properties = {} + events.processEvent(Events.USER_INVITED, properties) +} + +exports.inviteAccepted = () => { + const properties = {} + events.processEvent(Events.USER_INVITED_ACCEPTED, properties) +} + +// SELF + +exports.selfUpdated = () => { + const properties = {} + events.processEvent(Events.USER_SELF_UPDATED, properties) +} + +exports.selfPasswordUpdated = () => { + const properties = {} + events.processEvent(Events.USER_SELF_PASSWORD_UPDATED, properties) +} + +exports.passwordResetRequested = () => { + const properties = {} + events.processEvent(Events.USER_PASSWORD_RESET_REQUESTED, properties) +} + +exports.passwordReset = () => { + const properties = {} + events.processEvent(Events.USER_PASSWORD_RESET, properties) +} diff --git a/packages/backend-core/src/events/handlers/view.js b/packages/backend-core/src/events/handlers/view.js new file mode 100644 index 0000000000..551960fcb0 --- /dev/null +++ b/packages/backend-core/src/events/handlers/view.js @@ -0,0 +1,49 @@ +const events = require("../events") +const { Events } = require("../constants") + +exports.created = () => { + const properties = {} + events.processEvent(Events.VIEW_CREATED, properties) +} + +// TODO +exports.updated = () => { + const properties = {} + events.processEvent(Events.VIEW_UPDATED, properties) +} + +// TODO +exports.deleted = () => { + const properties = {} + events.processEvent(Events.VIEW_DELETED, properties) +} + +// TODO +exports.exported = () => { + const properties = {} + events.processEvent(Events.VIEW_EXPORTED, properties) +} + +// TODO +exports.filterCreated = () => { + const properties = {} + events.processEvent(Events.VIEW_FILTER_CREATED, properties) +} + +// TODO +exports.filterDeleted = () => { + const properties = {} + events.processEvent(Events.VIEW_FILTER_DELETED, properties) +} + +// TODO +exports.calculationCreated = () => { + const properties = {} + events.processEvent(Events.VIEW_CALCULATION_CREATED, properties) +} + +// TODO +exports.calculationDeleted = () => { + const properties = {} + events.processEvent(Events.VIEW_CALCULATION_DELETED, properties) +} diff --git a/packages/backend-core/src/events/index.js b/packages/backend-core/src/events/index.js index e91cc25703..024194081b 100644 --- a/packages/backend-core/src/events/index.js +++ b/packages/backend-core/src/events/index.js @@ -1,2 +1,5 @@ -const events = require("./events") -module.exports = events +const handlers = require("./handlers") + +module.exports = { + ...handlers, +} diff --git a/packages/backend-core/src/index.js b/packages/backend-core/src/index.js index 2e5249a653..8450ba58d6 100644 --- a/packages/backend-core/src/index.js +++ b/packages/backend-core/src/index.js @@ -21,4 +21,5 @@ module.exports = { tenancy: require("./tenancy"), featureFlags: require("./featureFlags"), events: require("./events"), + analytics: require("./analytics"), } diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index 28e07878d9..191b69a702 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -51,7 +51,7 @@ const { } = require("@budibase/backend-core/context") import { getUniqueRows } from "../../utilities/usageQuota/rows" import { quotas } from "@budibase/pro" -import { errors } from "@budibase/backend-core" +import { errors, events } from "@budibase/backend-core" const URL_REGEX_SLASH = /\/|\\/g @@ -291,7 +291,33 @@ const performAppCreate = async (ctx: any) => { return newApplication } +const creationEvents = (request: any) => { + let creationFns = [] + + const body = request.body + if (body.useTemplate === "true") { + // from template + if (body.templateKey) { + creationFns.push(() => events.app.templateImported(body.templateKey)) + } + // from file + else if (request.files?.templateFile) { + creationFns.push(events.app.fileImported) + } + // unknown + else { + console.error("Could not determine template creation event") + } + } + creationFns.push(events.app.created) + + for (let fn of creationFns) { + fn() + } +} + const appPostCreate = async (ctx: any, appId: string) => { + creationEvents(ctx.request) // app import & template creation if (ctx.request.body.useTemplate === "true") { const rows = await getUniqueRows([appId]) @@ -336,6 +362,7 @@ export const update = async (ctx: any) => { } const data = await updateAppPackage(ctx.request.body, ctx.params.appId) + events.app.updated() ctx.status = 200 ctx.body = data } @@ -358,6 +385,7 @@ export const updateClient = async (ctx: any) => { revertableVersion: currentVersion, } const data = await updateAppPackage(appPackageUpdates, ctx.params.appId) + events.app.versionUpdated() ctx.status = 200 ctx.body = data } @@ -381,6 +409,7 @@ export const revertClient = async (ctx: any) => { revertableVersion: null, } const data = await updateAppPackage(appPackageUpdates, ctx.params.appId) + events.app.versionReverted() ctx.status = 200 ctx.body = data } @@ -391,8 +420,10 @@ const destroyApp = async (ctx: any) => { const result = await db.destroy() if (ctx.query?.unpublish) { await quotas.removePublishedApp() + events.app.unpublished() } else { await quotas.removeApp() + events.app.deleted() } /* istanbul ignore next */ if (!env.isTest() && !ctx.query.unpublish) { diff --git a/packages/server/src/api/controllers/backup.js b/packages/server/src/api/controllers/backup.js index daaa59b341..193f4139c5 100644 --- a/packages/server/src/api/controllers/backup.js +++ b/packages/server/src/api/controllers/backup.js @@ -1,4 +1,5 @@ const { streamBackup } = require("../../utilities/fileSystem") +const { events } = require("@budibase/backend-core") exports.exportAppDump = async function (ctx) { const { appId } = ctx.query @@ -6,4 +7,5 @@ exports.exportAppDump = async function (ctx) { const backupIdentifier = `${appName}-export-${new Date().getTime()}.txt` ctx.attachment(backupIdentifier) ctx.body = await streamBackup(appId) + events.app.exported() } diff --git a/packages/server/src/api/controllers/deploy/index.ts b/packages/server/src/api/controllers/deploy/index.ts index 663d4297fb..44de78fcc8 100644 --- a/packages/server/src/api/controllers/deploy/index.ts +++ b/packages/server/src/api/controllers/deploy/index.ts @@ -13,6 +13,7 @@ import { getProdAppDB, } from "@budibase/backend-core/context" import { quotas } from "@budibase/pro" +import { events } from "@budibase/backend-core" // the max time we can wait for an invalidation to complete before considering it failed const MAX_PENDING_TIME_MS = 30 * 60000 @@ -185,6 +186,7 @@ const _deployApp = async function (ctx: any) { await deployApp(deployment) } + events.app.published() ctx.body = deployment } diff --git a/packages/server/src/api/controllers/dev.js b/packages/server/src/api/controllers/dev.js index 54f554e358..208d91c699 100644 --- a/packages/server/src/api/controllers/dev.js +++ b/packages/server/src/api/controllers/dev.js @@ -7,6 +7,7 @@ const { Replication, getProdAppID } = require("@budibase/backend-core/db") const { DocumentTypes } = require("../../db/utils") const { app: appCache } = require("@budibase/backend-core/cache") const { getProdAppDB, getAppDB } = require("@budibase/backend-core/context") +const { events } = require("@budibase/backend-core") async function redirect(ctx, method, path = "global") { const { devPath } = ctx.params @@ -101,7 +102,10 @@ exports.revert = async ctx => { target: appId, }) - await replication.rollback() + if (!env.isTest()) { + // in-memory db stalls on rollback + await replication.rollback() + } // update appID in reverted app to be dev version again const db = getAppDB() const appDoc = await db.get(DocumentTypes.APP_METADATA) @@ -112,13 +116,16 @@ exports.revert = async ctx => { ctx.body = { message: "Reverted changes successfully.", } + events.app.reverted() } catch (err) { ctx.throw(400, `Unable to revert. ${err}`) } } exports.getBudibaseVersion = async ctx => { + const version = require("../../../package.json").version ctx.body = { - version: require("../../../package.json").version, + version, } + events.org.versionChecked(version) } diff --git a/packages/server/src/api/routes/tests/application.spec.js b/packages/server/src/api/routes/tests/application.spec.js index d2273a31b8..6f215165fe 100644 --- a/packages/server/src/api/routes/tests/application.spec.js +++ b/packages/server/src/api/routes/tests/application.spec.js @@ -28,17 +28,49 @@ describe("/applications", () => { beforeEach(async () => { await clearAllApps() await config.init() + jest.clearAllMocks() }) describe("create", () => { - it("returns a success message when the application is successfully created", async () => { + it("creates empty app", async () => { const res = await request .post("/api/applications") - .send({ name: "My App" }) + .field("name", "My App") .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) expect(res.body._id).toBeDefined() + expect(config.getEvents().app.created.mock.calls.length).toBe(1) + }) + + it("creates app from template", async () => { + const res = await request + .post("/api/applications") + .field("name", "My App") + .field("useTemplate", "true") + .field("templateKey", "test") + .field("templateString", "{}") // override the file download + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + expect(res.body._id).toBeDefined() + expect(config.getEvents().app.created.mock.calls.length).toBe(1) + expect(config.getEvents().app.templateImported.mock.calls.length).toBe(1) + }) + + + it("creates app from file", async () => { + const res = await request + .post("/api/applications") + .field("name", "My App") + .field("useTemplate", "true") + .set(config.defaultHeaders()) + .attach('templateFile', 'src/api/routes/tests/data/export.txt') + .expect("Content-Type", /json/) + .expect(200) + expect(res.body._id).toBeDefined() + expect(config.getEvents().app.created.mock.calls.length).toBe(1) + expect(config.getEvents().app.fileImported.mock.calls.length).toBe(1) }) it("should apply authorization to endpoint", async () => { @@ -102,6 +134,31 @@ describe("/applications", () => { .expect("Content-Type", /json/) .expect(200) expect(res.body.rev).toBeDefined() + expect(config.getEvents().app.updated.mock.calls.length).toBe(1) + }) + }) + + describe("delete", () => { + it("should delete app", async () => { + await config.createApp("to-delete") + const appId = config.getAppId() + await request + .delete(`/api/applications/${appId}`) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + expect(config.getEvents().app.deleted.mock.calls.length).toBe(1) + }) + + it("should unpublish app", async () => { + await config.createApp("to-unpublish") + const appId = config.getProdAppId() + await request + .delete(`/api/applications/${appId}?unpublish=true`) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + expect(config.getEvents().app.unpublished.mock.calls.length).toBe(1) }) }) @@ -113,6 +170,7 @@ describe("/applications", () => { .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) + expect(config.getEvents().app.versionUpdated.mock.calls.length).toBe(1) }) it("should be able to revert the app client library version", async () => { // We need to first update the version so that we can then revert @@ -126,6 +184,7 @@ describe("/applications", () => { .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) + expect(config.getEvents().app.versionReverted.mock.calls.length).toBe(1) }) }) diff --git a/packages/server/src/api/routes/tests/backup.spec.js b/packages/server/src/api/routes/tests/backup.spec.js index 78c0ee9268..a69cc9f31b 100644 --- a/packages/server/src/api/routes/tests/backup.spec.js +++ b/packages/server/src/api/routes/tests/backup.spec.js @@ -21,6 +21,7 @@ describe("/backups", () => { .expect(200) expect(res.text).toBeDefined() expect(res.text.includes(`"db_name":"${config.getAppId()}"`)).toEqual(true) + expect(config.getEvents().app.exported.mock.calls.length).toBe(1) }) it("should apply authorization to endpoint", async () => { diff --git a/packages/server/src/api/routes/tests/data/export.txt b/packages/server/src/api/routes/tests/data/export.txt new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/packages/server/src/api/routes/tests/data/export.txt @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/server/src/api/routes/tests/deployment.spec.js b/packages/server/src/api/routes/tests/deployment.spec.js new file mode 100644 index 0000000000..221a2164e2 --- /dev/null +++ b/packages/server/src/api/routes/tests/deployment.spec.js @@ -0,0 +1,24 @@ +const setup = require("./utilities") + +describe("/deployments", () => { + let request = setup.getRequest() + let config = setup.getConfig() + + afterAll(setup.afterAll) + + beforeEach(async () => { + await config.init() + jest.clearAllMocks() + }) + + describe("deploy", () => { + it("should deploy the application", async () => { + await request + .post(`/api/deploy`) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + expect(config.getEvents().app.published.mock.calls.length).toBe(1) + }) + }) +}) \ No newline at end of file diff --git a/packages/server/src/api/routes/tests/dev.spec.js b/packages/server/src/api/routes/tests/dev.spec.js new file mode 100644 index 0000000000..63e503bfb2 --- /dev/null +++ b/packages/server/src/api/routes/tests/dev.spec.js @@ -0,0 +1,24 @@ +const setup = require("./utilities") + +describe("/dev", () => { + let request = setup.getRequest() + let config = setup.getConfig() + + afterAll(setup.afterAll) + + beforeEach(async () => { + await config.init() + jest.clearAllMocks() + }) + + describe("revert", () => { + it("should revert the application", async () => { + await request + .post(`/api/dev/${config.getAppId()}/revert`) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + expect(config.getEvents().app.reverted.mock.calls.length).toBe(1) + }) + }) +}) \ No newline at end of file diff --git a/packages/server/src/tests/utilities/TestConfiguration.js b/packages/server/src/tests/utilities/TestConfiguration.js index 5d240d02d6..f25c76793f 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.js +++ b/packages/server/src/tests/utilities/TestConfiguration.js @@ -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 { events } = require("./mockEvents") const GLOBAL_USER_ID = "us_uuid1" const EMAIL = "babs@babs.com" @@ -55,6 +56,14 @@ class TestConfiguration { return this.appId } + getProdAppId() { + return this.prodAppId + } + + getEvents() { + return events + } + async _req(config, params, controlFunc) { const request = {} // fake cookies, we don't need them diff --git a/packages/server/src/tests/utilities/mockEvents.js b/packages/server/src/tests/utilities/mockEvents.js new file mode 100644 index 0000000000..4860024240 --- /dev/null +++ b/packages/server/src/tests/utilities/mockEvents.js @@ -0,0 +1,22 @@ +const core = require("@budibase/backend-core") + +const events = { + app: { + created: jest.fn(), + updated: jest.fn(), + deleted: jest.fn(), + published: jest.fn(), + unpublished: jest.fn(), + templateImported: jest.fn(), + fileImported: jest.fn(), + versionUpdated: jest.fn(), + versionReverted: jest.fn(), + reverted: jest.fn(), + exported: jest.fn(), + }, +} + +core.events = events +module.exports = { + events, +} From 8d39bf9f2eb739464ef98fa6c5c4f06893904251 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 5 Apr 2022 15:46:04 +0100 Subject: [PATCH 08/75] sso auth + datasource events, test updates --- .../backend-core/src/analytics/analytics.js | 45 ++++++++++++++++++- .../backend-core/src/analytics/posthog.js | 4 +- .../backend-core/src/db/tests/index.spec.js | 10 ++--- packages/backend-core/src/events/constants.js | 5 +++ .../src/events/handlers/account.js | 17 +++++++ .../backend-core/src/events/handlers/app.js | 1 - .../backend-core/src/events/handlers/auth.js | 11 ++++- .../src/events/handlers/datasource.js | 2 - packages/backend-core/src/index.js | 1 + .../src/migrations/tests/index.spec.js | 6 +-- .../src/tests/utilities/TestConfiguration.js | 3 -- .../backend-core/src/tests/utilities/index.js | 5 +++ .../src/tests/utilities/mocks/date.js | 13 ++++++ .../src/tests/utilities/mocks/events.js} | 15 ++++--- .../src/tests/utilities/mocks/index.js | 7 +++ .../server/src/api/controllers/datasource.js | 4 +- .../__snapshots__/datasource.spec.js.snap | 6 ++- .../src/api/routes/tests/application.spec.js | 21 ++++----- .../src/api/routes/tests/automation.spec.js | 3 +- .../src/api/routes/tests/backup.spec.js | 3 +- .../src/api/routes/tests/datasource.spec.js | 6 +++ .../src/api/routes/tests/deployment.spec.js | 3 +- .../server/src/api/routes/tests/dev.spec.js | 3 +- .../server/src/api/routes/tests/query.spec.js | 6 +++ .../server/src/api/routes/tests/row.spec.js | 8 ++-- .../src/tests/utilities/TestConfiguration.js | 6 +-- .../src/tests/utilities/mocks/events.js | 2 + .../server/src/tests/utilities/mocks/index.js | 1 + .../worker/src/api/controllers/global/auth.ts | 6 ++- 29 files changed, 171 insertions(+), 52 deletions(-) create mode 100644 packages/backend-core/src/events/handlers/account.js create mode 100644 packages/backend-core/src/tests/utilities/index.js create mode 100644 packages/backend-core/src/tests/utilities/mocks/date.js rename packages/{server/src/tests/utilities/mockEvents.js => backend-core/src/tests/utilities/mocks/events.js} (68%) create mode 100644 packages/backend-core/src/tests/utilities/mocks/index.js create mode 100644 packages/server/src/tests/utilities/mocks/events.js create mode 100644 packages/server/src/tests/utilities/mocks/index.js diff --git a/packages/backend-core/src/analytics/analytics.js b/packages/backend-core/src/analytics/analytics.js index eb8688de51..f7fc1360db 100644 --- a/packages/backend-core/src/analytics/analytics.js +++ b/packages/backend-core/src/analytics/analytics.js @@ -1,5 +1,17 @@ const PosthogClient = require("./posthog") const env = require("../environment") +const { getTenantId } = require("../context") + +const IdentityType = { + TENANT: "tenant", + USER: "user", + ACCOUNT: "account", +} + +const Hosting = { + CLOUD: "cloud", + SELF: "self", +} class Analytics { constructor() { @@ -14,9 +26,38 @@ class Analytics { return this.isEnabled } - updateUser(userId, properties) { + identify(type, id, hosting) { if (!this.isEnabled) return - this.posthog.updateUser(userId, properties) + const tenantId = getTenantId() + if (!hosting) { + hosting = env.SELF_HOSTED ? Hosting.SELF : Hosting.CLOUD + } + const properties = { + type, + hosting, + tenant: tenantId, + } + this.posthog.identify(id, properties) + } + + identifyUser(userId) { + this.identify(IdentityType.USER, userId) + } + + identifyTenant() { + let distinctId + if (env.SELF_HOSTED) { + distinctId = getTenantId() // TODO: Get installation ID + } else { + distinctId = getTenantId() + } + this.identify(IdentityType.TENANT, distinctId) + } + + identifyAccount(account) { + const distinctId = account.accountId + const hosting = account.hosting + this.identify(IdentityType.ACCOUNT, distinctId, hosting) } captureEvent(eventName, properties) { diff --git a/packages/backend-core/src/analytics/posthog.js b/packages/backend-core/src/analytics/posthog.js index d37f1b0cdd..afe1fb42af 100644 --- a/packages/backend-core/src/analytics/posthog.js +++ b/packages/backend-core/src/analytics/posthog.js @@ -5,8 +5,8 @@ class PosthogClient { this.posthog = new PostHog(token) } - updateUser(userId, properties) { - this.posthog.identify({ distinctId: userId, properties }) + identify(distinctId, properties) { + this.posthog.identify({ distinctId, properties }) } capture(userId, event, properties) { diff --git a/packages/backend-core/src/db/tests/index.spec.js b/packages/backend-core/src/db/tests/index.spec.js index 25cb7134b5..adb651aab5 100644 --- a/packages/backend-core/src/db/tests/index.spec.js +++ b/packages/backend-core/src/db/tests/index.spec.js @@ -1,8 +1,8 @@ -const { MOCK_DATE } = require("../../tests/utilities/TestConfiguration") +require("../../tests/utilities/TestConfiguration") +const { mocks } = require("../../tests/utilities") +mocks.date.mock() const { getDB, allDbs } = require("../") -Date = jest.fn(() => MOCK_DATE) - describe("db", () => { describe("getDB", () => { @@ -19,8 +19,8 @@ describe("db", () => { let doc = { _id: "test" } await db.put(doc) doc = await db.get(doc._id) - expect(doc.createdAt).toBe(MOCK_DATE.toISOString()) - expect(doc.updatedAt).toBe(MOCK_DATE.toISOString()) + expect(doc.createdAt).toBe(new Date().toISOString()) + expect(doc.updatedAt).toBe(new Date().toISOString()) await db.destroy() }) }) diff --git a/packages/backend-core/src/events/constants.js b/packages/backend-core/src/events/constants.js index 63d601d0d1..d74004acd0 100644 --- a/packages/backend-core/src/events/constants.js +++ b/packages/backend-core/src/events/constants.js @@ -131,4 +131,9 @@ exports.Events = { // LICENSING LICENSING_QUOTA_EXCEEDED: "licensing:quota:exceeded", + + // ACCOUNT + ACCOUNT_CREATED: "account:created", + ACCOUNT_DELETED: "account:deleted", + ACCOUNT_VERIFIED: "account:verified", } diff --git a/packages/backend-core/src/events/handlers/account.js b/packages/backend-core/src/events/handlers/account.js new file mode 100644 index 0000000000..e1d1eb8d35 --- /dev/null +++ b/packages/backend-core/src/events/handlers/account.js @@ -0,0 +1,17 @@ +const events = require("../events") +const { Events } = require("../constants") + +exports.created = () => { + const properties = {} + events.processEvent(Events.ACCOUNT_CREATED, properties) +} + +exports.deleted = () => { + const properties = {} + events.processEvent(Events.ACCOUNT_DELETED, properties) +} + +exports.verified = () => { + const properties = {} + events.processEvent(Events.ACCOUNT_VERIFIED, properties) +} diff --git a/packages/backend-core/src/events/handlers/app.js b/packages/backend-core/src/events/handlers/app.js index c55a5c15e9..17c86d9a6d 100644 --- a/packages/backend-core/src/events/handlers/app.js +++ b/packages/backend-core/src/events/handlers/app.js @@ -53,7 +53,6 @@ exports.reverted = () => { events.processEvent(Events.APP_REVERTED, properties) } -// TODO exports.exported = () => { const properties = {} events.processEvent(Events.APP_EXPORTED, properties) diff --git a/packages/backend-core/src/events/handlers/auth.js b/packages/backend-core/src/events/handlers/auth.js index eac957f2cb..e53cbc7957 100644 --- a/packages/backend-core/src/events/handlers/auth.js +++ b/packages/backend-core/src/events/handlers/auth.js @@ -1,16 +1,20 @@ const events = require("../events") const { Events } = require("../constants") -exports.login = () => { - const properties = {} +exports.login = source => { + const properties = { + source, + } events.processEvent(Events.AUTH_LOGIN, properties) } +// TODO exports.logout = () => { const properties = {} events.processEvent(Events.AUTH_LOGOUT, properties) } +// TODO exports.SSOCreated = type => { const properties = { type, @@ -18,6 +22,7 @@ exports.SSOCreated = type => { events.processEvent(Events.AUTH_SSO_CREATED, properties) } +// TODO exports.SSOUpdated = type => { const properties = { type, @@ -25,6 +30,7 @@ exports.SSOUpdated = type => { events.processEvent(Events.AUTH_SSO_UPDATED, properties) } +// TODO exports.SSOActivated = type => { const properties = { type, @@ -32,6 +38,7 @@ exports.SSOActivated = type => { events.processEvent(Events.AUTH_SSO_ACTIVATED, properties) } +// TODO exports.SSODeactivated = type => { const properties = { type, diff --git a/packages/backend-core/src/events/handlers/datasource.js b/packages/backend-core/src/events/handlers/datasource.js index c946d19f51..512a6cc28e 100644 --- a/packages/backend-core/src/events/handlers/datasource.js +++ b/packages/backend-core/src/events/handlers/datasource.js @@ -6,13 +6,11 @@ exports.created = () => { events.processEvent(Events.DATASOURCE_CREATED, properties) } -// TODO exports.updated = () => { const properties = {} events.processEvent(Events.DATASOURCE_UPDATED, properties) } -// TODO exports.deleted = () => { const properties = {} events.processEvent(Events.DATASOURCE_DELETED, properties) diff --git a/packages/backend-core/src/index.js b/packages/backend-core/src/index.js index 8450ba58d6..f759e5a23b 100644 --- a/packages/backend-core/src/index.js +++ b/packages/backend-core/src/index.js @@ -22,4 +22,5 @@ module.exports = { featureFlags: require("./featureFlags"), events: require("./events"), analytics: require("./analytics"), + testUtils: require("./tests/utilities"), } diff --git a/packages/backend-core/src/migrations/tests/index.spec.js b/packages/backend-core/src/migrations/tests/index.spec.js index 7ad5f3ad92..eceda08db4 100644 --- a/packages/backend-core/src/migrations/tests/index.spec.js +++ b/packages/backend-core/src/migrations/tests/index.spec.js @@ -1,12 +1,12 @@ -const { MOCK_DATE_TIMESTAMP, MOCK_DATE } = require("../../tests/utilities/TestConfiguration") +require("../../tests/utilities/TestConfiguration") +const { mocks } = require("../../tests/utilities") +mocks.date.mock() const { runMigrations, getMigrationsDoc } = require("../index") const { getDB } = require("../../db") const { StaticDatabases, } = require("../../db/utils") -Date = jest.fn(() => MOCK_DATE) -Date.now = jest.fn(() => MOCK_DATE_TIMESTAMP) let db describe("migrations", () => { diff --git a/packages/backend-core/src/tests/utilities/TestConfiguration.js b/packages/backend-core/src/tests/utilities/TestConfiguration.js index 5b9784eb4b..207b1d937f 100644 --- a/packages/backend-core/src/tests/utilities/TestConfiguration.js +++ b/packages/backend-core/src/tests/utilities/TestConfiguration.js @@ -1,4 +1 @@ require("./db") - -exports.MOCK_DATE = new Date("2020-01-01T00:00:00.000Z") -exports.MOCK_DATE_TIMESTAMP = 1577836800000 diff --git a/packages/backend-core/src/tests/utilities/index.js b/packages/backend-core/src/tests/utilities/index.js new file mode 100644 index 0000000000..99e7c96c50 --- /dev/null +++ b/packages/backend-core/src/tests/utilities/index.js @@ -0,0 +1,5 @@ +const mocks = require("./mocks") + +module.exports = { + mocks, +} diff --git a/packages/backend-core/src/tests/utilities/mocks/date.js b/packages/backend-core/src/tests/utilities/mocks/date.js new file mode 100644 index 0000000000..2d7cf4c698 --- /dev/null +++ b/packages/backend-core/src/tests/utilities/mocks/date.js @@ -0,0 +1,13 @@ +exports.MOCK_DATE = new Date("2020-01-01T00:00:00.000Z") +exports.MOCK_DATE_TIMESTAMP = 1577836800000 + +exports.mock = () => { + // eslint-disable-next-line no-global-assign + Date = jest.fn(() => exports.MOCK_DATE) + Date.now = jest.fn(() => exports.MOCK_DATE_TIMESTAMP) + + return { + MOCK_DATE: exports.MOCK_DATE, + MOCK_DATE_TIMESTAMP: exports.MOCK_DATE_TIMESTAMP, + } +} diff --git a/packages/server/src/tests/utilities/mockEvents.js b/packages/backend-core/src/tests/utilities/mocks/events.js similarity index 68% rename from packages/server/src/tests/utilities/mockEvents.js rename to packages/backend-core/src/tests/utilities/mocks/events.js index 4860024240..d44ed3ec9b 100644 --- a/packages/server/src/tests/utilities/mockEvents.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -1,5 +1,3 @@ -const core = require("@budibase/backend-core") - const events = { app: { created: jest.fn(), @@ -14,9 +12,14 @@ const events = { reverted: jest.fn(), exported: jest.fn(), }, + auth: { + login: jest.fn(), + }, + datasource: { + created: jest.fn(), + updated: jest.fn(), + deleted: jest.fn(), + }, } -core.events = events -module.exports = { - events, -} +module.exports = events diff --git a/packages/backend-core/src/tests/utilities/mocks/index.js b/packages/backend-core/src/tests/utilities/mocks/index.js new file mode 100644 index 0000000000..5604ce75e6 --- /dev/null +++ b/packages/backend-core/src/tests/utilities/mocks/index.js @@ -0,0 +1,7 @@ +const events = require("./events") +const date = require("./date") + +module.exports = { + events, + date, +} diff --git a/packages/server/src/api/controllers/datasource.js b/packages/server/src/api/controllers/datasource.js index 1117d15841..d9e9dda046 100644 --- a/packages/server/src/api/controllers/datasource.js +++ b/packages/server/src/api/controllers/datasource.js @@ -109,6 +109,7 @@ exports.update = async function (ctx) { } const response = await db.put(datasource) + events.datasource.updated() datasource._rev = response.rev // Drain connection pools when configuration is changed @@ -143,7 +144,7 @@ exports.save = async function (ctx) { } const dbResp = await db.put(datasource) - events.datasourceCreated(datasource) + events.datasource.created() datasource._rev = dbResp.rev // Drain connection pools when configuration is changed @@ -178,6 +179,7 @@ exports.destroy = async function (ctx) { // delete the datasource await db.remove(ctx.params.datasourceId, ctx.params.revId) + events.datasource.deleted() ctx.message = `Datasource deleted.` ctx.status = 200 diff --git a/packages/server/src/api/routes/tests/__snapshots__/datasource.spec.js.snap b/packages/server/src/api/routes/tests/__snapshots__/datasource.spec.js.snap index 287605cdd6..436818f089 100644 --- a/packages/server/src/api/routes/tests/__snapshots__/datasource.spec.js.snap +++ b/packages/server/src/api/routes/tests/__snapshots__/datasource.spec.js.snap @@ -7,7 +7,8 @@ Array [ "entities": Array [ Object { "_id": "ta_users", - "_rev": "1-039883a06c1f9cb3945731d79838181e", + "_rev": "1-6f4013e796887f1771bf7837598d87e7", + "createdAt": "2020-01-01T00:00:00.000Z", "name": "Users", "primaryDisplay": "email", "schema": Object { @@ -72,6 +73,7 @@ Array [ }, }, "type": "table", + "updatedAt": "2020-01-01T00:00:00.000Z", "views": Object {}, }, ], @@ -81,9 +83,11 @@ Array [ }, Object { "config": Object {}, + "createdAt": "2020-01-01T00:00:00.000Z", "name": "Test", "source": "POSTGRES", "type": "datasource", + "updatedAt": "2020-01-01T00:00:00.000Z", }, ] `; diff --git a/packages/server/src/api/routes/tests/application.spec.js b/packages/server/src/api/routes/tests/application.spec.js index 6f215165fe..4bf6aa8282 100644 --- a/packages/server/src/api/routes/tests/application.spec.js +++ b/packages/server/src/api/routes/tests/application.spec.js @@ -18,6 +18,7 @@ const { } = require("./utilities/TestFunctions") const setup = require("./utilities") const { AppStatus } = require("../../../db/utils") +const { events } = require("@budibase/backend-core") describe("/applications", () => { let request = setup.getRequest() @@ -40,7 +41,7 @@ describe("/applications", () => { .expect("Content-Type", /json/) .expect(200) expect(res.body._id).toBeDefined() - expect(config.getEvents().app.created.mock.calls.length).toBe(1) + expect(events.app.created.mock.calls.length).toBe(1) }) it("creates app from template", async () => { @@ -54,8 +55,8 @@ describe("/applications", () => { .expect("Content-Type", /json/) .expect(200) expect(res.body._id).toBeDefined() - expect(config.getEvents().app.created.mock.calls.length).toBe(1) - expect(config.getEvents().app.templateImported.mock.calls.length).toBe(1) + expect(events.app.created.mock.calls.length).toBe(1) + expect(events.app.templateImported.mock.calls.length).toBe(1) }) @@ -69,8 +70,8 @@ describe("/applications", () => { .expect("Content-Type", /json/) .expect(200) expect(res.body._id).toBeDefined() - expect(config.getEvents().app.created.mock.calls.length).toBe(1) - expect(config.getEvents().app.fileImported.mock.calls.length).toBe(1) + expect(events.app.created.mock.calls.length).toBe(1) + expect(events.app.fileImported.mock.calls.length).toBe(1) }) it("should apply authorization to endpoint", async () => { @@ -134,7 +135,7 @@ describe("/applications", () => { .expect("Content-Type", /json/) .expect(200) expect(res.body.rev).toBeDefined() - expect(config.getEvents().app.updated.mock.calls.length).toBe(1) + expect(events.app.updated.mock.calls.length).toBe(1) }) }) @@ -147,7 +148,7 @@ describe("/applications", () => { .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) - expect(config.getEvents().app.deleted.mock.calls.length).toBe(1) + expect(events.app.deleted.mock.calls.length).toBe(1) }) it("should unpublish app", async () => { @@ -158,7 +159,7 @@ describe("/applications", () => { .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) - expect(config.getEvents().app.unpublished.mock.calls.length).toBe(1) + expect(events.app.unpublished.mock.calls.length).toBe(1) }) }) @@ -170,7 +171,7 @@ describe("/applications", () => { .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) - expect(config.getEvents().app.versionUpdated.mock.calls.length).toBe(1) + expect(events.app.versionUpdated.mock.calls.length).toBe(1) }) it("should be able to revert the app client library version", async () => { // We need to first update the version so that we can then revert @@ -184,7 +185,7 @@ describe("/applications", () => { .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) - expect(config.getEvents().app.versionReverted.mock.calls.length).toBe(1) + expect(events.app.versionReverted.mock.calls.length).toBe(1) }) }) diff --git a/packages/server/src/api/routes/tests/automation.spec.js b/packages/server/src/api/routes/tests/automation.spec.js index 3e5725bb95..4e95e6ccca 100644 --- a/packages/server/src/api/routes/tests/automation.spec.js +++ b/packages/server/src/api/routes/tests/automation.spec.js @@ -6,7 +6,8 @@ const { } = require("./utilities/TestFunctions") const setup = require("./utilities") const { basicAutomation } = setup.structures - +const { testUtils } = require("@budibase/backend-core") +testUtils.mocks.date.mock() const MAX_RETRIES = 4 let ACTION_DEFINITIONS = {} diff --git a/packages/server/src/api/routes/tests/backup.spec.js b/packages/server/src/api/routes/tests/backup.spec.js index a69cc9f31b..7f92a4f3d1 100644 --- a/packages/server/src/api/routes/tests/backup.spec.js +++ b/packages/server/src/api/routes/tests/backup.spec.js @@ -2,6 +2,7 @@ jest.mock("../../../utilities/fileSystem/utilities") const { checkBuilderEndpoint } = require("./utilities/TestFunctions") const setup = require("./utilities") +const { events } = require("@budibase/backend-core") describe("/backups", () => { let request = setup.getRequest() @@ -21,7 +22,7 @@ describe("/backups", () => { .expect(200) expect(res.text).toBeDefined() expect(res.text.includes(`"db_name":"${config.getAppId()}"`)).toEqual(true) - expect(config.getEvents().app.exported.mock.calls.length).toBe(1) + expect(events.app.exported.mock.calls.length).toBe(1) }) it("should apply authorization to endpoint", async () => { diff --git a/packages/server/src/api/routes/tests/datasource.spec.js b/packages/server/src/api/routes/tests/datasource.spec.js index 880cc338a2..68f8134492 100644 --- a/packages/server/src/api/routes/tests/datasource.spec.js +++ b/packages/server/src/api/routes/tests/datasource.spec.js @@ -5,6 +5,8 @@ let { basicDatasource } = setup.structures let { checkBuilderEndpoint } = require("./utilities/TestFunctions") const pg = require("pg") const { checkCacheForDynamicVariable } = require("../../../threads/utils") +const { events, testUtils } = require("@budibase/backend-core") +testUtils.mocks.date.mock() describe("/datasources", () => { let request = setup.getRequest() @@ -16,6 +18,7 @@ describe("/datasources", () => { beforeEach(async () => { await config.init() datasource = await config.createDatasource() + jest.clearAllMocks() }) describe("create", () => { @@ -29,6 +32,7 @@ describe("/datasources", () => { expect(res.body.datasource.name).toEqual("Test") expect(res.body.errors).toBeUndefined() + expect(events.datasource.created.mock.calls.length).toBe(1) }) }) @@ -44,6 +48,7 @@ describe("/datasources", () => { expect(res.body.datasource.name).toEqual("Updated Test") expect(res.body.errors).toBeUndefined() + expect(events.datasource.updated.mock.calls.length).toBe(1) }) describe("dynamic variables", () => { @@ -160,6 +165,7 @@ describe("/datasources", () => { .expect(200) expect(res.body.length).toEqual(1) + expect(events.datasource.deleted.mock.calls.length).toBe(1) }) it("should apply authorization to endpoint", async () => { diff --git a/packages/server/src/api/routes/tests/deployment.spec.js b/packages/server/src/api/routes/tests/deployment.spec.js index 221a2164e2..be126fa239 100644 --- a/packages/server/src/api/routes/tests/deployment.spec.js +++ b/packages/server/src/api/routes/tests/deployment.spec.js @@ -1,4 +1,5 @@ const setup = require("./utilities") +const { events } = require("@budibase/backend-core") describe("/deployments", () => { let request = setup.getRequest() @@ -18,7 +19,7 @@ describe("/deployments", () => { .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) - expect(config.getEvents().app.published.mock.calls.length).toBe(1) + expect(events.app.published.mock.calls.length).toBe(1) }) }) }) \ No newline at end of file diff --git a/packages/server/src/api/routes/tests/dev.spec.js b/packages/server/src/api/routes/tests/dev.spec.js index 63e503bfb2..91a2c67c56 100644 --- a/packages/server/src/api/routes/tests/dev.spec.js +++ b/packages/server/src/api/routes/tests/dev.spec.js @@ -1,4 +1,5 @@ const setup = require("./utilities") +const { events } = require("@budibase/backend-core") describe("/dev", () => { let request = setup.getRequest() @@ -18,7 +19,7 @@ describe("/dev", () => { .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) - expect(config.getEvents().app.reverted.mock.calls.length).toBe(1) + expect(events.app.reverted.mock.calls.length).toBe(1) }) }) }) \ No newline at end of file diff --git a/packages/server/src/api/routes/tests/query.spec.js b/packages/server/src/api/routes/tests/query.spec.js index 48e3181d10..5dc17a53a4 100644 --- a/packages/server/src/api/routes/tests/query.spec.js +++ b/packages/server/src/api/routes/tests/query.spec.js @@ -13,6 +13,8 @@ const setup = require("./utilities") const { checkBuilderEndpoint } = require("./utilities/TestFunctions") const { checkCacheForDynamicVariable } = require("../../../threads/utils") const { basicQuery, basicDatasource } = setup.structures +const { testUtils } = require("@budibase/backend-core") +testUtils.mocks.date.mock() describe("/queries", () => { let request = setup.getRequest() @@ -56,6 +58,8 @@ describe("/queries", () => { _rev: res.body._rev, _id: res.body._id, ...query, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString() }) }) }) @@ -73,7 +77,9 @@ describe("/queries", () => { { _rev: query._rev, _id: query._id, + createdAt: new Date().toISOString(), ...basicQuery(datasource._id), + updatedAt: new Date().toISOString(), readable: true, }, ]) diff --git a/packages/server/src/api/routes/tests/row.spec.js b/packages/server/src/api/routes/tests/row.spec.js index 8354f01ad7..0a1481520d 100644 --- a/packages/server/src/api/routes/tests/row.spec.js +++ b/packages/server/src/api/routes/tests/row.spec.js @@ -2,9 +2,7 @@ const { outputProcessing } = require("../../../utilities/rowProcessor") const setup = require("./utilities") const { basicRow } = setup.structures const { doInAppContext } = require("@budibase/backend-core/context") - -// mock the fetch for the search system -jest.mock("node-fetch") +const { testUtils } = require("@budibase/backend-core") describe("/rows", () => { let request = setup.getRequest() @@ -69,6 +67,10 @@ describe("/rows", () => { .expect('Content-Type', /json/) .expect(200) + // can't mock dates due to coercion test requiring real Date clas + delete res.body.createdAt + delete res.body.updatedAt + expect(res.body).toEqual({ ...row, _id: existing._id, diff --git a/packages/server/src/tests/utilities/TestConfiguration.js b/packages/server/src/tests/utilities/TestConfiguration.js index f25c76793f..87c8f4f4b1 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.js +++ b/packages/server/src/tests/utilities/TestConfiguration.js @@ -1,3 +1,4 @@ +require("./mocks") require("../../db").init() const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles") const env = require("../../environment") @@ -25,7 +26,6 @@ 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 { events } = require("./mockEvents") const GLOBAL_USER_ID = "us_uuid1" const EMAIL = "babs@babs.com" @@ -60,10 +60,6 @@ class TestConfiguration { return this.prodAppId } - getEvents() { - return events - } - async _req(config, params, controlFunc) { const request = {} // fake cookies, we don't need them diff --git a/packages/server/src/tests/utilities/mocks/events.js b/packages/server/src/tests/utilities/mocks/events.js new file mode 100644 index 0000000000..10ac9b7f4f --- /dev/null +++ b/packages/server/src/tests/utilities/mocks/events.js @@ -0,0 +1,2 @@ +const core = require("@budibase/backend-core") +core.events = core.testUtils.mocks.events diff --git a/packages/server/src/tests/utilities/mocks/index.js b/packages/server/src/tests/utilities/mocks/index.js new file mode 100644 index 0000000000..8f2f873a17 --- /dev/null +++ b/packages/server/src/tests/utilities/mocks/index.js @@ -0,0 +1 @@ +require("./events") diff --git a/packages/worker/src/api/controllers/global/auth.ts b/packages/worker/src/api/controllers/global/auth.ts index 7e094315fc..5e6413bece 100644 --- a/packages/worker/src/api/controllers/global/auth.ts +++ b/packages/worker/src/api/controllers/global/auth.ts @@ -22,6 +22,7 @@ const { } = require("@budibase/backend-core/tenancy") const env = require("../../../environment") import { users } from "@budibase/pro" +const { events } = require("@budibase/backend-core") const ssoCallbackUrl = async (config: any, type: any) => { // incase there is a callback URL from before @@ -77,6 +78,7 @@ export const authenticate = async (ctx: any, next: any) => { "local", async (err: any, user: any, info: any) => { await authInternal(ctx, user, err, info) + events.auth.login("local") ctx.status = 200 } )(ctx, next) @@ -213,7 +215,7 @@ export const googleAuth = async (ctx: any, next: any) => { { successRedirect: "/", failureRedirect: "/error" }, async (err: any, user: any, info: any) => { await authInternal(ctx, user, err, info) - + events.auth.login("google") ctx.redirect("/") } )(ctx, next) @@ -257,7 +259,7 @@ export const oidcAuth = async (ctx: any, next: any) => { { successRedirect: "/", failureRedirect: "/error" }, async (err: any, user: any, info: any) => { await authInternal(ctx, user, err, info) - + events.auth.login("oidc") ctx.redirect("/") } )(ctx, next) From 6128dfadcf9fb8e091d0afbef99da49d7dd9e349 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 5 Apr 2022 16:56:28 +0100 Subject: [PATCH 09/75] Logout events --- packages/backend-core/package.json | 1 + .../backend-core/src/events/handlers/auth.js | 1 - packages/backend-core/src/index.js | 1 - .../src/tests/utilities/data/index.js | 8 + .../src/tests/utilities/data/koa.js | 5 + .../backend-core/src/tests/utilities/index.js | 2 + .../src/tests/utilities/mocks/events.js | 51 +-- packages/backend-core/src/tests/utils.spec.js | 13 + packages/backend-core/src/utils.js | 2 + packages/backend-core/testUtils.js | 1 + packages/backend-core/yarn.lock | 298 +++++++++++++++++- .../src/api/routes/tests/automation.spec.js | 4 +- .../src/api/routes/tests/datasource.spec.js | 5 +- .../server/src/api/routes/tests/query.spec.js | 4 +- .../server/src/tests/utilities/mocks/core.js | 3 + .../src/tests/utilities/mocks/events.js | 2 - .../server/src/tests/utilities/mocks/index.js | 2 +- .../src/api/controllers/global/configs.js | 65 +++- .../worker/src/api/routes/tests/auth.spec.js | 9 + .../tests/utilities/TestConfiguration.js | 1 + .../api/routes/tests/utilities/mocks/core.js | 3 + .../api/routes/tests/utilities/mocks/index.js | 1 + 22 files changed, 445 insertions(+), 37 deletions(-) create mode 100644 packages/backend-core/src/tests/utilities/data/index.js create mode 100644 packages/backend-core/src/tests/utilities/data/koa.js create mode 100644 packages/backend-core/src/tests/utils.spec.js create mode 100644 packages/backend-core/testUtils.js create mode 100644 packages/server/src/tests/utilities/mocks/core.js delete mode 100644 packages/server/src/tests/utilities/mocks/events.js create mode 100644 packages/worker/src/api/routes/tests/utilities/mocks/core.js create mode 100644 packages/worker/src/api/routes/tests/utilities/mocks/index.js diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index a271b33557..8639fa6dac 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -38,6 +38,7 @@ ] }, "devDependencies": { + "@shopify/jest-koa-mocks": "^3.1.5", "ioredis-mock": "^5.5.5", "jest": "^26.6.3", "pouchdb": "^7.2.1", diff --git a/packages/backend-core/src/events/handlers/auth.js b/packages/backend-core/src/events/handlers/auth.js index e53cbc7957..41b3a7535b 100644 --- a/packages/backend-core/src/events/handlers/auth.js +++ b/packages/backend-core/src/events/handlers/auth.js @@ -8,7 +8,6 @@ exports.login = source => { events.processEvent(Events.AUTH_LOGIN, properties) } -// TODO exports.logout = () => { const properties = {} events.processEvent(Events.AUTH_LOGOUT, properties) diff --git a/packages/backend-core/src/index.js b/packages/backend-core/src/index.js index f759e5a23b..8450ba58d6 100644 --- a/packages/backend-core/src/index.js +++ b/packages/backend-core/src/index.js @@ -22,5 +22,4 @@ module.exports = { featureFlags: require("./featureFlags"), events: require("./events"), analytics: require("./analytics"), - testUtils: require("./tests/utilities"), } diff --git a/packages/backend-core/src/tests/utilities/data/index.js b/packages/backend-core/src/tests/utilities/data/index.js new file mode 100644 index 0000000000..78f54e9104 --- /dev/null +++ b/packages/backend-core/src/tests/utilities/data/index.js @@ -0,0 +1,8 @@ +require("../mocks") +const koa = require("./koa") + +const data = { + koa, +} + +module.exports = data diff --git a/packages/backend-core/src/tests/utilities/data/koa.js b/packages/backend-core/src/tests/utilities/data/koa.js new file mode 100644 index 0000000000..c1e9955713 --- /dev/null +++ b/packages/backend-core/src/tests/utilities/data/koa.js @@ -0,0 +1,5 @@ +const { createMockContext } = require("@shopify/jest-koa-mocks") + +exports.newContext = () => { + return createMockContext() +} diff --git a/packages/backend-core/src/tests/utilities/index.js b/packages/backend-core/src/tests/utilities/index.js index 99e7c96c50..2b7f91cf0d 100644 --- a/packages/backend-core/src/tests/utilities/index.js +++ b/packages/backend-core/src/tests/utilities/index.js @@ -1,5 +1,7 @@ const mocks = require("./mocks") +const data = require("./data") module.exports = { mocks, + data, } diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index d44ed3ec9b..203af45497 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -1,25 +1,30 @@ -const events = { - app: { - created: jest.fn(), - updated: jest.fn(), - deleted: jest.fn(), - published: jest.fn(), - unpublished: jest.fn(), - templateImported: jest.fn(), - fileImported: jest.fn(), - versionUpdated: jest.fn(), - versionReverted: jest.fn(), - reverted: jest.fn(), - exported: jest.fn(), - }, - auth: { - login: jest.fn(), - }, - datasource: { - created: jest.fn(), - updated: jest.fn(), - deleted: jest.fn(), - }, -} +jest.mock("../../../events", () => { + return { + app: { + created: jest.fn(), + updated: jest.fn(), + deleted: jest.fn(), + published: jest.fn(), + unpublished: jest.fn(), + templateImported: jest.fn(), + fileImported: jest.fn(), + versionUpdated: jest.fn(), + versionReverted: jest.fn(), + reverted: jest.fn(), + exported: jest.fn(), + }, + auth: { + login: jest.fn(), + logout: jest.fn(), + }, + datasource: { + created: jest.fn(), + updated: jest.fn(), + deleted: jest.fn(), + }, + } +}) + +const events = require("../../../events") module.exports = events diff --git a/packages/backend-core/src/tests/utils.spec.js b/packages/backend-core/src/tests/utils.spec.js new file mode 100644 index 0000000000..618c71ad6a --- /dev/null +++ b/packages/backend-core/src/tests/utils.spec.js @@ -0,0 +1,13 @@ +const { data } = require("./utilities") +const utils = require("../utils") +const events = require("../events") + +describe("utils", () => { + describe("platformLogout", () => { + it("should call platform logout", async () => { + const ctx = data.koa.newContext() + await utils.platformLogout({ ctx, userId: "test" }) + expect(events.auth.logout.mock.calls.length).toBe(1) + }) + }) +}) \ No newline at end of file diff --git a/packages/backend-core/src/utils.js b/packages/backend-core/src/utils.js index 419bf55830..302b4a7bf8 100644 --- a/packages/backend-core/src/utils.js +++ b/packages/backend-core/src/utils.js @@ -20,6 +20,7 @@ const { hash } = require("./hashing") const userCache = require("./cache/user") const env = require("./environment") const { getUserSessions, invalidateSessions } = require("./security/sessions") +const events = require("./events") const APP_PREFIX = DocumentTypes.APP + SEPARATOR @@ -266,5 +267,6 @@ exports.platformLogout = async ({ ctx, userId, keepActiveSession }) => { userId, sessions.map(({ sessionId }) => sessionId) ) + events.auth.logout() await userCache.invalidateUser(userId) } diff --git a/packages/backend-core/testUtils.js b/packages/backend-core/testUtils.js new file mode 100644 index 0000000000..b2d84e3d05 --- /dev/null +++ b/packages/backend-core/testUtils.js @@ -0,0 +1 @@ +module.exports = require("./src/tests/utilities") diff --git a/packages/backend-core/yarn.lock b/packages/backend-core/yarn.lock index ad9769202b..148d208879 100644 --- a/packages/backend-core/yarn.lock +++ b/packages/backend-core/yarn.lock @@ -497,6 +497,14 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@shopify/jest-koa-mocks@^3.1.5": + version "3.1.5" + resolved "https://registry.yarnpkg.com/@shopify/jest-koa-mocks/-/jest-koa-mocks-3.1.5.tgz#11f77ccfbcaf35cf5ee2c6108a286e61e6bea084" + integrity sha512-gQ3/7ELerv00TWO37AGFX5mT9CsFCS+3/UbKMuoIlKEU0QH2OX8BV9WBf/EKw7adCDNlxss0lqV6J8kf5pgr4A== + dependencies: + koa "^2.13.4" + node-mocks-http "^1.5.8" + "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" @@ -648,6 +656,14 @@ abstract-leveldown@~2.7.1: dependencies: xtend "~4.0.0" +accepts@^1.3.5, accepts@^1.3.7: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + acorn-globals@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" @@ -1107,6 +1123,14 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +cache-content-type@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-content-type/-/cache-content-type-1.0.1.tgz#035cde2b08ee2129f4a8315ea8f00a00dba1453c" + integrity sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA== + dependencies: + mime-types "^2.1.18" + ylru "^1.2.0" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -1310,6 +1334,18 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +content-disposition@^0.5.3, content-disposition@~0.5.2: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" @@ -1317,6 +1353,14 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: dependencies: safe-buffer "~5.1.1" +cookies@~0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.8.0.tgz#1293ce4b391740a8406e3c9870e828c4b54f3f90" + integrity sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow== + dependencies: + depd "~2.0.0" + keygrip "~1.1.0" + copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" @@ -1406,6 +1450,13 @@ debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" +debug@^4.3.2: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -1421,6 +1472,11 @@ decode-uri-component@^0.2.0: resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= +deep-equal@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" + integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= + deep-is@~0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -1471,11 +1527,31 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + denque@^1.1.0: version "1.5.1" resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.1.tgz#07f670e29c9a78f8faecb2566a1e2c11929c5cbf" integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw== +depd@^1.1.0, depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +depd@^2.0.0, depd@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +destroy@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -1528,6 +1604,11 @@ ecdsa-sig-formatter@1.0.9: base64url "^2.0.0" safe-buffer "^5.0.1" +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + electron-to-chromium@^1.3.896: version "1.3.900" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.900.tgz#5be2c5818a2a012c511b4b43e87b6ab7a296d4f5" @@ -1550,6 +1631,11 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +encodeurl@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + encoding-down@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-6.3.0.tgz#b1c4eb0e1728c146ecaef8e32963c549e76d082b" @@ -1602,6 +1688,11 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +escape-html@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -1888,6 +1979,11 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" +fresh@^0.5.2, fresh@~0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" @@ -2064,6 +2160,18 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-symbols@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -2134,6 +2242,25 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +http-assert@^1.3.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.5.0.tgz#c389ccd87ac16ed2dfa6246fd73b926aa00e6b8f" + integrity sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w== + dependencies: + deep-equal "~1.0.1" + http-errors "~1.8.0" + +http-errors@^1.6.3, http-errors@~1.8.0: + version "1.8.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" + integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.1" + http-proxy-agent@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" @@ -2346,6 +2473,13 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== +is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -3044,6 +3178,13 @@ jws@^3.2.2: jwa "^1.4.1" safe-buffer "^5.0.1" +keygrip@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226" + integrity sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ== + dependencies: + tsscmp "1.0.6" + kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -3073,6 +3214,19 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +koa-compose@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877" + integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw== + +koa-convert@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-2.0.0.tgz#86a0c44d81d40551bae22fee6709904573eea4f5" + integrity sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA== + dependencies: + co "^4.6.0" + koa-compose "^4.1.0" + koa-passport@^4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/koa-passport/-/koa-passport-4.1.4.tgz#5f1665c1c2a37ace79af9f970b770885ca30ccfa" @@ -3080,6 +3234,35 @@ koa-passport@^4.1.4: dependencies: passport "^0.4.0" +koa@^2.13.4: + version "2.13.4" + resolved "https://registry.yarnpkg.com/koa/-/koa-2.13.4.tgz#ee5b0cb39e0b8069c38d115139c774833d32462e" + integrity sha512-43zkIKubNbnrULWlHdN5h1g3SEKXOEzoAlRsHOTFpnlDu8JlAOZSMJBLULusuXRequboiwJcj5vtYXKB3k7+2g== + dependencies: + accepts "^1.3.5" + cache-content-type "^1.0.0" + content-disposition "~0.5.2" + content-type "^1.0.4" + cookies "~0.8.0" + debug "^4.3.2" + delegates "^1.0.0" + depd "^2.0.0" + destroy "^1.0.4" + encodeurl "^1.0.2" + escape-html "^1.0.3" + fresh "~0.5.2" + http-assert "^1.3.0" + http-errors "^1.6.3" + is-generator-function "^1.0.7" + koa-compose "^4.1.0" + koa-convert "^2.0.0" + on-finished "^2.3.0" + only "~0.0.2" + parseurl "^1.3.2" + statuses "^1.5.0" + type-is "^1.6.16" + vary "^1.1.2" + level-codec@9.0.2, level-codec@^9.0.0: version "9.0.2" resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-9.0.2.tgz#fd60df8c64786a80d44e63423096ffead63d8cbc" @@ -3318,6 +3501,11 @@ md5@^2.3.0: crypt "0.0.2" is-buffer "~1.1.6" +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + memdown@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/memdown/-/memdown-1.4.1.tgz#b4e4e192174664ffbae41361aa500f3119efe215" @@ -3330,11 +3518,21 @@ memdown@1.4.1: ltgt "~2.2.0" safe-buffer "~5.1.1" +merge-descriptors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +methods@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" @@ -3367,6 +3565,11 @@ mime-db@1.51.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + mime-db@~1.27.0: version "1.27.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1" @@ -3379,6 +3582,13 @@ mime-types@^2.1.12, mime-types@~2.1.7: dependencies: mime-db "~1.27.0" +mime-types@^2.1.18, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + mime-types@~2.1.19: version "2.1.34" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" @@ -3391,6 +3601,11 @@ mime@^1.2.11: resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.6.tgz#591d84d3653a6b0b4a3b9df8de5aa8108e72e5e0" integrity sha1-WR2E02U6awtKO5343lqoEI5y5eA= +mime@^1.3.4: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -3480,6 +3695,11 @@ ndjson@^1.4.3: split2 "^2.1.0" through2 "^2.0.3" +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -3512,6 +3732,22 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= +node-mocks-http@^1.5.8: + version "1.11.0" + resolved "https://registry.yarnpkg.com/node-mocks-http/-/node-mocks-http-1.11.0.tgz#defc0febf6b935f08245397d47534a8de592996e" + integrity sha512-jS/WzSOcKbOeGrcgKbenZeNhxUNnP36Yw11+hL4TTxQXErGfqYZ+MaYNNvhaTiGIJlzNSqgQkk9j8dSu1YWSuw== + dependencies: + accepts "^1.3.7" + content-disposition "^0.5.3" + depd "^1.1.0" + fresh "^0.5.2" + merge-descriptors "^1.0.1" + methods "^1.1.2" + mime "^1.3.4" + parseurl "^1.3.3" + range-parser "^1.2.0" + type-is "^1.6.18" + node-modules-regexp@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" @@ -3618,6 +3854,13 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" +on-finished@^2.3.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -3632,6 +3875,11 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" +only@~0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" + integrity sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q= + optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -3698,6 +3946,11 @@ parse5@6.0.1: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== +parseurl@^1.3.2, parseurl@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" @@ -4168,6 +4421,11 @@ querystring@0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= +range-parser@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + react-is@^17.0.1: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" @@ -4400,7 +4658,7 @@ rsvp@^4.8.4: resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== -safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -4491,6 +4749,11 @@ set-value@^2.0.0, set-value@^2.0.1: is-plain-object "^2.0.3" split-string "^3.0.1" +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -4719,6 +4982,11 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" +"statuses@>= 1.5.0 < 2", statuses@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + step@0.0.x: version "0.0.6" resolved "https://registry.yarnpkg.com/step/-/step-0.0.6.tgz#143e7849a5d7d3f4a088fe29af94915216eeede2" @@ -4947,6 +5215,11 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + "tough-cookie@^2.3.3 || ^3.0.1 || ^4.0.0", tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -4983,6 +5256,11 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= +tsscmp@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb" + integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA== + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -5022,6 +5300,14 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-is@^1.6.16, type-is@^1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -5134,6 +5420,11 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +vary@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + verror@1.3.6: version "1.3.6" resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" @@ -5349,6 +5640,11 @@ yargs@^15.4.1: y18n "^4.0.0" yargs-parser "^18.1.2" +ylru@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/ylru/-/ylru-1.3.2.tgz#0de48017473275a4cbdfc83a1eaf67c01af8a785" + integrity sha512-RXRJzMiK6U2ye0BlGGZnmpwJDPgakn6aNQ0A7gHRbD4I0uvK4TW6UqkK1V0pp9jskjJBAXd3dRrbzWkqJ+6cxA== + zlib@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/zlib/-/zlib-1.0.5.tgz#6e7c972fc371c645a6afb03ab14769def114fcc0" diff --git a/packages/server/src/api/routes/tests/automation.spec.js b/packages/server/src/api/routes/tests/automation.spec.js index 4e95e6ccca..5c4936e4ea 100644 --- a/packages/server/src/api/routes/tests/automation.spec.js +++ b/packages/server/src/api/routes/tests/automation.spec.js @@ -6,8 +6,8 @@ const { } = require("./utilities/TestFunctions") const setup = require("./utilities") const { basicAutomation } = setup.structures -const { testUtils } = require("@budibase/backend-core") -testUtils.mocks.date.mock() +const { mocks } = require("@budibase/backend-core/testUtils") +mocks.date.mock() const MAX_RETRIES = 4 let ACTION_DEFINITIONS = {} diff --git a/packages/server/src/api/routes/tests/datasource.spec.js b/packages/server/src/api/routes/tests/datasource.spec.js index 68f8134492..97ee0c3c9c 100644 --- a/packages/server/src/api/routes/tests/datasource.spec.js +++ b/packages/server/src/api/routes/tests/datasource.spec.js @@ -5,8 +5,9 @@ let { basicDatasource } = setup.structures let { checkBuilderEndpoint } = require("./utilities/TestFunctions") const pg = require("pg") const { checkCacheForDynamicVariable } = require("../../../threads/utils") -const { events, testUtils } = require("@budibase/backend-core") -testUtils.mocks.date.mock() +const { events } = require("@budibase/backend-core") +const { mocks } = require("@budibase/backend-core/testUtils") +mocks.date.mock() describe("/datasources", () => { let request = setup.getRequest() diff --git a/packages/server/src/api/routes/tests/query.spec.js b/packages/server/src/api/routes/tests/query.spec.js index 5dc17a53a4..7ac757c7e9 100644 --- a/packages/server/src/api/routes/tests/query.spec.js +++ b/packages/server/src/api/routes/tests/query.spec.js @@ -13,8 +13,8 @@ const setup = require("./utilities") const { checkBuilderEndpoint } = require("./utilities/TestFunctions") const { checkCacheForDynamicVariable } = require("../../../threads/utils") const { basicQuery, basicDatasource } = setup.structures -const { testUtils } = require("@budibase/backend-core") -testUtils.mocks.date.mock() +const { mocks } = require("@budibase/backend-core/testUtils") +mocks.date.mock() describe("/queries", () => { let request = setup.getRequest() diff --git a/packages/server/src/tests/utilities/mocks/core.js b/packages/server/src/tests/utilities/mocks/core.js new file mode 100644 index 0000000000..2e15ade664 --- /dev/null +++ b/packages/server/src/tests/utilities/mocks/core.js @@ -0,0 +1,3 @@ +const utils = require("@budibase/backend-core/testUtils") +const core = require("@budibase/backend-core") +core.events = utils.mocks.events diff --git a/packages/server/src/tests/utilities/mocks/events.js b/packages/server/src/tests/utilities/mocks/events.js deleted file mode 100644 index 10ac9b7f4f..0000000000 --- a/packages/server/src/tests/utilities/mocks/events.js +++ /dev/null @@ -1,2 +0,0 @@ -const core = require("@budibase/backend-core") -core.events = core.testUtils.mocks.events diff --git a/packages/server/src/tests/utilities/mocks/index.js b/packages/server/src/tests/utilities/mocks/index.js index 8f2f873a17..1e7730932f 100644 --- a/packages/server/src/tests/utilities/mocks/index.js +++ b/packages/server/src/tests/utilities/mocks/index.js @@ -1 +1 @@ -require("./events") +require("./core") diff --git a/packages/worker/src/api/controllers/global/configs.js b/packages/worker/src/api/controllers/global/configs.js index 2562dd4e2e..997615b34e 100644 --- a/packages/worker/src/api/controllers/global/configs.js +++ b/packages/worker/src/api/controllers/global/configs.js @@ -14,13 +14,72 @@ const { const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy") const env = require("../../../environment") const { googleCallbackUrl, oidcCallbackUrl } = require("./auth") +// const { events } = require("@budibase/backend-core") const BB_TENANT_CDN = "https://tenants.cdn.budi.live" +// const getEventFns = (db, config) => { +// const fns = [] +// const isNew = !!config._id +// const type = config.type + +// let existing +// if (!isNew) { +// existing = db.get(config._id) +// } +// if (!existing) { +// switch (config.type) { +// case Configs.SMTP: +// fns.push(events.emailSMTPCreated) +// break +// case Configs.GOOGLE: +// fns.push(() => events.authSSOCreated(type)) +// break +// case Configs.OIDC: +// fns.push(() => events.authSSOCreated(type)) +// break +// case Configs.SETTINGS: +// if (config.company) { +// fns.push(events.orgNameUpdated) +// } +// if (config.logoUrl) { +// fns.push(events.orgLogoUpdated) +// } +// if (config.platformUrl) { +// fns.push(events.orgPlatformURLUpdated) +// } +// break +// } +// } else { +// switch (config.type) { +// case Configs.SMTP: +// fns.push(events.emailSMTPUpdated) +// break +// case Configs.GOOGLE: +// fns.push(() => events.authSSOUpdated(type)) +// break +// case Configs.OIDC: +// fns.push(() => events.authSSOUpdated(type)) +// break +// case Configs.SETTINGS: +// if (config.company && existing.company !== config.company) { +// fns.push(events.orgNameUpdated) +// } +// if (config.logoUrl && existing.logoUrl !== config.logoUrl) { +// fns.push(events.orgLogoUpdated) +// } +// if (config.platformUrl && existing.platformUrl !== config.platformUrl) { +// fns.push(events.orgPlatformURLUpdated) +// } +// break +// } +// } +// } + exports.save = async function (ctx) { const db = getGlobalDB() const { type, workspace, user, config } = ctx.request.body - + // let eventFns = getEventFns(db, ctx.request.body) // Config does not exist yet if (!ctx.request.body._id) { ctx.request.body._id = generateConfigID({ @@ -29,7 +88,6 @@ exports.save = async function (ctx) { user, }) } - try { // verify the configuration switch (type) { @@ -43,6 +101,9 @@ exports.save = async function (ctx) { try { const response = await db.put(ctx.request.body) + // for (const fn of eventFns) { + // fn() + // } ctx.body = { type, _id: response.id, diff --git a/packages/worker/src/api/routes/tests/auth.spec.js b/packages/worker/src/api/routes/tests/auth.spec.js index ec99117c2f..419c477882 100644 --- a/packages/worker/src/api/routes/tests/auth.spec.js +++ b/packages/worker/src/api/routes/tests/auth.spec.js @@ -1,6 +1,7 @@ jest.mock("nodemailer") const setup = require("./utilities") const sendMailMock = setup.emailMock() +const { events } = require("@budibase/backend-core") const TENANT_ID = "default" @@ -19,6 +20,14 @@ describe("/api/global/auth", () => { jest.clearAllMocks() }) + it("should logout", async () => { + await request + .post("/api/global/auth/logout") + .set(config.defaultHeaders()) + .expect(200) + expect(events.auth.logout.mock.calls.length).toBe(1) + }) + it("should be able to generate password reset email", async () => { // initially configure settings await config.saveSmtpConfig() diff --git a/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js b/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js index d297d818bd..0de0ca3234 100644 --- a/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js +++ b/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js @@ -1,3 +1,4 @@ +require("./mocks") require("../../../../db").init() const env = require("../../../../environment") const controllers = require("./controllers") diff --git a/packages/worker/src/api/routes/tests/utilities/mocks/core.js b/packages/worker/src/api/routes/tests/utilities/mocks/core.js new file mode 100644 index 0000000000..2e15ade664 --- /dev/null +++ b/packages/worker/src/api/routes/tests/utilities/mocks/core.js @@ -0,0 +1,3 @@ +const utils = require("@budibase/backend-core/testUtils") +const core = require("@budibase/backend-core") +core.events = utils.mocks.events diff --git a/packages/worker/src/api/routes/tests/utilities/mocks/index.js b/packages/worker/src/api/routes/tests/utilities/mocks/index.js new file mode 100644 index 0000000000..1e7730932f --- /dev/null +++ b/packages/worker/src/api/routes/tests/utilities/mocks/index.js @@ -0,0 +1 @@ +require("./core") From aa662a3744dbe0835974449d7ac009a8ac413f27 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 5 Apr 2022 21:53:27 +0100 Subject: [PATCH 10/75] account event mocks --- packages/backend-core/src/events/handlers/index.js | 2 ++ packages/backend-core/src/tests/utilities/mocks/events.js | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/packages/backend-core/src/events/handlers/index.js b/packages/backend-core/src/events/handlers/index.js index dc2fd4ea10..8cd523e2c0 100644 --- a/packages/backend-core/src/events/handlers/index.js +++ b/packages/backend-core/src/events/handlers/index.js @@ -1,3 +1,4 @@ +const account = require("./account") const app = require("./app") const auth = require("./auth") const automation = require("./automation") @@ -14,6 +15,7 @@ const user = require("./user") const view = require("./view") module.exports = { + account, app, auth, automation, diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index 203af45497..97caac4171 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -1,5 +1,10 @@ jest.mock("../../../events", () => { return { + account: { + created: jest.fn(), + deleted: jest.fn(), + verified: jest.fn(), + }, app: { created: jest.fn(), updated: jest.fn(), From d706ec8efd0921a06b8eefb9493eb9cea280e7bc Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 5 Apr 2022 23:14:53 +0100 Subject: [PATCH 11/75] google/oidc created/deleted events + tests --- .../src/tests/utilities/mocks/events.js | 8 ++ .../src/api/controllers/global/configs.js | 124 +++++++++--------- .../src/api/routes/tests/configs.spec.js | 69 +++++++++- 3 files changed, 139 insertions(+), 62 deletions(-) diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index 97caac4171..f6eb2eddcc 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -21,12 +21,20 @@ jest.mock("../../../events", () => { auth: { login: jest.fn(), logout: jest.fn(), + SSOCreated: jest.fn(), + SSOUpdated: jest.fn(), + SSOActivated: jest.fn(), + SSODeactivated: jest.fn(), }, datasource: { created: jest.fn(), updated: jest.fn(), deleted: jest.fn(), }, + email: { + SMTPCreated: jest.fn(), + SMTPUpdated: jest.fn(), + }, } }) diff --git a/packages/worker/src/api/controllers/global/configs.js b/packages/worker/src/api/controllers/global/configs.js index 997615b34e..0b388edbad 100644 --- a/packages/worker/src/api/controllers/global/configs.js +++ b/packages/worker/src/api/controllers/global/configs.js @@ -14,72 +14,74 @@ const { const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy") const env = require("../../../environment") const { googleCallbackUrl, oidcCallbackUrl } = require("./auth") -// const { events } = require("@budibase/backend-core") +const { events } = require("@budibase/backend-core") const BB_TENANT_CDN = "https://tenants.cdn.budi.live" -// const getEventFns = (db, config) => { -// const fns = [] -// const isNew = !!config._id -// const type = config.type +const getEventFns = async (db, config) => { + const fns = [] + const type = config.type -// let existing -// if (!isNew) { -// existing = db.get(config._id) -// } -// if (!existing) { -// switch (config.type) { -// case Configs.SMTP: -// fns.push(events.emailSMTPCreated) -// break -// case Configs.GOOGLE: -// fns.push(() => events.authSSOCreated(type)) -// break -// case Configs.OIDC: -// fns.push(() => events.authSSOCreated(type)) -// break -// case Configs.SETTINGS: -// if (config.company) { -// fns.push(events.orgNameUpdated) -// } -// if (config.logoUrl) { -// fns.push(events.orgLogoUpdated) -// } -// if (config.platformUrl) { -// fns.push(events.orgPlatformURLUpdated) -// } -// break -// } -// } else { -// switch (config.type) { -// case Configs.SMTP: -// fns.push(events.emailSMTPUpdated) -// break -// case Configs.GOOGLE: -// fns.push(() => events.authSSOUpdated(type)) -// break -// case Configs.OIDC: -// fns.push(() => events.authSSOUpdated(type)) -// break -// case Configs.SETTINGS: -// if (config.company && existing.company !== config.company) { -// fns.push(events.orgNameUpdated) -// } -// if (config.logoUrl && existing.logoUrl !== config.logoUrl) { -// fns.push(events.orgLogoUpdated) -// } -// if (config.platformUrl && existing.platformUrl !== config.platformUrl) { -// fns.push(events.orgPlatformURLUpdated) -// } -// break -// } -// } -// } + let existing + if (config._id) { + existing = await db.get(config._id) + } + + if (!existing) { + switch (config.type) { + case Configs.SMTP: + fns.push(events.email.SMTPCreated) + break + case Configs.GOOGLE: + fns.push(() => events.auth.SSOCreated(type)) + break + case Configs.OIDC: + fns.push(() => events.auth.SSOCreated(type)) + break + case Configs.SETTINGS: + if (config.company) { + fns.push(events.org.nameUpdated) + } + if (config.logoUrl) { + fns.push(events.org.logoUpdated) + } + if (config.platformUrl) { + fns.push(events.org.platformURLUpdated) + } + break + } + } else { + switch (config.type) { + case Configs.SMTP: + fns.push(events.email.SMTPUpdated) + break + case Configs.GOOGLE: + fns.push(() => events.auth.SSOUpdated(type)) + break + case Configs.OIDC: + fns.push(() => events.auth.SSOUpdated(type)) + break + case Configs.SETTINGS: + if (config.company && existing.company !== config.company) { + fns.push(events.org.nameUpdated) + } + if (config.logoUrl && existing.logoUrl !== config.logoUrl) { + fns.push(events.org.logoUpdated) + } + if (config.platformUrl && existing.platformUrl !== config.platformUrl) { + fns.push(events.org.platformURLUpdated) + } + break + } + } + + return fns +} exports.save = async function (ctx) { const db = getGlobalDB() const { type, workspace, user, config } = ctx.request.body - // let eventFns = getEventFns(db, ctx.request.body) + let eventFns = await getEventFns(db, ctx.request.body) // Config does not exist yet if (!ctx.request.body._id) { ctx.request.body._id = generateConfigID({ @@ -101,9 +103,9 @@ exports.save = async function (ctx) { try { const response = await db.put(ctx.request.body) - // for (const fn of eventFns) { - // fn() - // } + for (const fn of eventFns) { + fn() + } ctx.body = { type, _id: response.id, diff --git a/packages/worker/src/api/routes/tests/configs.spec.js b/packages/worker/src/api/routes/tests/configs.spec.js index dfb1f49c58..76cb6d447e 100644 --- a/packages/worker/src/api/routes/tests/configs.spec.js +++ b/packages/worker/src/api/routes/tests/configs.spec.js @@ -2,8 +2,10 @@ jest.mock("nodemailer") const setup = require("./utilities") setup.emailMock() +const { Configs } = require("@budibase/backend-core/constants") +const { events } = require("@budibase/backend-core") -describe("/api/global/configs/checklist", () => { +describe("configs", () => { let request = setup.getRequest() let config = setup.getConfig() @@ -11,8 +13,73 @@ describe("/api/global/configs/checklist", () => { await config.init() }) + beforeEach(() => { + jest.clearAllMocks() + }) + afterAll(setup.afterAll) + describe("post /api/global/configs", () => { + + const saveConfig = async (type, _id, _rev) => { + const data = { + type, + _id, + _rev + } + const res = await request + .post(`/api/global/configs`) + .send(data) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + + return res.body + } + + describe("google", () => { + const saveGoogleConfig = async (_id, _rev) => { + return saveConfig(Configs.GOOGLE, _id, _rev) + } + + it ("should create google config", async () => { + await saveGoogleConfig() + expect(events.auth.SSOCreated).toBeCalledTimes(1) + expect(events.auth.SSOCreated).toBeCalledWith(Configs.GOOGLE) + await config.deleteConfig(Configs.GOOGLE) + }) + + it ("should update google config", async () => { + const googleConf = await saveGoogleConfig() + await saveGoogleConfig(googleConf._id, googleConf._rev) + expect(events.auth.SSOUpdated).toBeCalledTimes(1) + expect(events.auth.SSOUpdated).toBeCalledWith(Configs.GOOGLE) + await config.deleteConfig(Configs.GOOGLE) + }) + }) + + describe("oidc", () => { + const saveOIDCConfig = async (_id, _rev) => { + return saveConfig(Configs.OIDC, _id, _rev) + } + + it ("should create OIDC config", async () => { + await saveOIDCConfig() + expect(events.auth.SSOCreated).toBeCalledTimes(1) + expect(events.auth.SSOCreated).toBeCalledWith(Configs.OIDC) + await config.deleteConfig(Configs.OIDC) + }) + + it ("should update OIDC config", async () => { + const oidcConf = await saveOIDCConfig() + await saveOIDCConfig(oidcConf._id, oidcConf._rev) + expect(events.auth.SSOUpdated).toBeCalledTimes(1) + expect(events.auth.SSOUpdated).toBeCalledWith(Configs.OIDC) + await config.deleteConfig(Configs.OIDC) + }) + }) + }) + it("should return the correct checklist status based on the state of the budibase installation", async () => { await config.saveSmtpConfig() From 82597a1afaf33ee1ea70e2f2a67e1f2d830fd3a6 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 6 Apr 2022 00:54:07 +0100 Subject: [PATCH 12/75] sso activated/deactivated events + tests --- .../backend-core/src/events/handlers/auth.js | 4 - .../src/api/controllers/global/configs.js | 22 +++ .../src/api/routes/tests/configs.spec.js | 155 ++++++++++++++---- 3 files changed, 145 insertions(+), 36 deletions(-) diff --git a/packages/backend-core/src/events/handlers/auth.js b/packages/backend-core/src/events/handlers/auth.js index 41b3a7535b..98bbbd2994 100644 --- a/packages/backend-core/src/events/handlers/auth.js +++ b/packages/backend-core/src/events/handlers/auth.js @@ -13,7 +13,6 @@ exports.logout = () => { events.processEvent(Events.AUTH_LOGOUT, properties) } -// TODO exports.SSOCreated = type => { const properties = { type, @@ -21,7 +20,6 @@ exports.SSOCreated = type => { events.processEvent(Events.AUTH_SSO_CREATED, properties) } -// TODO exports.SSOUpdated = type => { const properties = { type, @@ -29,7 +27,6 @@ exports.SSOUpdated = type => { events.processEvent(Events.AUTH_SSO_UPDATED, properties) } -// TODO exports.SSOActivated = type => { const properties = { type, @@ -37,7 +34,6 @@ exports.SSOActivated = type => { events.processEvent(Events.AUTH_SSO_ACTIVATED, properties) } -// TODO exports.SSODeactivated = type => { const properties = { type, diff --git a/packages/worker/src/api/controllers/global/configs.js b/packages/worker/src/api/controllers/global/configs.js index 0b388edbad..9ee2648d89 100644 --- a/packages/worker/src/api/controllers/global/configs.js +++ b/packages/worker/src/api/controllers/global/configs.js @@ -34,9 +34,15 @@ const getEventFns = async (db, config) => { break case Configs.GOOGLE: fns.push(() => events.auth.SSOCreated(type)) + if (config.config.activated) { + fns.push(() => events.auth.SSOActivated(type)) + } break case Configs.OIDC: fns.push(() => events.auth.SSOCreated(type)) + if (config.config.configs[0].activated) { + fns.push(() => events.auth.SSOActivated(type)) + } break case Configs.SETTINGS: if (config.company) { @@ -57,9 +63,25 @@ const getEventFns = async (db, config) => { break case Configs.GOOGLE: fns.push(() => events.auth.SSOUpdated(type)) + if (!existing.config.activated && config.config.activated) { + fns.push(() => events.auth.SSOActivated(type)) + } else if (existing.config.activated && !config.config.activated) { + fns.push(() => events.auth.SSODeactivated(type)) + } break case Configs.OIDC: fns.push(() => events.auth.SSOUpdated(type)) + if ( + !existing.config.configs[0].activated && + config.config.configs[0].activated + ) { + fns.push(() => events.auth.SSOActivated(type)) + } else if ( + existing.config.configs[0].activated && + !config.config.configs[0].activated + ) { + fns.push(() => events.auth.SSODeactivated(type)) + } break case Configs.SETTINGS: if (config.company && existing.company !== config.company) { diff --git a/packages/worker/src/api/routes/tests/configs.spec.js b/packages/worker/src/api/routes/tests/configs.spec.js index 76cb6d447e..a89e394fdb 100644 --- a/packages/worker/src/api/routes/tests/configs.spec.js +++ b/packages/worker/src/api/routes/tests/configs.spec.js @@ -17,16 +17,20 @@ describe("configs", () => { jest.clearAllMocks() }) - afterAll(setup.afterAll) + afterAll(async () => { + await setup.afterAll() + }) describe("post /api/global/configs", () => { - const saveConfig = async (type, _id, _rev) => { + const saveConfig = async (conf, type, _id, _rev) => { const data = { type, + config: conf, _id, _rev } + const res = await request .post(`/api/global/configs`) .send(data) @@ -34,49 +38,136 @@ describe("configs", () => { .expect("Content-Type", /json/) .expect(200) - return res.body + return { + ...data, + ...res.body + } } describe("google", () => { - const saveGoogleConfig = async (_id, _rev) => { - return saveConfig(Configs.GOOGLE, _id, _rev) + const saveGoogleConfig = async (conf, _id, _rev) => { + const googleConfig = { + clientID: "clientID", + clientSecret: "clientSecret", + activated: true, + ...conf + } + + return saveConfig(googleConfig, Configs.GOOGLE, _id, _rev) } - it ("should create google config", async () => { - await saveGoogleConfig() - expect(events.auth.SSOCreated).toBeCalledTimes(1) - expect(events.auth.SSOCreated).toBeCalledWith(Configs.GOOGLE) - await config.deleteConfig(Configs.GOOGLE) - }) - - it ("should update google config", async () => { - const googleConf = await saveGoogleConfig() - await saveGoogleConfig(googleConf._id, googleConf._rev) - expect(events.auth.SSOUpdated).toBeCalledTimes(1) - expect(events.auth.SSOUpdated).toBeCalledWith(Configs.GOOGLE) - await config.deleteConfig(Configs.GOOGLE) + describe("create", () => { + it ("should create activated google config", async () => { + await saveGoogleConfig() + expect(events.auth.SSOCreated).toBeCalledTimes(1) + expect(events.auth.SSOCreated).toBeCalledWith(Configs.GOOGLE) + expect(events.auth.SSODeactivated).not.toBeCalled() + expect(events.auth.SSOActivated).toBeCalledTimes(1) + expect(events.auth.SSOActivated).toBeCalledWith(Configs.GOOGLE) + await config.deleteConfig(Configs.GOOGLE) + }) + + it ("should create deactivated google config", async () => { + await saveGoogleConfig({ activated: false }) + expect(events.auth.SSOCreated).toBeCalledTimes(1) + expect(events.auth.SSOCreated).toBeCalledWith(Configs.GOOGLE) + expect(events.auth.SSOActivated).not.toBeCalled() + expect(events.auth.SSODeactivated).not.toBeCalled() + await config.deleteConfig(Configs.GOOGLE) + }) }) + + describe("update", () => { + it ("should update google config to deactivated", async () => { + const googleConf = await saveGoogleConfig() + jest.clearAllMocks() + await saveGoogleConfig({ ...googleConf.config, activated: false }, googleConf._id, googleConf._rev) + expect(events.auth.SSOUpdated).toBeCalledTimes(1) + expect(events.auth.SSOUpdated).toBeCalledWith(Configs.GOOGLE) + expect(events.auth.SSOActivated).not.toBeCalled() + expect(events.auth.SSODeactivated).toBeCalledTimes(1) + expect(events.auth.SSODeactivated).toBeCalledWith(Configs.GOOGLE) + await config.deleteConfig(Configs.GOOGLE) + }) + + it ("should update google config to activated", async () => { + const googleConf = await saveGoogleConfig({ activated: false }) + jest.clearAllMocks() + await saveGoogleConfig({ ...googleConf.config, activated: true}, googleConf._id, googleConf._rev) + expect(events.auth.SSOUpdated).toBeCalledTimes(1) + expect(events.auth.SSOUpdated).toBeCalledWith(Configs.GOOGLE) + expect(events.auth.SSODeactivated).not.toBeCalled() + expect(events.auth.SSOActivated).toBeCalledTimes(1) + expect(events.auth.SSOActivated).toBeCalledWith(Configs.GOOGLE) + await config.deleteConfig(Configs.GOOGLE) + }) + }) }) describe("oidc", () => { - const saveOIDCConfig = async (_id, _rev) => { - return saveConfig(Configs.OIDC, _id, _rev) + const saveOIDCConfig = async (conf, _id, _rev) => { + const oidcConfig = { + configs: [{ + clientID: "clientID", + clientSecret: "clientSecret", + configUrl: "http://example.com", + logo: "logo", + name: "oidc", + uuid: "uuid", + activated: true, + ...conf + }] + } + return saveConfig(oidcConfig, Configs.OIDC, _id, _rev) } - it ("should create OIDC config", async () => { - await saveOIDCConfig() - expect(events.auth.SSOCreated).toBeCalledTimes(1) - expect(events.auth.SSOCreated).toBeCalledWith(Configs.OIDC) - await config.deleteConfig(Configs.OIDC) + describe("create", () => { + it ("should create activated OIDC config", async () => { + await saveOIDCConfig() + expect(events.auth.SSOCreated).toBeCalledTimes(1) + expect(events.auth.SSOCreated).toBeCalledWith(Configs.OIDC) + expect(events.auth.SSODeactivated).not.toBeCalled() + expect(events.auth.SSOActivated).toBeCalledTimes(1) + expect(events.auth.SSOActivated).toBeCalledWith(Configs.OIDC) + await config.deleteConfig(Configs.OIDC) + }) + + it ("should create deactivated OIDC config", async () => { + await saveOIDCConfig({ activated: false }) + expect(events.auth.SSOCreated).toBeCalledTimes(1) + expect(events.auth.SSOCreated).toBeCalledWith(Configs.OIDC) + expect(events.auth.SSOActivated).not.toBeCalled() + expect(events.auth.SSODeactivated).not.toBeCalled() + await config.deleteConfig(Configs.OIDC) + }) }) - - it ("should update OIDC config", async () => { - const oidcConf = await saveOIDCConfig() - await saveOIDCConfig(oidcConf._id, oidcConf._rev) - expect(events.auth.SSOUpdated).toBeCalledTimes(1) - expect(events.auth.SSOUpdated).toBeCalledWith(Configs.OIDC) - await config.deleteConfig(Configs.OIDC) + + describe("update", () => { + it ("should update OIDC config to deactivated", async () => { + const oidcConf = await saveOIDCConfig() + jest.clearAllMocks() + await saveOIDCConfig({ ...oidcConf.config.configs[0], activated: false }, oidcConf._id, oidcConf._rev) + expect(events.auth.SSOUpdated).toBeCalledTimes(1) + expect(events.auth.SSOUpdated).toBeCalledWith(Configs.OIDC) + expect(events.auth.SSOActivated).not.toBeCalled() + expect(events.auth.SSODeactivated).toBeCalledTimes(1) + expect(events.auth.SSODeactivated).toBeCalledWith(Configs.OIDC) + await config.deleteConfig(Configs.OIDC) + }) + + it ("should update google config to activated", async () => { + const oidcConf = await saveOIDCConfig({ activated: false }) + jest.clearAllMocks() + await saveOIDCConfig({ ...oidcConf.config.configs[0], activated: true}, oidcConf._id, oidcConf._rev) + expect(events.auth.SSOUpdated).toBeCalledTimes(1) + expect(events.auth.SSOUpdated).toBeCalledWith(Configs.OIDC) + expect(events.auth.SSODeactivated).not.toBeCalled() + expect(events.auth.SSOActivated).toBeCalledTimes(1) + expect(events.auth.SSOActivated).toBeCalledWith(Configs.OIDC) + await config.deleteConfig(Configs.OIDC) + }) }) + }) }) From ec4e9df4c2b4f1fa9fb5abb9175d3dd13292604e Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 6 Apr 2022 12:34:31 +0100 Subject: [PATCH 13/75] Refactor automation tests to run independently --- .../src/api/routes/tests/automation.spec.js | 112 +++++++++--------- packages/server/src/automations/index.js | 5 + .../server/src/tests/utilities/structures.js | 36 ++++++ .../src/api/routes/tests/configs.spec.js | 2 +- 4 files changed, 96 insertions(+), 59 deletions(-) diff --git a/packages/server/src/api/routes/tests/automation.spec.js b/packages/server/src/api/routes/tests/automation.spec.js index 5c4936e4ea..cce51fc94b 100644 --- a/packages/server/src/api/routes/tests/automation.spec.js +++ b/packages/server/src/api/routes/tests/automation.spec.js @@ -5,18 +5,15 @@ const { testAutomation, } = require("./utilities/TestFunctions") const setup = require("./utilities") -const { basicAutomation } = setup.structures +const { basicAutomation, newAutomation, automationTrigger, automationStep } = setup.structures const { mocks } = require("@budibase/backend-core/testUtils") mocks.date.mock() const MAX_RETRIES = 4 - -let ACTION_DEFINITIONS = {} -let TRIGGER_DEFINITIONS = {} +const { TRIGGER_DEFINITIONS, ACTION_DEFINITIONS } = require("../../../automations") describe("/automations", () => { let request = setup.getRequest() let config = setup.getConfig() - let automation afterAll(setup.afterAll) @@ -33,7 +30,6 @@ describe("/automations", () => { .expect(200) expect(Object.keys(res.body).length).not.toEqual(0) - ACTION_DEFINITIONS = res.body }) it("returns a list of definitions for triggerInfo", async () => { @@ -44,7 +40,6 @@ describe("/automations", () => { .expect(200) expect(Object.keys(res.body).length).not.toEqual(0) - TRIGGER_DEFINITIONS = res.body }) it("returns all of the definitions in one", async () => { @@ -54,76 +49,37 @@ describe("/automations", () => { .expect('Content-Type', /json/) .expect(200) - expect(Object.keys(res.body.action).length).toBeGreaterThanOrEqual(Object.keys(ACTION_DEFINITIONS).length) + let definitionsLength = Object.keys(ACTION_DEFINITIONS).length + definitionsLength-- // OUTGOING_WEBHOOK is deprecated + + expect(Object.keys(res.body.action).length).toBeGreaterThanOrEqual(definitionsLength) expect(Object.keys(res.body.trigger).length).toEqual(Object.keys(TRIGGER_DEFINITIONS).length) }) }) describe("create", () => { - const autoConfig = basicAutomation() - it("should setup the automation fully", () => { - let trigger = TRIGGER_DEFINITIONS["ROW_SAVED"] - trigger.id = "wadiawdo34" - let createAction = ACTION_DEFINITIONS["CREATE_ROW"] - createAction.inputs.row = { - name: "{{trigger.row.name}}", - description: "{{trigger.row.description}}" - } - createAction.id = "awde444wk" - - autoConfig.definition.steps.push(createAction) - autoConfig.definition.trigger = trigger - }) - it("returns a success message when the automation is successfully created", async () => { + const automation = newAutomation() + const res = await request .post(`/api/automations`) .set(config.defaultHeaders()) - .send(autoConfig) + .send(automation) .expect('Content-Type', /json/) .expect(200) expect(res.body.message).toEqual("Automation created successfully") expect(res.body.automation.name).toEqual("My Automation") expect(res.body.automation._id).not.toEqual(null) - automation = res.body.automation - }) - - it("should be able to create an automation with a webhook trigger", async () => { - const autoConfig = basicAutomation() - autoConfig.definition.trigger = TRIGGER_DEFINITIONS["WEBHOOK"] - autoConfig.definition.trigger.id = "webhook_trigger_id" - const res = await request - .post(`/api/automations`) - .set(config.defaultHeaders()) - .send(autoConfig) - .expect('Content-Type', /json/) - .expect(200) - const originalAuto = res.body.automation - expect(originalAuto._id).toBeDefined() - expect(originalAuto._rev).toBeDefined() - // try removing the webhook trigger - const newConfig = originalAuto - newConfig.definition.trigger = TRIGGER_DEFINITIONS["ROW_SAVED"] - newConfig.definition.trigger.id = "row_saved_id" - const newRes = await request - .post(`/api/automations`) - .set(config.defaultHeaders()) - .send(newConfig) - .expect('Content-Type', /json/) - .expect(200) - const newAuto = newRes.body.automation - expect(newAuto._id).toEqual(originalAuto._id) - expect(newAuto._rev).toBeDefined() - expect(newAuto._rev).not.toEqual(originalAuto._rev) }) it("should apply authorization to endpoint", async () => { + const automation = newAutomation() await checkBuilderEndpoint({ config, method: "POST", url: `/api/automations`, - body: autoConfig + body: automation }) }) }) @@ -144,8 +100,15 @@ describe("/automations", () => { describe("trigger", () => { it("trigger the automation successfully", async () => { let table = await config.createTable() + let automation = newAutomation() automation.definition.trigger.inputs.tableId = table._id - automation.definition.steps[0].inputs.row.tableId = table._id + automation.definition.steps[0].inputs = { + row: { + name: "{{trigger.row.name}}", + description: "{{trigger.row.description}}", + tableId: table._id + } + } automation.appId = config.appId automation = await config.createAutomation(automation) await setup.delay(500) @@ -172,9 +135,9 @@ describe("/automations", () => { describe("update", () => { it("updates a automations data", async () => { - automation = await config.createAutomation(automation) + let automation = newAutomation() + await config.createAutomation(automation) automation.name = "Updated Name" - automation.type = "automation" const res = await request .put(`/api/automations`) @@ -186,6 +149,39 @@ describe("/automations", () => { expect(res.body.message).toEqual(`Automation ${automation._id} updated successfully.`) expect(res.body.automation.name).toEqual("Updated Name") }) + + it("should be able to update an automation trigger", async () => { + // create webhook automation + const webhookTrigger = automationTrigger(TRIGGER_DEFINITIONS.WEBHOOK) + let automation = newAutomation({ trigger: webhookTrigger }) + + let res = await request + .post(`/api/automations`) + .set(config.defaultHeaders()) + .send(automation) + .expect('Content-Type', /json/) + .expect(200) + + automation = res.body.automation + expect(automation._id).toBeDefined() + expect(automation._rev).toBeDefined() + + // change the trigger + automation.trigger = automationTrigger(TRIGGER_DEFINITIONS.ROW_SAVED) + + // check the post request honours updates with same id + res = await request + .post(`/api/automations`) + .set(config.defaultHeaders()) + .send(automation) + .expect('Content-Type', /json/) + .expect(200) + + const automationRes = res.body.automation + expect(automationRes._id).toEqual(automation._id) + expect(automationRes._rev).toBeDefined() + expect(automationRes._rev).not.toEqual(automation._rev) + }) }) describe("fetch", () => { diff --git a/packages/server/src/automations/index.js b/packages/server/src/automations/index.js index 87f35ce763..6fd3ca87a9 100644 --- a/packages/server/src/automations/index.js +++ b/packages/server/src/automations/index.js @@ -1,5 +1,7 @@ const { processEvent } = require("./utils") const { queue } = require("./bullboard") +const { TRIGGER_DEFINITIONS } = require("./triggers") +const { ACTION_DEFINITIONS } = require("./actions") /** * This module is built purely to kick off the worker farm and manage the inputs/outputs @@ -14,4 +16,7 @@ exports.init = function () { exports.getQueues = () => { return [queue] } + exports.queue = queue +exports.TRIGGER_DEFINITIONS = TRIGGER_DEFINITIONS +exports.ACTION_DEFINITIONS = ACTION_DEFINITIONS diff --git a/packages/server/src/tests/utilities/structures.js b/packages/server/src/tests/utilities/structures.js index 45a8f2dece..4cb6c3b141 100644 --- a/packages/server/src/tests/utilities/structures.js +++ b/packages/server/src/tests/utilities/structures.js @@ -3,6 +3,8 @@ const { BUILTIN_PERMISSION_IDS } = require("@budibase/backend-core/permissions") const { createHomeScreen } = require("../../constants/screens") const { EMPTY_LAYOUT } = require("../../constants/layouts") const { cloneDeep } = require("lodash/fp") +const { v4: uuidv4 } = require("uuid") +const { TRIGGER_DEFINITIONS, ACTION_DEFINITIONS } = require("../../automations") exports.TENANT_ID = "default" @@ -28,6 +30,40 @@ exports.basicTable = () => { } } +exports.automationStep = (actionDefinition = ACTION_DEFINITIONS.CREATE_ROW) => { + return { + id: uuidv4(), + ...actionDefinition, + } +} + +exports.automationTrigger = ( + triggerDefinition = TRIGGER_DEFINITIONS.ROW_SAVED +) => { + return { + id: uuidv4(), + ...triggerDefinition, + } +} + +exports.newAutomation = ({ steps, trigger } = {}) => { + const automation = exports.basicAutomation() + + if (trigger) { + automation.definition.trigger = trigger + } else { + automation.definition.trigger = exports.automationTrigger() + } + + if (steps) { + automation.definition.steps = steps + } else { + automation.definition.steps = [exports.automationStep()] + } + + return automation +} + exports.basicAutomation = () => { return { name: "My Automation", diff --git a/packages/worker/src/api/routes/tests/configs.spec.js b/packages/worker/src/api/routes/tests/configs.spec.js index a89e394fdb..9f0570023e 100644 --- a/packages/worker/src/api/routes/tests/configs.spec.js +++ b/packages/worker/src/api/routes/tests/configs.spec.js @@ -155,7 +155,7 @@ describe("configs", () => { await config.deleteConfig(Configs.OIDC) }) - it ("should update google config to activated", async () => { + it ("should update OIDC config to activated", async () => { const oidcConf = await saveOIDCConfig({ activated: false }) jest.clearAllMocks() await saveOIDCConfig({ ...oidcConf.config.configs[0], activated: true}, oidcConf._id, oidcConf._rev) From 266b34aaa06e0d234a5543e66283b03a28725fae Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 6 Apr 2022 13:54:57 +0100 Subject: [PATCH 14/75] automation events + tests --- packages/backend-core/src/events/constants.js | 3 +- .../src/events/handlers/automation.js | 17 +- .../src/tests/utilities/mocks/events.js | 9 + .../server/src/api/controllers/automation.js | 37 +++- .../src/api/routes/tests/automation.spec.js | 177 ++++++++++++++---- 5 files changed, 200 insertions(+), 43 deletions(-) diff --git a/packages/backend-core/src/events/constants.js b/packages/backend-core/src/events/constants.js index d74004acd0..712e41b17f 100644 --- a/packages/backend-core/src/events/constants.js +++ b/packages/backend-core/src/events/constants.js @@ -125,9 +125,10 @@ exports.Events = { AUTOMATION_CREATED: "automation:created", AUTOMATION_DELETED: "automation:deleted", AUTOMATION_TESTED: "automation:tested", - AUTOMATION_RUN: "automation:run", + // AUTOMATION_RUN: "automation:run", AUTOMATION_STEP_CREATED: "automation:step:created", AUTOMATION_STEP_DELETED: "automation:step:deleted", + AUTOMATION_TRIGGER_UPDATED: "automation:trigger:updated", // LICENSING LICENSING_QUOTA_EXCEEDED: "licensing:quota:exceeded", diff --git a/packages/backend-core/src/events/handlers/automation.js b/packages/backend-core/src/events/handlers/automation.js index 72c2d73d56..2a978b4645 100644 --- a/packages/backend-core/src/events/handlers/automation.js +++ b/packages/backend-core/src/events/handlers/automation.js @@ -6,32 +6,33 @@ exports.created = () => { events.processEvent(Events.AUTOMATION_CREATED, properties) } -// TODO exports.deleted = () => { const properties = {} events.processEvent(Events.AUTOMATION_DELETED, properties) } -// TODO exports.tested = () => { const properties = {} events.processEvent(Events.AUTOMATION_TESTED, properties) } // TODO -exports.run = () => { - const properties = {} - events.processEvent(Events.AUTOMATION_RUN, properties) -} +// exports.run = () => { +// const properties = {} +// events.processEvent(Events.AUTOMATION_RUN, properties) +// } -// TODO exports.stepCreated = () => { const properties = {} events.processEvent(Events.AUTOMATION_STEP_CREATED, properties) } -// TODO exports.stepDeleted = () => { const properties = {} events.processEvent(Events.AUTOMATION_STEP_DELETED, properties) } + +exports.triggerUpdated = () => { + const properties = {} + events.processEvent(Events.AUTOMATION_TRIGGER_UPDATED, properties) +} diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index f6eb2eddcc..74e62ef73c 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -26,6 +26,15 @@ jest.mock("../../../events", () => { SSOActivated: jest.fn(), SSODeactivated: jest.fn(), }, + automation: { + created: jest.fn(), + deleted: jest.fn(), + tested: jest.fn(), + // run: jest.fn(), + stepCreated: jest.fn(), + stepDeleted: jest.fn(), + triggerUpdated: jest.fn(), + }, datasource: { created: jest.fn(), updated: jest.fn(), diff --git a/packages/server/src/api/controllers/automation.js b/packages/server/src/api/controllers/automation.js index 74942dad40..2c4c669392 100644 --- a/packages/server/src/api/controllers/automation.js +++ b/packages/server/src/api/controllers/automation.js @@ -10,6 +10,7 @@ const { deleteEntityMetadata } = require("../../utilities") const { MetadataTypes } = require("../../constants") const { setTestFlag, clearTestFlag } = require("../../utilities/redis") const { getAppDB } = require("@budibase/backend-core/context") +const { events } = require("@budibase/backend-core") const ACTION_DEFS = removeDeprecated(actions.ACTION_DEFINITIONS) const TRIGGER_DEFS = removeDeprecated(triggers.TRIGGER_DEFINITIONS) @@ -70,6 +71,10 @@ exports.create = async function (ctx) { newAuto: automation, }) const response = await db.put(automation) + events.automation.created() + for (let step of automation.definition.steps) { + events.automation.stepCreated(step) + } automation._rev = response.rev ctx.status = 200 @@ -82,6 +87,29 @@ exports.create = async function (ctx) { } } +const getNewSteps = (oldAutomation, automation) => { + const oldStepIds = oldAutomation.definition.steps.map(s => s.id) + return automation.definition.steps.filter(s => !oldStepIds.includes(s.id)) +} + +const getDeletedSteps = (oldAutomation, automation) => { + const stepIds = automation.definition.steps.map(s => s.id) + return oldAutomation.definition.steps.filter(s => !stepIds.includes(s.id)) +} + +const handleStepEvents = (oldAutomation, automation) => { + // new steps + const newSteps = getNewSteps(oldAutomation, automation) + for (let step of newSteps) { + events.automation.stepCreated(step) + } + + // old steps + const deletedSteps = getDeletedSteps(oldAutomation, automation) + for (let step of deletedSteps) { + events.automation.stepDeleted(step) + } +} exports.update = async function (ctx) { const db = getAppDB() let automation = ctx.request.body @@ -98,13 +126,14 @@ exports.update = async function (ctx) { const oldAutoTrigger = oldAutomation && oldAutomation.definition.trigger ? oldAutomation.definition.trigger - : {} + : undefined const newAutoTrigger = automation && automation.definition.trigger ? automation.definition.trigger : {} // trigger has been updated, remove the test inputs - if (oldAutoTrigger.id !== newAutoTrigger.id) { + if (oldAutoTrigger && oldAutoTrigger.id !== newAutoTrigger.id) { + events.automation.triggerUpdated() await deleteEntityMetadata( ctx.appId, MetadataTypes.AUTOMATION_TEST_INPUT, @@ -112,6 +141,8 @@ exports.update = async function (ctx) { ) } + handleStepEvents(oldAutomation, automation) + ctx.status = 200 ctx.body = { message: `Automation ${automation._id} updated successfully.`, @@ -148,6 +179,7 @@ exports.destroy = async function (ctx) { // delete metadata first await cleanupAutomationMetadata(automationId) ctx.body = await db.remove(automationId, ctx.params.rev) + events.automation.deleted() } exports.getActionList = async function (ctx) { @@ -215,4 +247,5 @@ exports.test = async function (ctx) { }) await clearTestFlag(automation._id) ctx.body = response + events.automation.tested() } diff --git a/packages/server/src/api/routes/tests/automation.spec.js b/packages/server/src/api/routes/tests/automation.spec.js index cce51fc94b..aef2927889 100644 --- a/packages/server/src/api/routes/tests/automation.spec.js +++ b/packages/server/src/api/routes/tests/automation.spec.js @@ -10,6 +10,7 @@ const { mocks } = require("@budibase/backend-core/testUtils") mocks.date.mock() const MAX_RETRIES = 4 const { TRIGGER_DEFINITIONS, ACTION_DEFINITIONS } = require("../../../automations") +const { events } = require("@budibase/backend-core") describe("/automations", () => { let request = setup.getRequest() @@ -58,8 +59,9 @@ describe("/automations", () => { }) describe("create", () => { - it("returns a success message when the automation is successfully created", async () => { + it("creates an automation with no steps", async () => { const automation = newAutomation() + automation.definition.steps = [] const res = await request .post(`/api/automations`) @@ -71,6 +73,27 @@ describe("/automations", () => { expect(res.body.message).toEqual("Automation created successfully") expect(res.body.automation.name).toEqual("My Automation") expect(res.body.automation._id).not.toEqual(null) + expect(events.automation.created).toBeCalledTimes(1) + expect(events.automation.stepCreated).not.toBeCalled() + }) + + it("creates an automation with steps", async () => { + const automation = newAutomation() + automation.definition.steps.push(automationStep()) + jest.clearAllMocks() + + const res = await request + .post(`/api/automations`) + .set(config.defaultHeaders()) + .send(automation) + .expect('Content-Type', /json/) + .expect(200) + + expect(res.body.message).toEqual("Automation created successfully") + expect(res.body.automation.name).toEqual("My Automation") + expect(res.body.automation._id).not.toEqual(null) + expect(events.automation.created).toBeCalledTimes(1) + expect(events.automation.stepCreated).toBeCalledTimes(2) }) it("should apply authorization to endpoint", async () => { @@ -97,8 +120,8 @@ describe("/automations", () => { }) }) - describe("trigger", () => { - it("trigger the automation successfully", async () => { + describe("test", () => { + it("tests the automation successfully", async () => { let table = await config.createTable() let automation = newAutomation() automation.definition.trigger.inputs.tableId = table._id @@ -113,6 +136,7 @@ describe("/automations", () => { automation = await config.createAutomation(automation) await setup.delay(500) const res = await testAutomation(config, automation) + expect(events.automation.tested).toBeCalledTimes(1) // this looks a bit mad but we don't actually have a way to wait for a response from the automation to // know that it has finished all of its actions - this is currently the best way // also when this runs in CI it is very temper-mental so for now trying to make run stable by repeating until it works @@ -134,53 +158,141 @@ describe("/automations", () => { }) describe("update", () => { - it("updates a automations data", async () => { - let automation = newAutomation() - await config.createAutomation(automation) - automation.name = "Updated Name" - const res = await request + const update = async (automation) => { + return request .put(`/api/automations`) .set(config.defaultHeaders()) .send(automation) .expect('Content-Type', /json/) .expect(200) + } - expect(res.body.message).toEqual(`Automation ${automation._id} updated successfully.`) - expect(res.body.automation.name).toEqual("Updated Name") - }) - - it("should be able to update an automation trigger", async () => { - // create webhook automation - const webhookTrigger = automationTrigger(TRIGGER_DEFINITIONS.WEBHOOK) - let automation = newAutomation({ trigger: webhookTrigger }) - - let res = await request + const updateWithPost = async (automation) => { + return request .post(`/api/automations`) .set(config.defaultHeaders()) .send(automation) .expect('Content-Type', /json/) .expect(200) + } - automation = res.body.automation - expect(automation._id).toBeDefined() - expect(automation._rev).toBeDefined() + it("updates a automations name", async () => { + let automation = newAutomation() + await config.createAutomation(automation) + automation.name = "Updated Name" + jest.clearAllMocks() - // change the trigger - automation.trigger = automationTrigger(TRIGGER_DEFINITIONS.ROW_SAVED) - - // check the post request honours updates with same id - res = await request - .post(`/api/automations`) - .set(config.defaultHeaders()) - .send(automation) - .expect('Content-Type', /json/) - .expect(200) + const res = await update(automation) const automationRes = res.body.automation + const message = res.body.message + + // doc attributes expect(automationRes._id).toEqual(automation._id) expect(automationRes._rev).toBeDefined() expect(automationRes._rev).not.toEqual(automation._rev) + // content updates + expect(automationRes.name).toEqual("Updated Name") + expect(message).toEqual(`Automation ${automation._id} updated successfully.`) + // events + expect(events.automation.created).not.toBeCalled() + expect(events.automation.stepCreated).not.toBeCalled() + expect(events.automation.stepDeleted).not.toBeCalled() + expect(events.automation.triggerUpdated).not.toBeCalled() + }) + + + it("updates a automations name using POST request", async () => { + let automation = newAutomation() + await config.createAutomation(automation) + automation.name = "Updated Name" + jest.clearAllMocks() + + // the POST request will defer to the update + // when an id has been supplied. + const res = await updateWithPost(automation) + + const automationRes = res.body.automation + const message = res.body.message + // doc attributes + expect(automationRes._id).toEqual(automation._id) + expect(automationRes._rev).toBeDefined() + expect(automationRes._rev).not.toEqual(automation._rev) + // content updates + expect(automationRes.name).toEqual("Updated Name") + expect(message).toEqual(`Automation ${automation._id} updated successfully.`) + // events + expect(events.automation.created).not.toBeCalled() + expect(events.automation.stepCreated).not.toBeCalled() + expect(events.automation.stepDeleted).not.toBeCalled() + expect(events.automation.triggerUpdated).not.toBeCalled() + }) + + it("updates an automation trigger", async () => { + let automation = newAutomation() + automation = await config.createAutomation(automation) + automation.definition.trigger = automationTrigger(TRIGGER_DEFINITIONS.WEBHOOK) + jest.clearAllMocks() + + await update(automation) + + // events + expect(events.automation.created).not.toBeCalled() + expect(events.automation.stepCreated).not.toBeCalled() + expect(events.automation.stepDeleted).not.toBeCalled() + expect(events.automation.triggerUpdated).toBeCalledTimes(1) + }) + + it("adds automation steps", async () => { + let automation = newAutomation() + automation = await config.createAutomation(automation) + automation.definition.steps.push(automationStep()) + automation.definition.steps.push(automationStep()) + jest.clearAllMocks() + + // check the post request honours updates with same id + await update(automation) + + // events + expect(events.automation.stepCreated).toBeCalledTimes(2) + expect(events.automation.created).not.toBeCalled() + expect(events.automation.stepDeleted).not.toBeCalled() + expect(events.automation.triggerUpdated).not.toBeCalled() + }) + + + it("removes automation steps", async () => { + let automation = newAutomation() + automation.definition.steps.push(automationStep()) + automation = await config.createAutomation(automation) + automation.definition.steps = [] + jest.clearAllMocks() + + // check the post request honours updates with same id + await update(automation) + + // events + expect(events.automation.stepDeleted).toBeCalledTimes(2) + expect(events.automation.stepCreated).not.toBeCalled() + expect(events.automation.created).not.toBeCalled() + expect(events.automation.triggerUpdated).not.toBeCalled() + }) + + it("adds and removes automation steps", async () => { + let automation = newAutomation() + automation = await config.createAutomation(automation) + automation.definition.steps = [automationStep(), automationStep()] + jest.clearAllMocks() + + // check the post request honours updates with same id + await update(automation) + + // events + expect(events.automation.stepCreated).toBeCalledTimes(2) + expect(events.automation.stepDeleted).toBeCalledTimes(1) + expect(events.automation.created).not.toBeCalled() + expect(events.automation.triggerUpdated).not.toBeCalled() }) }) @@ -216,7 +328,8 @@ describe("/automations", () => { .expect('Content-Type', /json/) .expect(200) - expect(res.body.id).toEqual(automation._id) + expect(res.body.id).toEqual(automation._id) + expect(events.automation.deleted).toBeCalledTimes(1) }) it("should apply authorization to endpoint", async () => { From befb61c91590581db60fb552d2aa21298fdbd966 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 6 Apr 2022 16:57:56 +0100 Subject: [PATCH 15/75] smtp events + centralise worker test config --- .../src/tests/utilities/TestConfiguration.js | 670 +++++++++--------- .../worker/src/api/routes/tests/auth.spec.js | 14 +- .../src/api/routes/tests/configs.spec.js | 68 +- .../worker/src/api/routes/tests/email.spec.js | 15 +- .../src/api/routes/tests/realEmail.spec.js | 10 +- .../worker/src/api/routes/tests/users.spec.js | 12 +- .../api/routes/tests/utilities/controllers.js | 7 - .../src/api/routes/tests/utilities/index.js | 41 -- .../api/routes/tests/utilities/mocks/index.js | 1 - .../api/routes/tests/utilities/structures.js | 2 - .../utilities => tests}/TestConfiguration.js | 169 ++--- packages/worker/src/tests/controllers.js | 7 + packages/worker/src/tests/index.js | 12 + .../tests/utilities => tests}/mocks/core.js | 0 packages/worker/src/tests/mocks/email.js | 10 + packages/worker/src/tests/mocks/index.js | 6 + .../worker/src/tests/structures/configs.js | 76 ++ packages/worker/src/tests/structures/index.js | 10 + 18 files changed, 600 insertions(+), 530 deletions(-) delete mode 100644 packages/worker/src/api/routes/tests/utilities/controllers.js delete mode 100644 packages/worker/src/api/routes/tests/utilities/index.js delete mode 100644 packages/worker/src/api/routes/tests/utilities/mocks/index.js delete mode 100644 packages/worker/src/api/routes/tests/utilities/structures.js rename packages/worker/src/{api/routes/tests/utilities => tests}/TestConfiguration.js (65%) create mode 100644 packages/worker/src/tests/controllers.js create mode 100644 packages/worker/src/tests/index.js rename packages/worker/src/{api/routes/tests/utilities => tests}/mocks/core.js (100%) create mode 100644 packages/worker/src/tests/mocks/email.js create mode 100644 packages/worker/src/tests/mocks/index.js create mode 100644 packages/worker/src/tests/structures/configs.js create mode 100644 packages/worker/src/tests/structures/index.js diff --git a/packages/server/src/tests/utilities/TestConfiguration.js b/packages/server/src/tests/utilities/TestConfiguration.js index 87c8f4f4b1..16658bec3c 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.js +++ b/packages/server/src/tests/utilities/TestConfiguration.js @@ -60,6 +60,26 @@ class TestConfiguration { return this.prodAppId } + // SETUP / TEARDOWN + + // use a new id as the name to avoid name collisions + async init(appName = newid()) { + await this.globalUser() + return this.createApp(appName) + } + + end() { + if (!this) { + return + } + if (this.server) { + this.server.close() + } + cleanup(this.allApps.map(app => app.appId)) + } + + // UTILS + async _req(config, params, controlFunc) { const request = {} // fake cookies, we don't need them @@ -88,19 +108,7 @@ class TestConfiguration { } } - async generateApiKey(userId = GLOBAL_USER_ID) { - const db = getGlobalDB(TENANT_ID) - const id = generateDevInfoID(userId) - let devInfo - try { - devInfo = await db.get(id) - } catch (err) { - devInfo = { _id: id, userId } - } - devInfo.apiKey = encrypt(`${TENANT_ID}${SEPARATOR}${newid()}`) - await db.put(devInfo) - return devInfo.apiKey - } + // USER / AUTH async globalUser({ id = GLOBAL_USER_ID, @@ -138,314 +146,6 @@ class TestConfiguration { } } - // use a new id as the name to avoid name collisions - async init(appName = newid()) { - await this.globalUser() - return this.createApp(appName) - } - - end() { - if (!this) { - return - } - if (this.server) { - this.server.close() - } - cleanup(this.allApps.map(app => app.appId)) - } - - defaultHeaders(extras = {}) { - const auth = { - userId: GLOBAL_USER_ID, - sessionId: "sessionid", - tenantId: TENANT_ID, - } - const app = { - roleId: BUILTIN_ROLE_IDS.ADMIN, - appId: this.appId, - } - const authToken = jwt.sign(auth, env.JWT_SECRET) - const appToken = jwt.sign(app, env.JWT_SECRET) - const headers = { - Accept: "application/json", - Cookie: [ - `${Cookies.Auth}=${authToken}`, - `${Cookies.CurrentApp}=${appToken}`, - ], - [Headers.CSRF_TOKEN]: CSRF_TOKEN, - ...extras, - } - if (this.appId) { - headers[Headers.APP_ID] = this.appId - } - return headers - } - - publicHeaders({ prodApp = true } = {}) { - const appId = prodApp ? this.prodAppId : this.appId - - const headers = { - Accept: "application/json", - } - if (appId) { - headers[Headers.APP_ID] = appId - } - return headers - } - - async roleHeaders({ - email = EMAIL, - roleId = BUILTIN_ROLE_IDS.ADMIN, - builder = false, - prodApp = true, - } = {}) { - return this.login({ email, roleId, builder, prodApp }) - } - - async createApp(appName) { - // create dev app - this.app = await this._req({ name: appName }, null, controllers.app.create) - this.appId = this.app.appId - context.updateAppId(this.appId) - - // create production app - this.prodApp = await this.deploy() - this.prodAppId = this.prodApp.appId - - this.allApps.push(this.prodApp) - this.allApps.push(this.app) - - return this.app - } - - async deploy() { - await this._req(null, null, controllers.deploy.deployApp) - const prodAppId = this.getAppId().replace("_dev", "") - return context.doInAppContext(prodAppId, async () => { - const appPackage = await this._req( - null, - { appId: prodAppId }, - controllers.app.fetchAppPackage - ) - return appPackage.application - }) - } - - async updateTable(config = null) { - config = config || basicTable() - this.table = await this._req(config, null, controllers.table.save) - return this.table - } - - async createTable(config = null) { - if (config != null && config._id) { - delete config._id - } - return this.updateTable(config) - } - - async getTable(tableId = null) { - tableId = tableId || this.table._id - return this._req(null, { tableId }, controllers.table.find) - } - - async createLinkedTable(relationshipType = null, links = ["link"]) { - if (!this.table) { - throw "Must have created a table first." - } - const tableConfig = basicTable() - tableConfig.primaryDisplay = "name" - for (let link of links) { - tableConfig.schema[link] = { - type: "link", - fieldName: link, - tableId: this.table._id, - name: link, - } - if (relationshipType) { - tableConfig.schema[link].relationshipType = relationshipType - } - } - const linkedTable = await this.createTable(tableConfig) - this.linkedTable = linkedTable - return linkedTable - } - - async createAttachmentTable() { - const table = basicTable() - table.schema.attachment = { - type: "attachment", - } - return this.createTable(table) - } - - async createRow(config = null) { - if (!this.table) { - throw "Test requires table to be configured." - } - const tableId = (config && config.tableId) || this.table._id - config = config || basicRow(tableId) - return this._req(config, { tableId }, controllers.row.save) - } - - async getRow(tableId, rowId) { - return this._req(null, { tableId, rowId }, controllers.row.find) - } - - async getRows(tableId) { - if (!tableId && this.table) { - tableId = this.table._id - } - return this._req(null, { tableId }, controllers.row.fetch) - } - - async createRole(config = null) { - config = config || basicRole() - return this._req(config, null, controllers.role.save) - } - - async addPermission(roleId, resourceId, level = "read") { - return this._req( - null, - { - roleId, - resourceId, - level, - }, - controllers.perms.addPermission - ) - } - - async createView(config) { - if (!this.table) { - throw "Test requires table to be configured." - } - const view = config || { - map: "function(doc) { emit(doc[doc.key], doc._id); } ", - tableId: this.table._id, - name: "ViewTest", - } - return this._req(view, null, controllers.view.save) - } - - async createAutomation(config) { - config = config || basicAutomation() - if (config._rev) { - delete config._rev - } - this.automation = ( - await this._req(config, null, controllers.automation.create) - ).automation - return this.automation - } - - async getAllAutomations() { - return this._req(null, null, controllers.automation.fetch) - } - - async deleteAutomation(automation = null) { - automation = automation || this.automation - if (!automation) { - return - } - return this._req( - null, - { id: automation._id, rev: automation._rev }, - controllers.automation.destroy - ) - } - - async createDatasource(config = null) { - config = config || basicDatasource() - const response = await this._req(config, null, controllers.datasource.save) - this.datasource = response.datasource - return this.datasource - } - - async updateDatasource(datasource) { - const response = await this._req( - datasource, - { datasourceId: datasource._id }, - controllers.datasource.update - ) - this.datasource = response.datasource - return this.datasource - } - - async restDatasource(cfg) { - return this.createDatasource({ - datasource: { - ...basicDatasource().datasource, - source: "REST", - config: cfg || {}, - }, - }) - } - - async dynamicVariableDatasource() { - let datasource = await this.restDatasource() - const basedOnQuery = await this.createQuery({ - ...basicQuery(datasource._id), - fields: { - path: "www.google.com", - }, - }) - datasource = await this.updateDatasource({ - ...datasource, - config: { - dynamicVariables: [ - { - queryId: basedOnQuery._id, - name: "variable3", - value: "{{ data.0.[value] }}", - }, - ], - }, - }) - return { datasource, query: basedOnQuery } - } - - async previewQuery(request, config, datasource, fields) { - return request - .post(`/api/queries/preview`) - .send({ - datasourceId: datasource._id, - parameters: {}, - fields, - queryVerb: "read", - name: datasource.name, - }) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) - } - - async createQuery(config = null) { - if (!this.datasource && !config) { - throw "No data source created for query." - } - config = config || basicQuery(this.datasource._id) - return this._req(config, null, controllers.query.save) - } - - async createScreen(config = null) { - config = config || basicScreen() - return this._req(config, null, controllers.screen.save) - } - - async createWebhook(config = null) { - if (!this.automation) { - throw "Must create an automation before creating webhook." - } - config = config || basicWebhook(this.automation._id) - return (await this._req(config, null, controllers.webhook.save)).webhook - } - - async createLayout(config = null) { - config = config || basicLayout() - return await this._req(config, null, controllers.layout.save) - } - async createUser(id = null, email = EMAIL) { const globalId = !id ? `us_${Math.random()}` : `us_${id}` const resp = await this.globalUser({ id: globalId, email }) @@ -500,6 +200,334 @@ class TestConfiguration { } }) } + + defaultHeaders(extras = {}) { + const auth = { + userId: GLOBAL_USER_ID, + sessionId: "sessionid", + tenantId: TENANT_ID, + } + const app = { + roleId: BUILTIN_ROLE_IDS.ADMIN, + appId: this.appId, + } + const authToken = jwt.sign(auth, env.JWT_SECRET) + const appToken = jwt.sign(app, env.JWT_SECRET) + const headers = { + Accept: "application/json", + Cookie: [ + `${Cookies.Auth}=${authToken}`, + `${Cookies.CurrentApp}=${appToken}`, + ], + [Headers.CSRF_TOKEN]: CSRF_TOKEN, + ...extras, + } + if (this.appId) { + headers[Headers.APP_ID] = this.appId + } + return headers + } + + publicHeaders({ prodApp = true } = {}) { + const appId = prodApp ? this.prodAppId : this.appId + + const headers = { + Accept: "application/json", + } + if (appId) { + headers[Headers.APP_ID] = appId + } + return headers + } + + async roleHeaders({ + email = EMAIL, + roleId = BUILTIN_ROLE_IDS.ADMIN, + builder = false, + prodApp = true, + } = {}) { + return this.login({ email, roleId, builder, prodApp }) + } + + // API + + async generateApiKey(userId = GLOBAL_USER_ID) { + const db = getGlobalDB(TENANT_ID) + const id = generateDevInfoID(userId) + let devInfo + try { + devInfo = await db.get(id) + } catch (err) { + devInfo = { _id: id, userId } + } + devInfo.apiKey = encrypt(`${TENANT_ID}${SEPARATOR}${newid()}`) + await db.put(devInfo) + return devInfo.apiKey + } + + // APP + + async createApp(appName) { + // create dev app + this.app = await this._req({ name: appName }, null, controllers.app.create) + this.appId = this.app.appId + context.updateAppId(this.appId) + + // create production app + this.prodApp = await this.deploy() + this.prodAppId = this.prodApp.appId + + this.allApps.push(this.prodApp) + this.allApps.push(this.app) + + return this.app + } + + async deploy() { + await this._req(null, null, controllers.deploy.deployApp) + const prodAppId = this.getAppId().replace("_dev", "") + return context.doInAppContext(prodAppId, async () => { + const appPackage = await this._req( + null, + { appId: prodAppId }, + controllers.app.fetchAppPackage + ) + return appPackage.application + }) + } + + // TABLE + + async updateTable(config = null) { + config = config || basicTable() + this.table = await this._req(config, null, controllers.table.save) + return this.table + } + + async createTable(config = null) { + if (config != null && config._id) { + delete config._id + } + return this.updateTable(config) + } + + async getTable(tableId = null) { + tableId = tableId || this.table._id + return this._req(null, { tableId }, controllers.table.find) + } + + async createLinkedTable(relationshipType = null, links = ["link"]) { + if (!this.table) { + throw "Must have created a table first." + } + const tableConfig = basicTable() + tableConfig.primaryDisplay = "name" + for (let link of links) { + tableConfig.schema[link] = { + type: "link", + fieldName: link, + tableId: this.table._id, + name: link, + } + if (relationshipType) { + tableConfig.schema[link].relationshipType = relationshipType + } + } + const linkedTable = await this.createTable(tableConfig) + this.linkedTable = linkedTable + return linkedTable + } + + async createAttachmentTable() { + const table = basicTable() + table.schema.attachment = { + type: "attachment", + } + return this.createTable(table) + } + + // ROW + + async createRow(config = null) { + if (!this.table) { + throw "Test requires table to be configured." + } + const tableId = (config && config.tableId) || this.table._id + config = config || basicRow(tableId) + return this._req(config, { tableId }, controllers.row.save) + } + + async getRow(tableId, rowId) { + return this._req(null, { tableId, rowId }, controllers.row.find) + } + + async getRows(tableId) { + if (!tableId && this.table) { + tableId = this.table._id + } + return this._req(null, { tableId }, controllers.row.fetch) + } + + // ROLE + + async createRole(config = null) { + config = config || basicRole() + return this._req(config, null, controllers.role.save) + } + + async addPermission(roleId, resourceId, level = "read") { + return this._req( + null, + { + roleId, + resourceId, + level, + }, + controllers.perms.addPermission + ) + } + + // VIEW + + async createView(config) { + if (!this.table) { + throw "Test requires table to be configured." + } + const view = config || { + map: "function(doc) { emit(doc[doc.key], doc._id); } ", + tableId: this.table._id, + name: "ViewTest", + } + return this._req(view, null, controllers.view.save) + } + + // AUTOMATION + + async createAutomation(config) { + config = config || basicAutomation() + if (config._rev) { + delete config._rev + } + this.automation = ( + await this._req(config, null, controllers.automation.create) + ).automation + return this.automation + } + + async getAllAutomations() { + return this._req(null, null, controllers.automation.fetch) + } + + async deleteAutomation(automation = null) { + automation = automation || this.automation + if (!automation) { + return + } + return this._req( + null, + { id: automation._id, rev: automation._rev }, + controllers.automation.destroy + ) + } + + async createWebhook(config = null) { + if (!this.automation) { + throw "Must create an automation before creating webhook." + } + config = config || basicWebhook(this.automation._id) + return (await this._req(config, null, controllers.webhook.save)).webhook + } + + // DATASOURCE + + async createDatasource(config = null) { + config = config || basicDatasource() + const response = await this._req(config, null, controllers.datasource.save) + this.datasource = response.datasource + return this.datasource + } + + async updateDatasource(datasource) { + const response = await this._req( + datasource, + { datasourceId: datasource._id }, + controllers.datasource.update + ) + this.datasource = response.datasource + return this.datasource + } + + async restDatasource(cfg) { + return this.createDatasource({ + datasource: { + ...basicDatasource().datasource, + source: "REST", + config: cfg || {}, + }, + }) + } + + async dynamicVariableDatasource() { + let datasource = await this.restDatasource() + const basedOnQuery = await this.createQuery({ + ...basicQuery(datasource._id), + fields: { + path: "www.google.com", + }, + }) + datasource = await this.updateDatasource({ + ...datasource, + config: { + dynamicVariables: [ + { + queryId: basedOnQuery._id, + name: "variable3", + value: "{{ data.0.[value] }}", + }, + ], + }, + }) + return { datasource, query: basedOnQuery } + } + + // QUERY + + async previewQuery(request, config, datasource, fields) { + return request + .post(`/api/queries/preview`) + .send({ + datasourceId: datasource._id, + parameters: {}, + fields, + queryVerb: "read", + name: datasource.name, + }) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + } + + async createQuery(config = null) { + if (!this.datasource && !config) { + throw "No data source created for query." + } + config = config || basicQuery(this.datasource._id) + return this._req(config, null, controllers.query.save) + } + + // SCREEN + + async createScreen(config = null) { + config = config || basicScreen() + return this._req(config, null, controllers.screen.save) + } + + // LAYOUT + + async createLayout(config = null) { + config = config || basicLayout() + return await this._req(config, null, controllers.layout.save) + } } module.exports = TestConfiguration diff --git a/packages/worker/src/api/routes/tests/auth.spec.js b/packages/worker/src/api/routes/tests/auth.spec.js index 419c477882..9426b9f779 100644 --- a/packages/worker/src/api/routes/tests/auth.spec.js +++ b/packages/worker/src/api/routes/tests/auth.spec.js @@ -1,20 +1,20 @@ jest.mock("nodemailer") -const setup = require("./utilities") -const sendMailMock = setup.emailMock() +const { config, request, mocks, structures } = require("../../../tests") +const sendMailMock = mocks.email.mock() const { events } = require("@budibase/backend-core") -const TENANT_ID = "default" +const TENANT_ID = structures.TENANT_ID describe("/api/global/auth", () => { - let request = setup.getRequest() - let config = setup.getConfig() let code beforeAll(async () => { - await config.init() + await config.beforeAll() }) - afterAll(setup.afterAll) + afterAll(async () => { + await config.afterAll() + }) afterEach(() => { jest.clearAllMocks() diff --git a/packages/worker/src/api/routes/tests/configs.spec.js b/packages/worker/src/api/routes/tests/configs.spec.js index 9f0570023e..98e0fef17f 100644 --- a/packages/worker/src/api/routes/tests/configs.spec.js +++ b/packages/worker/src/api/routes/tests/configs.spec.js @@ -1,16 +1,14 @@ // mock the email system jest.mock("nodemailer") -const setup = require("./utilities") -setup.emailMock() +const { config, structures, mocks, request } = require("../../../tests") +mocks.email.mock() const { Configs } = require("@budibase/backend-core/constants") const { events } = require("@budibase/backend-core") describe("configs", () => { - let request = setup.getRequest() - let config = setup.getConfig() beforeAll(async () => { - await config.init() + await config.beforeAll() }) beforeEach(() => { @@ -18,15 +16,14 @@ describe("configs", () => { }) afterAll(async () => { - await setup.afterAll() + await config.afterAll() }) describe("post /api/global/configs", () => { - const saveConfig = async (conf, type, _id, _rev) => { + const saveConfig = async (conf, _id, _rev) => { const data = { - type, - config: conf, + ...conf, _id, _rev } @@ -46,14 +43,8 @@ describe("configs", () => { describe("google", () => { const saveGoogleConfig = async (conf, _id, _rev) => { - const googleConfig = { - clientID: "clientID", - clientSecret: "clientSecret", - activated: true, - ...conf - } - - return saveConfig(googleConfig, Configs.GOOGLE, _id, _rev) + const googleConfig = structures.configs.google(conf) + return saveConfig(googleConfig, _id, _rev) } describe("create", () => { @@ -106,19 +97,8 @@ describe("configs", () => { describe("oidc", () => { const saveOIDCConfig = async (conf, _id, _rev) => { - const oidcConfig = { - configs: [{ - clientID: "clientID", - clientSecret: "clientSecret", - configUrl: "http://example.com", - logo: "logo", - name: "oidc", - uuid: "uuid", - activated: true, - ...conf - }] - } - return saveConfig(oidcConfig, Configs.OIDC, _id, _rev) + const oidcConfig = structures.configs.oidc(conf) + return saveConfig(oidcConfig, _id, _rev) } describe("create", () => { @@ -169,6 +149,34 @@ describe("configs", () => { }) }) + + describe("smtp", () => { + const saveSMTPConfig = async (conf, _id, _rev) => { + const smtpConfig = structures.configs.smtp(conf) + return saveConfig(smtpConfig, _id, _rev) + } + + describe("create", () => { + it ("should create SMTP config", async () => { + await config.deleteConfig(Configs.SMTP) + await saveSMTPConfig() + expect(events.email.SMTPUpdated).not.toBeCalled() + expect(events.email.SMTPCreated).toBeCalledTimes(1) + await config.deleteConfig(Configs.SMTP) + }) + }) + + describe("update", () => { + it ("should update SMTP config", async () => { + const smtpConf = await saveSMTPConfig() + jest.clearAllMocks() + await saveSMTPConfig(smtpConf.config, smtpConf._id, smtpConf._rev) + expect(events.email.SMTPCreated).not.toBeCalled() + expect(events.email.SMTPUpdated).toBeCalledTimes(1) + await config.deleteConfig(Configs.SMTP) + }) + }) + }) }) it("should return the correct checklist status based on the state of the budibase installation", async () => { diff --git a/packages/worker/src/api/routes/tests/email.spec.js b/packages/worker/src/api/routes/tests/email.spec.js index 4cc7ca2d26..e90d953880 100644 --- a/packages/worker/src/api/routes/tests/email.spec.js +++ b/packages/worker/src/api/routes/tests/email.spec.js @@ -1,19 +1,20 @@ jest.mock("nodemailer") -const setup = require("./utilities") -const sendMailMock = setup.emailMock() +const { config, mocks, structures, request } = require("../../../tests") +const sendMailMock = mocks.email.mock() const { EmailTemplatePurpose } = require("../../../constants") -const { TENANT_ID } = require("./utilities/structures") + +const TENANT_ID = structures.TENANT_ID describe("/api/global/email", () => { - let request = setup.getRequest() - let config = setup.getConfig() beforeAll(async () => { - await config.init() + await config.beforeAll() }) - afterAll(setup.afterAll) + afterAll(async () => { + await config.afterAll() + }) it("should be able to send an email (with mocking)", async () => { // initially configure settings diff --git a/packages/worker/src/api/routes/tests/realEmail.spec.js b/packages/worker/src/api/routes/tests/realEmail.spec.js index d0cfd24010..c440eb13b3 100644 --- a/packages/worker/src/api/routes/tests/realEmail.spec.js +++ b/packages/worker/src/api/routes/tests/realEmail.spec.js @@ -1,17 +1,17 @@ -const setup = require("./utilities") +const { config, request } = require("../../../tests") const { EmailTemplatePurpose } = require("../../../constants") const nodemailer = require("nodemailer") const fetch = require("node-fetch") describe("/api/global/email", () => { - let request = setup.getRequest() - let config = setup.getConfig() beforeAll(async () => { - await config.init() + await config.beforeAll() }) - afterAll(setup.afterAll) + afterAll(async () => { + await config.afterAll() + }) async function sendRealEmail(purpose) { let response, text diff --git a/packages/worker/src/api/routes/tests/users.spec.js b/packages/worker/src/api/routes/tests/users.spec.js index 310ae0fe20..be9807c5d4 100644 --- a/packages/worker/src/api/routes/tests/users.spec.js +++ b/packages/worker/src/api/routes/tests/users.spec.js @@ -1,17 +1,17 @@ jest.mock("nodemailer") -const setup = require("./utilities") -const sendMailMock = setup.emailMock() +const { config, request, mocks } = require("../../../tests") +const sendMailMock = mocks.email.mock() describe("/api/global/users", () => { - let request = setup.getRequest() - let config = setup.getConfig() let code beforeAll(async () => { - await config.init() + await config.beforeAll() }) - afterAll(setup.afterAll) + afterAll(async () => { + await config.afterAll() + }) it("should be able to generate an invitation", async () => { // initially configure settings diff --git a/packages/worker/src/api/routes/tests/utilities/controllers.js b/packages/worker/src/api/routes/tests/utilities/controllers.js deleted file mode 100644 index 45216ae634..0000000000 --- a/packages/worker/src/api/routes/tests/utilities/controllers.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - email: require("../../../controllers/global/email"), - workspaces: require("../../../controllers/global/workspaces"), - config: require("../../../controllers/global/configs"), - templates: require("../../../controllers/global/templates"), - users: require("../../../controllers/global/users"), -} diff --git a/packages/worker/src/api/routes/tests/utilities/index.js b/packages/worker/src/api/routes/tests/utilities/index.js deleted file mode 100644 index a639caef60..0000000000 --- a/packages/worker/src/api/routes/tests/utilities/index.js +++ /dev/null @@ -1,41 +0,0 @@ -const TestConfig = require("./TestConfiguration") - -let request, config - -exports.beforeAll = () => { - config = new TestConfig() - request = config.getRequest() -} - -exports.afterAll = async () => { - if (config) { - await config.end() - } - request = null - config = null -} - -exports.getRequest = () => { - if (!request) { - exports.beforeAll() - } - return request -} - -exports.getConfig = () => { - if (!config) { - exports.beforeAll() - } - return config -} - -exports.emailMock = () => { - // mock the email system - const sendMailMock = jest.fn() - const nodemailer = require("nodemailer") - nodemailer.createTransport.mockReturnValue({ - sendMail: sendMailMock, - verify: jest.fn(), - }) - return sendMailMock -} diff --git a/packages/worker/src/api/routes/tests/utilities/mocks/index.js b/packages/worker/src/api/routes/tests/utilities/mocks/index.js deleted file mode 100644 index 1e7730932f..0000000000 --- a/packages/worker/src/api/routes/tests/utilities/mocks/index.js +++ /dev/null @@ -1 +0,0 @@ -require("./core") diff --git a/packages/worker/src/api/routes/tests/utilities/structures.js b/packages/worker/src/api/routes/tests/utilities/structures.js deleted file mode 100644 index 45f1f0077c..0000000000 --- a/packages/worker/src/api/routes/tests/utilities/structures.js +++ /dev/null @@ -1,2 +0,0 @@ -exports.TENANT_ID = "default" -exports.CSRF_TOKEN = "e3727778-7af0-4226-b5eb-f43cbe60a306" diff --git a/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js b/packages/worker/src/tests/TestConfiguration.js similarity index 65% rename from packages/worker/src/api/routes/tests/utilities/TestConfiguration.js rename to packages/worker/src/tests/TestConfiguration.js index 0de0ca3234..a718f7b8b9 100644 --- a/packages/worker/src/api/routes/tests/utilities/TestConfiguration.js +++ b/packages/worker/src/tests/TestConfiguration.js @@ -1,22 +1,22 @@ require("./mocks") -require("../../../../db").init() -const env = require("../../../../environment") +require("../db").init() +const env = require("../environment") const controllers = require("./controllers") const supertest = require("supertest") const { jwt } = require("@budibase/backend-core/auth") const { Cookies, Headers } = require("@budibase/backend-core/constants") -const { Configs, LOGO_URL } = require("../../../../constants") +const { Configs } = require("../constants") const { getGlobalUserByEmail } = require("@budibase/backend-core/utils") const { createASession } = require("@budibase/backend-core/sessions") -const { newid } = require("@budibase/backend-core/src/hashing") const { TENANT_ID, CSRF_TOKEN } = require("./structures") +const structures = require("./structures") const { doInTenant } = require("@budibase/backend-core/tenancy") class TestConfiguration { constructor(openServer = true) { if (openServer) { - env.PORT = 4012 - this.server = require("../../../../index") + env.PORT = "0" // random port + this.server = require("../index") // we need the request for logging in, involves cookies, hard to fake this.request = supertest(this.server) } @@ -26,6 +26,8 @@ class TestConfiguration { return this.request } + // UTILS + async _req(config, params, controlFunc) { const request = {} // fake cookies, we don't need them @@ -49,25 +51,37 @@ class TestConfiguration { return request.body } - async init(createUser = true) { - if (createUser) { - // create a test user - await this._req( - { - email: "test@test.com", - password: "test", - _id: "us_uuid1", - builder: { - global: true, - }, - admin: { - global: true, - }, - }, - null, - controllers.users.save - ) + // SETUP / TEARDOWN + + async beforeAll() { + await this.login() + } + + async afterAll() { + if (this.server) { + await this.server.close() } + } + + // USER / AUTH + + async login() { + // create a test user + await this._req( + { + email: "test@test.com", + password: "test", + _id: "us_uuid1", + builder: { + global: true, + }, + admin: { + global: true, + }, + }, + null, + controllers.users.save + ) await createASession("us_uuid1", { sessionId: "sessionid", tenantId: TENANT_ID, @@ -75,12 +89,6 @@ class TestConfiguration { }) } - async end() { - if (this.server) { - await this.server.close() - } - } - cookieHeader(cookies) { return { Cookie: [cookies], @@ -123,6 +131,20 @@ class TestConfiguration { ) } + async saveAdminUser() { + await this._req( + { + email: "testuser@test.com", + password: "test@test.com", + tenantId: TENANT_ID, + }, + null, + controllers.users.adminUser + ) + } + + // CONFIGS + async deleteConfig(type) { try { const cfg = await this._req( @@ -147,37 +169,26 @@ class TestConfiguration { } } + // CONFIGS - SETTINGS + async saveSettingsConfig() { await this.deleteConfig(Configs.SETTINGS) await this._req( - { - type: Configs.SETTINGS, - config: { - platformUrl: "http://localhost:10000", - logoUrl: LOGO_URL, - company: "Budibase", - }, - }, + structures.configs.settings(), null, controllers.config.save ) } - async saveOAuthConfig() { + // CONFIGS - GOOGLE + + async saveGoogleConfig() { await this.deleteConfig(Configs.GOOGLE) - await this._req( - { - type: Configs.GOOGLE, - config: { - clientID: "clientId", - clientSecret: "clientSecret", - }, - }, - null, - controllers.config.save - ) + await this._req(structures.configs.google(), null, controllers.config.save) } + // CONFIGS - OIDC + getOIDConfigCookie(configId) { const token = jwt.sign(configId, env.JWT_SECRET) return this.cookieHeader([[`${Cookies.OIDC_CONFIG}=${token}`]]) @@ -185,75 +196,27 @@ class TestConfiguration { async saveOIDCConfig() { await this.deleteConfig(Configs.OIDC) - const config = { - type: Configs.OIDC, - config: { - configs: [ - { - configUrl: "http://someconfigurl", - clientID: "clientId", - clientSecret: "clientSecret", - logo: "Microsoft", - name: "Active Directory", - uuid: newid(), - }, - ], - }, - } + const config = structures.configs.oidc() await this._req(config, null, controllers.config.save) return config } + // CONFIGS - SMTP + async saveSmtpConfig() { await this.deleteConfig(Configs.SMTP) - await this._req( - { - type: Configs.SMTP, - config: { - port: 12345, - host: "smtptesthost.com", - from: "testfrom@test.com", - subject: "Hello!", - }, - }, - null, - controllers.config.save - ) + await this._req(structures.configs.smtp(), null, controllers.config.save) } async saveEtherealSmtpConfig() { await this.deleteConfig(Configs.SMTP) await this._req( - { - type: Configs.SMTP, - config: { - port: 587, - host: "smtp.ethereal.email", - secure: false, - auth: { - user: "don.bahringer@ethereal.email", - pass: "yCKSH8rWyUPbnhGYk9", - }, - connectionTimeout: 1000, // must be less than the jest default of 5000 - }, - }, + structures.configs.smtpEthereal(), null, controllers.config.save ) } - - async saveAdminUser() { - await this._req( - { - email: "testuser@test.com", - password: "test@test.com", - tenantId: TENANT_ID, - }, - null, - controllers.users.adminUser - ) - } } module.exports = TestConfiguration diff --git a/packages/worker/src/tests/controllers.js b/packages/worker/src/tests/controllers.js new file mode 100644 index 0000000000..226355506f --- /dev/null +++ b/packages/worker/src/tests/controllers.js @@ -0,0 +1,7 @@ +module.exports = { + email: require("../api/controllers/global/email"), + workspaces: require("../api/controllers/global/workspaces"), + config: require("../api/controllers/global/configs"), + templates: require("../api/controllers/global/templates"), + users: require("../api/controllers/global/users"), +} diff --git a/packages/worker/src/tests/index.js b/packages/worker/src/tests/index.js new file mode 100644 index 0000000000..9aa88dc444 --- /dev/null +++ b/packages/worker/src/tests/index.js @@ -0,0 +1,12 @@ +const TestConfiguration = require("./TestConfiguration") +const structures = require("./structures") +const mocks = require("./mocks") +const config = new TestConfiguration() +const request = config.getRequest() + +module.exports = { + structures, + mocks, + config, + request, +} diff --git a/packages/worker/src/api/routes/tests/utilities/mocks/core.js b/packages/worker/src/tests/mocks/core.js similarity index 100% rename from packages/worker/src/api/routes/tests/utilities/mocks/core.js rename to packages/worker/src/tests/mocks/core.js diff --git a/packages/worker/src/tests/mocks/email.js b/packages/worker/src/tests/mocks/email.js new file mode 100644 index 0000000000..5e34c38f41 --- /dev/null +++ b/packages/worker/src/tests/mocks/email.js @@ -0,0 +1,10 @@ +exports.mock = () => { + // mock the email system + const sendMailMock = jest.fn() + const nodemailer = require("nodemailer") + nodemailer.createTransport.mockReturnValue({ + sendMail: sendMailMock, + verify: jest.fn(), + }) + return sendMailMock +} diff --git a/packages/worker/src/tests/mocks/index.js b/packages/worker/src/tests/mocks/index.js new file mode 100644 index 0000000000..575938870b --- /dev/null +++ b/packages/worker/src/tests/mocks/index.js @@ -0,0 +1,6 @@ +require("./core") +const email = require("./email") + +module.exports = { + email, +} diff --git a/packages/worker/src/tests/structures/configs.js b/packages/worker/src/tests/structures/configs.js new file mode 100644 index 0000000000..4856df572c --- /dev/null +++ b/packages/worker/src/tests/structures/configs.js @@ -0,0 +1,76 @@ +const { Configs, LOGO_URL } = require("../../constants") +const { utils } = require("@budibase/backend-core") + +exports.oidc = conf => { + return { + type: Configs.OIDC, + config: { + configs: [ + { + configUrl: "http://someconfigurl", + clientID: "clientId", + clientSecret: "clientSecret", + logo: "Microsoft", + name: "Active Directory", + uuid: utils.newid(), + activated: true, + ...conf, + }, + ], + }, + } +} + +exports.google = conf => { + return { + type: Configs.GOOGLE, + config: { + clientID: "clientId", + clientSecret: "clientSecret", + activated: true, + ...conf, + }, + } +} + +exports.smtp = conf => { + return { + type: Configs.SMTP, + config: { + port: 12345, + host: "smtptesthost.com", + from: "testfrom@test.com", + subject: "Hello!", + secure: false, + ...conf, + }, + } +} + +exports.smtpEthereal = () => { + return { + type: Configs.SMTP, + config: { + port: 587, + host: "smtp.ethereal.email", + secure: false, + auth: { + user: "don.bahringer@ethereal.email", + pass: "yCKSH8rWyUPbnhGYk9", + }, + connectionTimeout: 1000, // must be less than the jest default of 5000 + }, + } +} + +exports.settings = conf => { + return { + type: Configs.SETTINGS, + config: { + platformUrl: "http://localhost:10000", + logoUrl: LOGO_URL, + company: "Budibase", + ...conf, + }, + } +} diff --git a/packages/worker/src/tests/structures/index.js b/packages/worker/src/tests/structures/index.js new file mode 100644 index 0000000000..0d82b67d5e --- /dev/null +++ b/packages/worker/src/tests/structures/index.js @@ -0,0 +1,10 @@ +const configs = require("./configs") + +const TENANT_ID = "default" +const CSRF_TOKEN = "e3727778-7af0-4226-b5eb-f43cbe60a306" + +module.exports = { + configs, + TENANT_ID, + CSRF_TOKEN, +} From cd0e37d2c10d7250447bd1ec30bc269cf046bd7f Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 6 Apr 2022 22:00:10 +0100 Subject: [PATCH 16/75] rename data -> structures --- packages/backend-core/src/tests/utilities/index.js | 4 ++-- .../src/tests/utilities/{data => structures}/index.js | 4 ++-- .../src/tests/utilities/{data => structures}/koa.js | 0 packages/backend-core/src/tests/utils.spec.js | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) rename packages/backend-core/src/tests/utilities/{data => structures}/index.js (55%) rename packages/backend-core/src/tests/utilities/{data => structures}/koa.js (100%) diff --git a/packages/backend-core/src/tests/utilities/index.js b/packages/backend-core/src/tests/utilities/index.js index 2b7f91cf0d..db3d027511 100644 --- a/packages/backend-core/src/tests/utilities/index.js +++ b/packages/backend-core/src/tests/utilities/index.js @@ -1,7 +1,7 @@ const mocks = require("./mocks") -const data = require("./data") +const structures = require("./structures") module.exports = { mocks, - data, + structures, } diff --git a/packages/backend-core/src/tests/utilities/data/index.js b/packages/backend-core/src/tests/utilities/structures/index.js similarity index 55% rename from packages/backend-core/src/tests/utilities/data/index.js rename to packages/backend-core/src/tests/utilities/structures/index.js index 78f54e9104..6ac3b1f5f4 100644 --- a/packages/backend-core/src/tests/utilities/data/index.js +++ b/packages/backend-core/src/tests/utilities/structures/index.js @@ -1,8 +1,8 @@ require("../mocks") const koa = require("./koa") -const data = { +const structures = { koa, } -module.exports = data +module.exports = structures diff --git a/packages/backend-core/src/tests/utilities/data/koa.js b/packages/backend-core/src/tests/utilities/structures/koa.js similarity index 100% rename from packages/backend-core/src/tests/utilities/data/koa.js rename to packages/backend-core/src/tests/utilities/structures/koa.js diff --git a/packages/backend-core/src/tests/utils.spec.js b/packages/backend-core/src/tests/utils.spec.js index 618c71ad6a..a1415a21b9 100644 --- a/packages/backend-core/src/tests/utils.spec.js +++ b/packages/backend-core/src/tests/utils.spec.js @@ -1,11 +1,11 @@ -const { data } = require("./utilities") +const { structures } = require("./utilities") const utils = require("../utils") const events = require("../events") describe("utils", () => { describe("platformLogout", () => { it("should call platform logout", async () => { - const ctx = data.koa.newContext() + const ctx = structures.koa.newContext() await utils.platformLogout({ ctx, userId: "test" }) expect(events.auth.logout.mock.calls.length).toBe(1) }) From deaf0de3494b1520204d95cb56c554a72ff7464a Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 6 Apr 2022 22:07:21 +0100 Subject: [PATCH 17/75] layout events + tests --- packages/backend-core/src/events/handlers/layout.js | 4 ---- packages/backend-core/src/tests/utilities/mocks/events.js | 4 ++++ packages/server/src/api/controllers/layout.js | 3 +++ packages/server/src/api/routes/tests/layout.spec.js | 4 ++++ 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/backend-core/src/events/handlers/layout.js b/packages/backend-core/src/events/handlers/layout.js index 682a0e6655..fdeda28d60 100644 --- a/packages/backend-core/src/events/handlers/layout.js +++ b/packages/backend-core/src/events/handlers/layout.js @@ -1,15 +1,11 @@ const events = require("../events") const { Events } = require("../constants") -// LAYOUT - -// TODO exports.created = () => { const properties = {} events.processEvent(Events.LAYOUT_CREATED, properties) } -// TODO exports.deleted = () => { const properties = {} events.processEvent(Events.LAYOUT_DELETED, properties) diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index 74e62ef73c..0ee2eb0b27 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -44,6 +44,10 @@ jest.mock("../../../events", () => { SMTPCreated: jest.fn(), SMTPUpdated: jest.fn(), }, + layout: { + created: jest.fn(), + deleted: jest.fn(), + }, } }) diff --git a/packages/server/src/api/controllers/layout.js b/packages/server/src/api/controllers/layout.js index a92eec424a..1809fce344 100644 --- a/packages/server/src/api/controllers/layout.js +++ b/packages/server/src/api/controllers/layout.js @@ -4,6 +4,7 @@ const { } = require("../../constants/layouts") const { generateLayoutID, getScreenParams } = require("../../db/utils") const { getAppDB } = require("@budibase/backend-core/context") +const { events } = require("@budibase/backend-core") exports.save = async function (ctx) { const db = getAppDB() @@ -19,6 +20,7 @@ exports.save = async function (ctx) { layout._id = layout._id || generateLayoutID() const response = await db.put(layout) + events.layout.created() layout._rev = response.rev ctx.body = layout @@ -46,6 +48,7 @@ exports.destroy = async function (ctx) { } await db.remove(layoutId, layoutRev) + events.layout.deleted() ctx.body = { message: "Layout deleted successfully" } ctx.status = 200 } diff --git a/packages/server/src/api/routes/tests/layout.spec.js b/packages/server/src/api/routes/tests/layout.spec.js index 4842b2cc8e..c149a0fec5 100644 --- a/packages/server/src/api/routes/tests/layout.spec.js +++ b/packages/server/src/api/routes/tests/layout.spec.js @@ -1,6 +1,7 @@ const { checkBuilderEndpoint } = require("./utilities/TestFunctions") const setup = require("./utilities") const { basicLayout } = setup.structures +const { events } = require("@budibase/backend-core") describe("/layouts", () => { let request = setup.getRequest() @@ -12,6 +13,7 @@ describe("/layouts", () => { beforeEach(async () => { await config.init() layout = await config.createLayout() + jest.clearAllMocks() }) describe("save", () => { @@ -23,6 +25,7 @@ describe("/layouts", () => { .expect("Content-Type", /json/) .expect(200) expect(res.body._rev).toBeDefined() + expect(events.layout.created).toBeCalledTimes(1) }) it("should apply authorization to endpoint", async () => { @@ -42,6 +45,7 @@ describe("/layouts", () => { .expect("Content-Type", /json/) .expect(200) expect(res.body.message).toBeDefined() + expect(events.layout.deleted).toBeCalledTimes(1) }) it("should apply authorization to endpoint", async () => { From 8a08e9322f596c3cf0741743e845a9dd1d1094dd Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 6 Apr 2022 22:53:33 +0100 Subject: [PATCH 18/75] org settings events + tests + stub license events --- packages/backend-core/src/events/constants.js | 8 ++- .../backend-core/src/events/handlers/index.js | 4 +- .../src/events/handlers/license.js | 30 ++++++++++ .../src/events/handlers/licensing.js | 10 ---- .../src/tests/utilities/mocks/events.js | 8 +++ .../src/api/controllers/global/configs.js | 59 ++++++++++++++----- .../src/api/routes/tests/configs.spec.js | 50 ++++++++++++++++ .../worker/src/tests/structures/configs.js | 4 +- 8 files changed, 143 insertions(+), 30 deletions(-) create mode 100644 packages/backend-core/src/events/handlers/license.js delete mode 100644 packages/backend-core/src/events/handlers/licensing.js diff --git a/packages/backend-core/src/events/constants.js b/packages/backend-core/src/events/constants.js index 712e41b17f..5fba4d733e 100644 --- a/packages/backend-core/src/events/constants.js +++ b/packages/backend-core/src/events/constants.js @@ -130,8 +130,12 @@ exports.Events = { AUTOMATION_STEP_DELETED: "automation:step:deleted", AUTOMATION_TRIGGER_UPDATED: "automation:trigger:updated", - // LICENSING - LICENSING_QUOTA_EXCEEDED: "licensing:quota:exceeded", + // LICENSE + LICENSE_UPGRADED: "license:upgraded", + LICENSE_DOWNGRADED: "license:downgraded", + LICENSE_UPDATED: "license:updated", + LICENSE_PAIRED: "license:paired", + LICENSE_QUOTA_EXCEEDED: "license:quota:exceeded", // ACCOUNT ACCOUNT_CREATED: "account:created", diff --git a/packages/backend-core/src/events/handlers/index.js b/packages/backend-core/src/events/handlers/index.js index 8cd523e2c0..5b825e7ad9 100644 --- a/packages/backend-core/src/events/handlers/index.js +++ b/packages/backend-core/src/events/handlers/index.js @@ -4,7 +4,7 @@ const auth = require("./auth") const automation = require("./automation") const datasource = require("./datasource") const email = require("./email") -const licensing = require("./licensing") +const license = require("./license") const layout = require("./layout") const org = require("./org") const query = require("./query") @@ -21,7 +21,7 @@ module.exports = { automation, datasource, email, - licensing, + license, layout, org, query, diff --git a/packages/backend-core/src/events/handlers/license.js b/packages/backend-core/src/events/handlers/license.js new file mode 100644 index 0000000000..8e9befc088 --- /dev/null +++ b/packages/backend-core/src/events/handlers/license.js @@ -0,0 +1,30 @@ +const events = require("../events") +const { Events } = require("../constants") + +exports.updgraded = () => { + const properties = {} + events.processEvent(Events.LICENSE_UPGRADED, properties) +} + +exports.downgraded = () => { + const properties = {} + events.processEvent(Events.LICENSE_DOWNGRADED, properties) +} + +exports.updated = () => { + const properties = {} + events.processEvent(Events.LICENSE_UPDATED, properties) +} + +exports.paired = () => { + const properties = {} + events.processEvent(Events.LICENSE_PAIRED, properties) +} + +exports.quotaExceeded = (quotaName, value) => { + const properties = { + name: quotaName, + value, + } + events.processEvent(Events.LICENSE_QUOTA_EXCEEDED, properties) +} diff --git a/packages/backend-core/src/events/handlers/licensing.js b/packages/backend-core/src/events/handlers/licensing.js deleted file mode 100644 index 144a9abf78..0000000000 --- a/packages/backend-core/src/events/handlers/licensing.js +++ /dev/null @@ -1,10 +0,0 @@ -const events = require("../events") -const { Events } = require("../constants") - -exports.quotaExceeded = (quotaName, value) => { - const properties = { - name: quotaName, - value, - } - events.processEvent(Events.LICENSING_QUOTA_EXCEEDED, properties) -} diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index 0ee2eb0b27..ee80e362ab 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -48,6 +48,14 @@ jest.mock("../../../events", () => { created: jest.fn(), deleted: jest.fn(), }, + org: { + nameUpdated: jest.fn(), + logoUpdated: jest.fn(), + platformURLUpdated: jest.fn(), + versionChecked: jest.fn(), + analyticsOptOut: jest.fn(), + npsSubmitted: jest.fn(), + }, } }) diff --git a/packages/worker/src/api/controllers/global/configs.js b/packages/worker/src/api/controllers/global/configs.js index 9ee2648d89..69c4982272 100644 --- a/packages/worker/src/api/controllers/global/configs.js +++ b/packages/worker/src/api/controllers/global/configs.js @@ -29,39 +29,52 @@ const getEventFns = async (db, config) => { if (!existing) { switch (config.type) { - case Configs.SMTP: + case Configs.SMTP: { fns.push(events.email.SMTPCreated) break - case Configs.GOOGLE: + } + case Configs.GOOGLE: { fns.push(() => events.auth.SSOCreated(type)) if (config.config.activated) { fns.push(() => events.auth.SSOActivated(type)) } break - case Configs.OIDC: + } + case Configs.OIDC: { fns.push(() => events.auth.SSOCreated(type)) if (config.config.configs[0].activated) { fns.push(() => events.auth.SSOActivated(type)) } break - case Configs.SETTINGS: - if (config.company) { + } + case Configs.SETTINGS: { + // company + const company = config.config.company + if (company && company !== "Budibase") { fns.push(events.org.nameUpdated) } - if (config.logoUrl) { + + // logo + const logoUrl = config.config.logoUrl + if (logoUrl) { fns.push(events.org.logoUpdated) } - if (config.platformUrl) { + + // platform url + const platformUrl = config.config.platformUrl + if (platformUrl && platformUrl !== "http://localhost:10000") { fns.push(events.org.platformURLUpdated) } break + } } } else { switch (config.type) { - case Configs.SMTP: + case Configs.SMTP: { fns.push(events.email.SMTPUpdated) break - case Configs.GOOGLE: + } + case Configs.GOOGLE: { fns.push(() => events.auth.SSOUpdated(type)) if (!existing.config.activated && config.config.activated) { fns.push(() => events.auth.SSOActivated(type)) @@ -69,7 +82,8 @@ const getEventFns = async (db, config) => { fns.push(() => events.auth.SSODeactivated(type)) } break - case Configs.OIDC: + } + case Configs.OIDC: { fns.push(() => events.auth.SSOUpdated(type)) if ( !existing.config.configs[0].activated && @@ -83,17 +97,34 @@ const getEventFns = async (db, config) => { fns.push(() => events.auth.SSODeactivated(type)) } break - case Configs.SETTINGS: - if (config.company && existing.company !== config.company) { + } + case Configs.SETTINGS: { + // company + const existingCompany = existing.config.company + const company = config.config.company + if (company && company !== "Budibase" && existingCompany !== company) { fns.push(events.org.nameUpdated) } - if (config.logoUrl && existing.logoUrl !== config.logoUrl) { + + // logo + const existingLogoUrl = existing.config.logoUrl + const logoUrl = config.config.logoUrl + if (logoUrl && existingLogoUrl !== logoUrl) { fns.push(events.org.logoUpdated) } - if (config.platformUrl && existing.platformUrl !== config.platformUrl) { + + // platform url + const existingPlatformUrl = existing.config.platformUrl + const platformUrl = config.config.platformUrl + if ( + platformUrl && + platformUrl !== "http://localhost:10000" && + existingPlatformUrl !== platformUrl + ) { fns.push(events.org.platformURLUpdated) } break + } } } diff --git a/packages/worker/src/api/routes/tests/configs.spec.js b/packages/worker/src/api/routes/tests/configs.spec.js index 98e0fef17f..b2dc8124da 100644 --- a/packages/worker/src/api/routes/tests/configs.spec.js +++ b/packages/worker/src/api/routes/tests/configs.spec.js @@ -177,6 +177,56 @@ describe("configs", () => { }) }) }) + + describe("settings", () => { + const saveSettingsConfig = async (conf, _id, _rev) => { + const settingsConfig = structures.configs.settings(conf) + return saveConfig(settingsConfig, _id, _rev) + } + + describe("create", () => { + it ("should create settings config with default settings", async () => { + await config.deleteConfig(Configs.SETTINGS) + + await saveSettingsConfig() + + expect(events.org.nameUpdated).not.toBeCalled() + expect(events.org.logoUpdated).not.toBeCalled() + expect(events.org.platformURLUpdated).not.toBeCalled() + }) + + it ("should create settings config with non-default settings", async () => { + await config.deleteConfig(Configs.SETTINGS) + const conf = { + company: "acme", + logoUrl: "http://example.com", + platformUrl: "http://example.com" + } + + await saveSettingsConfig(conf) + + expect(events.org.nameUpdated).toBeCalledTimes(1) + expect(events.org.logoUpdated).toBeCalledTimes(1) + expect(events.org.platformURLUpdated).toBeCalledTimes(1) + }) + }) + + describe("update", () => { + it ("should update settings config", async () => { + await config.deleteConfig(Configs.SETTINGS) + const settingsConfig = await saveSettingsConfig() + settingsConfig.config.company = "acme" + settingsConfig.config.logoUrl = "http://example.com" + settingsConfig.config.platformUrl = "http://example.com" + + await saveSettingsConfig(settingsConfig.config, settingsConfig._id, settingsConfig._rev) + + expect(events.org.nameUpdated).toBeCalledTimes(1) + expect(events.org.logoUpdated).toBeCalledTimes(1) + expect(events.org.platformURLUpdated).toBeCalledTimes(1) + }) + }) + }) }) it("should return the correct checklist status based on the state of the budibase installation", async () => { diff --git a/packages/worker/src/tests/structures/configs.js b/packages/worker/src/tests/structures/configs.js index 4856df572c..41ba283eaf 100644 --- a/packages/worker/src/tests/structures/configs.js +++ b/packages/worker/src/tests/structures/configs.js @@ -1,4 +1,4 @@ -const { Configs, LOGO_URL } = require("../../constants") +const { Configs } = require("../../constants") const { utils } = require("@budibase/backend-core") exports.oidc = conf => { @@ -68,7 +68,7 @@ exports.settings = conf => { type: Configs.SETTINGS, config: { platformUrl: "http://localhost:10000", - logoUrl: LOGO_URL, + logoUrl: "", company: "Budibase", ...conf, }, From ac8573b67ea42b3eeab6f3bb2e157600af8eeca0 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Thu, 7 Apr 2022 00:38:18 +0100 Subject: [PATCH 19/75] query / update events + tests --- packages/backend-core/src/events/constants.js | 14 ++---- .../src/events/handlers/license.js | 4 +- .../backend-core/src/events/handlers/org.js | 6 +-- .../backend-core/src/events/handlers/query.js | 14 +++--- .../backend-core/src/events/handlers/row.js | 31 +++--------- .../src/tests/utilities/mocks/events.js | 8 ++- .../server/src/api/controllers/analytics.js | 5 -- .../src/api/controllers/query/import/index.ts | 17 ++++++- .../query/import/sources/base/index.ts | 1 + .../controllers/query/import/sources/curl.ts | 4 ++ .../query/import/sources/openapi2.ts | 4 ++ .../query/import/sources/openapi3.ts | 4 ++ .../query/import/tests/index.spec.js | 22 +++++++-- .../server/src/api/controllers/query/index.ts | 10 ++++ .../server/src/api/routes/tests/dev.spec.js | 17 ++++++- .../server/src/api/routes/tests/query.spec.js | 49 ++++++++++++++++--- .../src/api/controllers/global/configs.js | 9 +++- 17 files changed, 148 insertions(+), 71 deletions(-) diff --git a/packages/backend-core/src/events/constants.js b/packages/backend-core/src/events/constants.js index 5fba4d733e..e99c45feb7 100644 --- a/packages/backend-core/src/events/constants.js +++ b/packages/backend-core/src/events/constants.js @@ -38,9 +38,6 @@ exports.Events = { ORG_LOGO_UPDATED: "org:info:logo:updated", ORG_PLATFORM_URL_UPDATED: "org:platformurl:updated", - // ORG / NPS - NPS_SUBMITTED: "nps:submitted", - // ORG / UPDATE UPDATE_VERSION_CHECKED: "version:checked", @@ -77,8 +74,8 @@ exports.Events = { QUERY_CREATED: "query:created", QUERY_UPDATED: "query:updated", QUERY_DELETED: "query:deleted", - QUERY_IMPORTED: "query:imported", - QUERY_RUN: "query:run", + QUERY_IMPORT: "query:import", + // QUERY_RUN: "query:run", QUERY_PREVIEWED: "query:previewed", // TABLE @@ -101,10 +98,7 @@ exports.Events = { VIEW_CALCULATION_DELETED: "view:calculation:created", // ROW - ROW_CREATED: "row:created", - ROW_UPDATED: "row:updated", - ROW_DELETED: "row:deleted", - ROW_IMPORTED: "row:imported", + // ROW_CREATED: "row:created", // BUILDER BUILDER_SERVED: "builder:served", @@ -134,7 +128,7 @@ exports.Events = { LICENSE_UPGRADED: "license:upgraded", LICENSE_DOWNGRADED: "license:downgraded", LICENSE_UPDATED: "license:updated", - LICENSE_PAIRED: "license:paired", + LICENSE_ACTIVATED: "license:activated", LICENSE_QUOTA_EXCEEDED: "license:quota:exceeded", // ACCOUNT diff --git a/packages/backend-core/src/events/handlers/license.js b/packages/backend-core/src/events/handlers/license.js index 8e9befc088..2696fc5df0 100644 --- a/packages/backend-core/src/events/handlers/license.js +++ b/packages/backend-core/src/events/handlers/license.js @@ -16,9 +16,9 @@ exports.updated = () => { events.processEvent(Events.LICENSE_UPDATED, properties) } -exports.paired = () => { +exports.activated = () => { const properties = {} - events.processEvent(Events.LICENSE_PAIRED, properties) + events.processEvent(Events.LICENSE_ACTIVATED, properties) } exports.quotaExceeded = (quotaName, value) => { diff --git a/packages/backend-core/src/events/handlers/org.js b/packages/backend-core/src/events/handlers/org.js index f82ba3bae4..fc5ad13c64 100644 --- a/packages/backend-core/src/events/handlers/org.js +++ b/packages/backend-core/src/events/handlers/org.js @@ -23,12 +23,8 @@ exports.versionChecked = version => { events.processEvent(Events.UPDATE_VERSION_CHECKED, properties) } +// TODO exports.analyticsOptOut = () => { const properties = {} events.processEvent(Events.ANALYTICS_OPT_OUT, properties) } - -exports.npsSubmitted = () => { - const properties = {} - events.processEvent(Events.NPS_SUBMITTED, properties) -} diff --git a/packages/backend-core/src/events/handlers/query.js b/packages/backend-core/src/events/handlers/query.js index aa303bd06f..9f01788a05 100644 --- a/packages/backend-core/src/events/handlers/query.js +++ b/packages/backend-core/src/events/handlers/query.js @@ -6,29 +6,27 @@ exports.created = () => { events.processEvent(Events.QUERY_CREATED, properties) } -// TODO exports.updated = () => { const properties = {} events.processEvent(Events.QUERY_UPDATED, properties) } -// TODO exports.deleted = () => { const properties = {} events.processEvent(Events.QUERY_DELETED, properties) } // TODO -exports.imported = () => { +exports.import = () => { const properties = {} - events.processEvent(Events.QUERY_IMPORTED, properties) + events.processEvent(Events.QUERY_IMPORT, properties) } // TODO -exports.run = () => { - const properties = {} - events.processEvent(Events.QUERY_RUN, properties) -} +// exports.run = () => { +// const properties = {} +// events.processEvent(Events.QUERY_RUN, properties) +// } // TODO exports.previewed = () => { diff --git a/packages/backend-core/src/events/handlers/row.js b/packages/backend-core/src/events/handlers/row.js index 3f5b30839a..07c18e241f 100644 --- a/packages/backend-core/src/events/handlers/row.js +++ b/packages/backend-core/src/events/handlers/row.js @@ -1,26 +1,7 @@ -const events = require("../events") -const { Events } = require("../constants") +// const events = require("../events") +// const { Events } = require("../constants") -exports.created = () => { - const properties = {} - events.processEvent(Events.ROW_CREATED, properties) -} - -// TODO -exports.imported = () => { - const properties = {} - events.processEvent(Events.ROW_IMPORTED, properties) - exports.rowCreated() -} - -// TODO -exports.updated = () => { - const properties = {} - events.processEvent(Events.ROW_UPDATED, properties) -} - -// TODO -exports.deleted = () => { - const properties = {} - events.processEvent(Events.ROW_DELETED, properties) -} +// exports.created = () => { +// const properties = {} +// events.processEvent(Events.ROW_CREATED, properties) +// } diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index ee80e362ab..c33111501f 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -54,7 +54,13 @@ jest.mock("../../../events", () => { platformURLUpdated: jest.fn(), versionChecked: jest.fn(), analyticsOptOut: jest.fn(), - npsSubmitted: jest.fn(), + }, + query: { + created: jest.fn(), + updated: jest.fn(), + deleted: jest.fn(), + import: jest.fn(), + previewed: jest.fn(), }, } }) diff --git a/packages/server/src/api/controllers/analytics.js b/packages/server/src/api/controllers/analytics.js index dc96b28062..faf567d957 100644 --- a/packages/server/src/api/controllers/analytics.js +++ b/packages/server/src/api/controllers/analytics.js @@ -14,11 +14,6 @@ exports.endUserPing = async ctx => { return } - // posthogClient.identify({ - // distinctId: ctx.user && ctx.user._id, - // properties: {}, - // }) - analytics.captureEvent(ctx.user._id, "budibase:end_user_ping", { appId: ctx.appId, }) diff --git a/packages/server/src/api/controllers/query/import/index.ts b/packages/server/src/api/controllers/query/import/index.ts index d6f5beedf5..f2cf764bf5 100644 --- a/packages/server/src/api/controllers/query/import/index.ts +++ b/packages/server/src/api/controllers/query/import/index.ts @@ -7,6 +7,8 @@ import { Query } from "./../../../../definitions/common" import { Curl } from "./sources/curl" // @ts-ignore import { getAppDB } from "@budibase/backend-core/context" +import { events } from "@budibase/backend-core" + interface ImportResult { errorQueries: Query[] queries: Query[] @@ -36,7 +38,7 @@ export class RestImporter { } importQueries = async (datasourceId: string): Promise => { - // constuct the queries + // construct the queries let queries = await this.source.getQueries(datasourceId) // validate queries @@ -76,9 +78,20 @@ export class RestImporter { } }) + const successQueries = Object.values(queryIndex) + + // events + const count = successQueries.length + const importSource = this.source.getImportSource() + const datasource = await db.get(datasourceId) + events.query.import({ datasource, importSource, count }) + for (let query of successQueries) { + events.query.created(query) + } + return { errorQueries, - queries: Object.values(queryIndex), + queries: successQueries, } } } diff --git a/packages/server/src/api/controllers/query/import/sources/base/index.ts b/packages/server/src/api/controllers/query/import/sources/base/index.ts index 5f0b203984..28737b9731 100644 --- a/packages/server/src/api/controllers/query/import/sources/base/index.ts +++ b/packages/server/src/api/controllers/query/import/sources/base/index.ts @@ -17,6 +17,7 @@ export abstract class ImportSource { abstract isSupported(data: string): Promise abstract getInfo(): Promise abstract getQueries(datasourceId: string): Promise + abstract getImportSource(): string constructQuery = ( datasourceId: string, diff --git a/packages/server/src/api/controllers/query/import/sources/curl.ts b/packages/server/src/api/controllers/query/import/sources/curl.ts index a59b036467..223b9ed98c 100644 --- a/packages/server/src/api/controllers/query/import/sources/curl.ts +++ b/packages/server/src/api/controllers/query/import/sources/curl.ts @@ -71,6 +71,10 @@ export class Curl extends ImportSource { } } + getImportSource(): string { + return "curl" + } + getQueries = async (datasourceId: string): Promise => { const url = this.getUrl() const name = url.pathname diff --git a/packages/server/src/api/controllers/query/import/sources/openapi2.ts b/packages/server/src/api/controllers/query/import/sources/openapi2.ts index c193654909..e2dcec7613 100644 --- a/packages/server/src/api/controllers/query/import/sources/openapi2.ts +++ b/packages/server/src/api/controllers/query/import/sources/openapi2.ts @@ -70,6 +70,10 @@ export class OpenAPI2 extends OpenAPISource { } } + getImportSource(): string { + return "openapi2.0" + } + getQueries = async (datasourceId: string): Promise => { const url = this.getUrl() const queries = [] diff --git a/packages/server/src/api/controllers/query/import/sources/openapi3.ts b/packages/server/src/api/controllers/query/import/sources/openapi3.ts index e6bdbe8682..f08f21e495 100644 --- a/packages/server/src/api/controllers/query/import/sources/openapi3.ts +++ b/packages/server/src/api/controllers/query/import/sources/openapi3.ts @@ -106,6 +106,10 @@ export class OpenAPI3 extends OpenAPISource { } } + getImportSource(): string { + return "openapi3.0" + } + getQueries = async (datasourceId: string): Promise => { let url: string | URL | undefined if (this.document.servers?.length) { diff --git a/packages/server/src/api/controllers/query/import/tests/index.spec.js b/packages/server/src/api/controllers/query/import/tests/index.spec.js index f207213587..ad4dd3baed 100644 --- a/packages/server/src/api/controllers/query/import/tests/index.spec.js +++ b/packages/server/src/api/controllers/query/import/tests/index.spec.js @@ -1,9 +1,10 @@ const TestConfig = require("../../../../../tests/utilities/TestConfiguration") - const { RestImporter } = require("../index") - const fs = require("fs") const path = require('path') +const { events} = require("@budibase/backend-core") +const { mocks } = require("@budibase/backend-core/testUtils") +mocks.date.mock() const getData = (file) => { return fs.readFileSync(path.join(__dirname, `../sources/tests/${file}`), "utf8") @@ -103,9 +104,13 @@ describe("Rest Importer", () => { const testImportQueries = async (key, data, assertions) => { await init(data) - const importResult = await restImporter.importQueries("datasourceId") + const datasource = await config.createDatasource() + const importResult = await restImporter.importQueries(datasource._id) expect(importResult.errorQueries.length).toBe(0) expect(importResult.queries.length).toBe(assertions[key].count) + expect(events.query.import).toBeCalledTimes(1) + const eventData = { datasource, importSource: assertions[key].source, count: assertions[key].count} + expect(events.query.import).toBeCalledWith(eventData) jest.clearAllMocks() } @@ -116,32 +121,41 @@ describe("Rest Importer", () => { // openapi2 (swagger) "oapi2CrudJson" : { count: 6, + source: "openapi2.0", }, "oapi2CrudYaml" :{ count: 6, + source: "openapi2.0" }, "oapi2PetstoreJson" : { count: 20, + source: "openapi2.0" }, "oapi2PetstoreYaml" :{ count: 20, + source: "openapi2.0" }, // openapi3 "oapi3CrudJson" : { count: 6, + source: "openapi3.0" }, "oapi3CrudYaml" :{ count: 6, + source: "openapi3.0" }, "oapi3PetstoreJson" : { count: 19, + source: "openapi3.0" }, "oapi3PetstoreYaml" :{ count: 19, + source: "openapi3.0" }, // curl "curl": { - count: 1 + count: 1, + source: "curl" } } await runTest(testImportQueries, assertions) diff --git a/packages/server/src/api/controllers/query/index.ts b/packages/server/src/api/controllers/query/index.ts index 3f9d0275b4..4ee5c38fbf 100644 --- a/packages/server/src/api/controllers/query/index.ts +++ b/packages/server/src/api/controllers/query/index.ts @@ -7,6 +7,7 @@ import { invalidateDynamicVariables } from "../../../threads/utils" import { QUERY_THREAD_TIMEOUT } from "../../../environment" import { getAppDB } from "@budibase/backend-core/context" import { quotas } from "@budibase/pro" +import { events } from "@budibase/backend-core" const Runner = new Thread(ThreadType.QUERY, { timeoutMs: QUERY_THREAD_TIMEOUT || 10000, @@ -80,11 +81,18 @@ export async function save(ctx: any) { const db = getAppDB() const query = ctx.request.body + const datasource = await db.get(query.datasourceId) + + let eventFn if (!query._id) { query._id = generateQueryID(query.datasourceId) + eventFn = () => events.query.created(datasource, query) + } else { + eventFn = () => events.query.updated(datasource, query) } const response = await db.put(query) + eventFn() query._rev = response.rev ctx.body = query @@ -124,6 +132,7 @@ export async function preview(ctx: any) { }) const { rows, keys, info, extra } = await quotas.addQuery(runFn) + events.query.previewed(datasource) ctx.body = { rows, schemaFields: [...new Set(keys)], @@ -211,4 +220,5 @@ export async function destroy(ctx: any) { await db.remove(ctx.params.queryId, ctx.params.revId) ctx.message = `Query deleted.` ctx.status = 200 + events.query.deleted() } diff --git a/packages/server/src/api/routes/tests/dev.spec.js b/packages/server/src/api/routes/tests/dev.spec.js index 91a2c67c56..0138ce2a6c 100644 --- a/packages/server/src/api/routes/tests/dev.spec.js +++ b/packages/server/src/api/routes/tests/dev.spec.js @@ -1,5 +1,6 @@ const setup = require("./utilities") const { events } = require("@budibase/backend-core") +const version = require("../../../../package.json").version describe("/dev", () => { let request = setup.getRequest() @@ -19,7 +20,21 @@ describe("/dev", () => { .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) - expect(events.app.reverted.mock.calls.length).toBe(1) + expect(events.app.reverted).toBeCalledTimes(1) + }) + }) + + describe("version", () => { + it("should get the installation version", async () => { + const res = await request + .get(`/api/dev/version`) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + + expect(res.body.version).toBe(version) + expect(events.org.versionChecked).toBeCalledTimes(1) + expect(events.org.versionChecked).toBeCalledWith(version) }) }) }) \ No newline at end of file diff --git a/packages/server/src/api/routes/tests/query.spec.js b/packages/server/src/api/routes/tests/query.spec.js index 7ac757c7e9..40d6890904 100644 --- a/packages/server/src/api/routes/tests/query.spec.js +++ b/packages/server/src/api/routes/tests/query.spec.js @@ -15,6 +15,7 @@ const { checkCacheForDynamicVariable } = require("../../../threads/utils") const { basicQuery, basicDatasource } = setup.structures const { mocks } = require("@budibase/backend-core/testUtils") mocks.date.mock() +const { events } = require("@budibase/backend-core") describe("/queries", () => { let request = setup.getRequest() @@ -40,16 +41,21 @@ describe("/queries", () => { return { datasource, query } } + const createQuery = async (query) => { + return request + .post(`/api/queries`) + .send(query) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + } + describe("create", () => { it("should create a new query", async () => { const { _id } = await config.createDatasource() const query = basicQuery(_id) - const res = await request - .post(`/api/queries`) - .send(query) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) + jest.clearAllMocks() + const res = await createQuery(query) expect(res.res.statusMessage).toEqual( `Query ${query.name} saved successfully.` @@ -61,6 +67,33 @@ describe("/queries", () => { createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }) + expect(events.query.created).toBeCalledTimes(1) + expect(events.query.updated).not.toBeCalled() + }) + }) + + describe("update", () => { + it("should update query", async () => { + const { _id } = await config.createDatasource() + const query = basicQuery(_id) + const res = await createQuery(query) + jest.clearAllMocks() + query._id = res.body._id + query._rev = res.body._rev + await createQuery(query) + + expect(res.res.statusMessage).toEqual( + `Query ${query.name} saved successfully.` + ) + expect(res.body).toEqual({ + _rev: res.body._rev, + _id: res.body._id, + ...query, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString() + }) + expect(events.query.created).not.toBeCalled() + expect(events.query.updated).toBeCalledTimes(1) }) }) @@ -155,6 +188,7 @@ describe("/queries", () => { .expect(200) expect(res.body).toEqual([]) + expect(events.query.deleted).toBeCalledTimes(1) }) it("should apply authorization to endpoint", async () => { @@ -183,6 +217,9 @@ describe("/queries", () => { // these responses come from the mock expect(res.body.schemaFields).toEqual(["a", "b"]) expect(res.body.rows.length).toEqual(1) + expect(events.query.previewed).toBeCalledTimes(1) + datasource.config = { schema: "public" } + expect(events.query.previewed).toBeCalledWith(datasource) }) it("should apply authorization to endpoint", async () => { diff --git a/packages/worker/src/api/controllers/global/configs.js b/packages/worker/src/api/controllers/global/configs.js index 69c4982272..8c820d83e8 100644 --- a/packages/worker/src/api/controllers/global/configs.js +++ b/packages/worker/src/api/controllers/global/configs.js @@ -62,7 +62,11 @@ const getEventFns = async (db, config) => { // platform url const platformUrl = config.config.platformUrl - if (platformUrl && platformUrl !== "http://localhost:10000") { + if ( + platformUrl && + platformUrl !== "http://localhost:10000" && + env.SELF_HOSTED + ) { fns.push(events.org.platformURLUpdated) } break @@ -119,7 +123,8 @@ const getEventFns = async (db, config) => { if ( platformUrl && platformUrl !== "http://localhost:10000" && - existingPlatformUrl !== platformUrl + existingPlatformUrl !== platformUrl && + env.SELF_HOSTED ) { fns.push(events.org.platformURLUpdated) } From e98e6593460660d48931a672618d42cc2eb1fdee Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Fri, 8 Apr 2022 01:28:22 +0100 Subject: [PATCH 20/75] user / rbac events + tests --- packages/backend-core/src/errors/base.js | 4 +- packages/backend-core/src/errors/generic.js | 11 + packages/backend-core/src/errors/http.js | 12 + packages/backend-core/src/errors/index.js | 10 +- packages/backend-core/src/errors/licensing.js | 13 +- packages/backend-core/src/events/constants.js | 2 + packages/backend-core/src/events/events.js | 2 +- .../backend-core/src/events/handlers/index.js | 2 + .../src/events/handlers/license.js | 5 + .../backend-core/src/events/handlers/query.js | 14 +- .../backend-core/src/events/handlers/role.js | 20 +- .../backend-core/src/events/handlers/user.js | 42 +-- packages/backend-core/src/index.js | 5 + .../src/middleware/passport/local.js | 4 +- .../middleware/passport/third-party-common.js | 6 +- .../src/tests/utilities/mocks/events.js | 23 ++ packages/backend-core/src/users.js | 19 ++ packages/backend-core/src/utils.js | 131 +-------- .../src/api/controllers/query/import/index.ts | 2 +- .../query/import/tests/index.spec.js | 3 +- .../server/src/api/controllers/query/index.ts | 7 +- packages/server/src/api/controllers/role.js | 10 + .../server/src/api/routes/tests/query.spec.js | 1 + .../server/src/api/routes/tests/role.spec.js | 47 +++- .../worker/src/api/controllers/global/auth.ts | 15 +- .../src/api/controllers/global/roles.js | 6 +- .../worker/src/api/controllers/global/self.js | 4 +- .../src/api/controllers/global/users.ts | 105 ++----- packages/worker/src/api/routes/global/self.js | 4 +- .../worker/src/api/routes/global/users.js | 6 +- .../worker/src/api/routes/tests/users.spec.js | 257 +++++++++++++++++- .../worker/src/api/routes/validation/index.ts | 1 + .../validation/users.ts} | 8 +- packages/worker/src/api/utilities/index.js | 33 --- packages/worker/src/sdk/index.ts | 1 + packages/worker/src/sdk/users/events.ts | 136 +++++++++ packages/worker/src/sdk/users/index.ts | 1 + packages/worker/src/sdk/users/users.ts | 180 ++++++++++++ .../worker/src/tests/TestConfiguration.js | 19 +- packages/worker/src/tests/structures/index.js | 2 + packages/worker/src/tests/structures/users.ts | 28 ++ 41 files changed, 861 insertions(+), 340 deletions(-) create mode 100644 packages/backend-core/src/errors/generic.js create mode 100644 packages/backend-core/src/errors/http.js create mode 100644 packages/backend-core/src/users.js create mode 100644 packages/worker/src/api/routes/validation/index.ts rename packages/worker/src/api/{utilities/validation.js => routes/validation/users.ts} (80%) delete mode 100644 packages/worker/src/api/utilities/index.js create mode 100644 packages/worker/src/sdk/index.ts create mode 100644 packages/worker/src/sdk/users/events.ts create mode 100644 packages/worker/src/sdk/users/index.ts create mode 100644 packages/worker/src/sdk/users/users.ts create mode 100644 packages/worker/src/tests/structures/users.ts diff --git a/packages/backend-core/src/errors/base.js b/packages/backend-core/src/errors/base.js index d31f9838f4..7cb0c0fc23 100644 --- a/packages/backend-core/src/errors/base.js +++ b/packages/backend-core/src/errors/base.js @@ -1,8 +1,8 @@ class BudibaseError extends Error { - constructor(message, type, code) { + constructor(message, code, type) { super(message) - this.type = type this.code = code + this.type = type } } diff --git a/packages/backend-core/src/errors/generic.js b/packages/backend-core/src/errors/generic.js new file mode 100644 index 0000000000..5c7661f035 --- /dev/null +++ b/packages/backend-core/src/errors/generic.js @@ -0,0 +1,11 @@ +const { BudibaseError } = require("./base") + +class GenericError extends BudibaseError { + constructor(message, code, type) { + super(message, code, type ? type : "generic") + } +} + +module.exports = { + GenericError, +} diff --git a/packages/backend-core/src/errors/http.js b/packages/backend-core/src/errors/http.js new file mode 100644 index 0000000000..342d663da7 --- /dev/null +++ b/packages/backend-core/src/errors/http.js @@ -0,0 +1,12 @@ +const { GenericError } = require("./generic") + +class HTTPError extends GenericError { + constructor(message, httpStatus, code, type) { + super(message, code ? code : "http", type) + this.status = httpStatus + } +} + +module.exports = { + HTTPError, +} diff --git a/packages/backend-core/src/errors/index.js b/packages/backend-core/src/errors/index.js index 4f3b4e0c41..58b4eea8c5 100644 --- a/packages/backend-core/src/errors/index.js +++ b/packages/backend-core/src/errors/index.js @@ -1,12 +1,11 @@ +const http = require("./http") const licensing = require("./licensing") const codes = { ...licensing.codes, } -const types = { - ...licensing.types, -} +const types = [licensing.type] const context = { ...licensing.context, @@ -36,6 +35,9 @@ const getPublicError = err => { module.exports = { codes, types, - UsageLimitError: licensing.UsageLimitError, + errors: { + UsageLimitError: licensing.UsageLimitError, + HTTPError: http.HTTPError, + }, getPublicError, } diff --git a/packages/backend-core/src/errors/licensing.js b/packages/backend-core/src/errors/licensing.js index c05f9c561e..0d8ce08146 100644 --- a/packages/backend-core/src/errors/licensing.js +++ b/packages/backend-core/src/errors/licensing.js @@ -1,8 +1,6 @@ -const { BudibaseError } = require("./base") +const { HTTPError } = require("./http") -const types = { - LICENSE_ERROR: "license_error", -} +const type = "license_error" const codes = { USAGE_LIMIT_EXCEEDED: "usage_limit_exceeded", @@ -16,16 +14,15 @@ const context = { }, } -class UsageLimitError extends BudibaseError { +class UsageLimitError extends HTTPError { constructor(message, limitName) { - super(message, types.LICENSE_ERROR, codes.USAGE_LIMIT_EXCEEDED) + super(message, 400, codes.USAGE_LIMIT_EXCEEDED, type) this.limitName = limitName - this.status = 400 } } module.exports = { - types, + type, codes, context, UsageLimitError, diff --git a/packages/backend-core/src/events/constants.js b/packages/backend-core/src/events/constants.js index e99c45feb7..c35fc71ab8 100644 --- a/packages/backend-core/src/events/constants.js +++ b/packages/backend-core/src/events/constants.js @@ -59,8 +59,10 @@ exports.Events = { // ROLE ROLE_CREATED: "role:created", + ROLE_UPDATED: "role:updated", ROLE_DELETED: "role:deleted", ROLE_ASSIGNED: "role:assigned", + ROLE_UNASSIGNED: "role:unassigned", // APP / CLIENT CLIENT_SERVED: "client:served", diff --git a/packages/backend-core/src/events/events.js b/packages/backend-core/src/events/events.js index 8a51c602c7..56fe069c7e 100644 --- a/packages/backend-core/src/events/events.js +++ b/packages/backend-core/src/events/events.js @@ -4,7 +4,7 @@ const analytics = require("../analytics") const logEvent = messsage => { const tenantId = getTenantId() const userId = getTenantId() // TODO - console.log(`[tenant=${tenantId}] [user=${userId}] ${messsage}`) + console.log(`[audit] [tenant=${tenantId}] [user=${userId}] ${messsage}`) } exports.processEvent = (event, properties) => { diff --git a/packages/backend-core/src/events/handlers/index.js b/packages/backend-core/src/events/handlers/index.js index 5b825e7ad9..c9007ae28f 100644 --- a/packages/backend-core/src/events/handlers/index.js +++ b/packages/backend-core/src/events/handlers/index.js @@ -8,6 +8,7 @@ const license = require("./license") const layout = require("./layout") const org = require("./org") const query = require("./query") +const role = require("./role") const row = require("./screen") const table = require("./table") const serve = require("./serve") @@ -25,6 +26,7 @@ module.exports = { layout, org, query, + role, row, table, serve, diff --git a/packages/backend-core/src/events/handlers/license.js b/packages/backend-core/src/events/handlers/license.js index 2696fc5df0..68107e8fd9 100644 --- a/packages/backend-core/src/events/handlers/license.js +++ b/packages/backend-core/src/events/handlers/license.js @@ -1,26 +1,31 @@ const events = require("../events") const { Events } = require("../constants") +// TODO exports.updgraded = () => { const properties = {} events.processEvent(Events.LICENSE_UPGRADED, properties) } +// TODO exports.downgraded = () => { const properties = {} events.processEvent(Events.LICENSE_DOWNGRADED, properties) } +// TODO exports.updated = () => { const properties = {} events.processEvent(Events.LICENSE_UPDATED, properties) } +// TODO exports.activated = () => { const properties = {} events.processEvent(Events.LICENSE_ACTIVATED, properties) } +// TODO exports.quotaExceeded = (quotaName, value) => { const properties = { name: quotaName, diff --git a/packages/backend-core/src/events/handlers/query.js b/packages/backend-core/src/events/handlers/query.js index 9f01788a05..d878c79610 100644 --- a/packages/backend-core/src/events/handlers/query.js +++ b/packages/backend-core/src/events/handlers/query.js @@ -1,23 +1,24 @@ const events = require("../events") const { Events } = require("../constants") -exports.created = () => { +/* eslint-disable */ + +exports.created = (datasource, query) => { const properties = {} events.processEvent(Events.QUERY_CREATED, properties) } -exports.updated = () => { +exports.updated = (datasource, query) => { const properties = {} events.processEvent(Events.QUERY_UPDATED, properties) } -exports.deleted = () => { +exports.deleted = (datasource, query) => { const properties = {} events.processEvent(Events.QUERY_DELETED, properties) } -// TODO -exports.import = () => { +exports.import = (datasource, importSource, count) => { const properties = {} events.processEvent(Events.QUERY_IMPORT, properties) } @@ -28,8 +29,7 @@ exports.import = () => { // events.processEvent(Events.QUERY_RUN, properties) // } -// TODO -exports.previewed = () => { +exports.previewed = datasource => { const properties = {} events.processEvent(Events.QUERY_PREVIEWED, properties) } diff --git a/packages/backend-core/src/events/handlers/role.js b/packages/backend-core/src/events/handlers/role.js index 16b342f214..1181af01ed 100644 --- a/packages/backend-core/src/events/handlers/role.js +++ b/packages/backend-core/src/events/handlers/role.js @@ -1,19 +1,29 @@ const events = require("../events") const { Events } = require("../constants") -exports.created = () => { +/* eslint-disable */ + +exports.created = role => { const properties = {} events.processEvent(Events.ROLE_CREATED, properties) } -// TODO -exports.deleted = () => { +exports.updated = role => { + const properties = {} + events.processEvent(Events.ROLE_UPDATED, properties) +} + +exports.deleted = role => { const properties = {} events.processEvent(Events.ROLE_DELETED, properties) } -// TODO -exports.assigned = () => { +exports.assigned = (user, role) => { const properties = {} events.processEvent(Events.ROLE_ASSIGNED, properties) } + +exports.unassigned = (user, role) => { + const properties = {} + events.processEvent(Events.ROLE_UNASSIGNED, properties) +} diff --git a/packages/backend-core/src/events/handlers/user.js b/packages/backend-core/src/events/handlers/user.js index 7d1bc592e8..8cbb627797 100644 --- a/packages/backend-core/src/events/handlers/user.js +++ b/packages/backend-core/src/events/handlers/user.js @@ -1,85 +1,87 @@ const events = require("../events") const { Events } = require("../constants") -// TODO -exports.created = () => { +/* eslint-disable */ + +exports.created = user => { const properties = {} events.processEvent(Events.USER_CREATED, properties) } -// TODO -exports.updated = () => { +exports.updated = user => { const properties = {} events.processEvent(Events.USER_UPDATED, properties) } -exports.deleted = () => { +exports.deleted = user => { const properties = {} events.processEvent(Events.USER_DELETED, properties) } // TODO -exports.passwordForceReset = () => { +exports.passwordForceReset = user => { const properties = {} events.processEvent(Events.USER_PASSWORD_FORCE_RESET, properties) } // PERMISSIONS -// TODO -exports.permissionAdminAssigned = () => { +exports.permissionAdminAssigned = user => { const properties = {} events.processEvent(Events.USER_PERMISSION_ADMIN_ASSIGNED, properties) } -// TODO -exports.permissionAdminRemoved = () => { +exports.permissionAdminRemoved = user => { const properties = {} events.processEvent(Events.USER_PERMISSION_ADMIN_REMOVED, properties) } -// TODO -exports.permissionBuilderAssigned = () => { +exports.permissionBuilderAssigned = user => { const properties = {} events.processEvent(Events.USER_PERMISSION_BUILDER_ASSIGNED, properties) } -// TODO -exports.permissionBuilderRemoved = () => { +exports.permissionBuilderRemoved = user => { const properties = {} events.processEvent(Events.USER_PERMISSION_BUILDER_REMOVED, properties) } // INVITE -exports.invited = () => { +// TODO +exports.invited = user => { const properties = {} events.processEvent(Events.USER_INVITED, properties) } -exports.inviteAccepted = () => { +// TODO +exports.inviteAccepted = user => { const properties = {} events.processEvent(Events.USER_INVITED_ACCEPTED, properties) } // SELF -exports.selfUpdated = () => { +// TODO +exports.selfUpdated = user => { const properties = {} events.processEvent(Events.USER_SELF_UPDATED, properties) } -exports.selfPasswordUpdated = () => { +// TODO +exports.selfPasswordUpdated = user => { const properties = {} events.processEvent(Events.USER_SELF_PASSWORD_UPDATED, properties) } -exports.passwordResetRequested = () => { +// TODO +exports.passwordResetRequested = user => { const properties = {} events.processEvent(Events.USER_PASSWORD_RESET_REQUESTED, properties) } -exports.passwordReset = () => { +// TODO +exports.passwordReset = user => { const properties = {} events.processEvent(Events.USER_PASSWORD_RESET, properties) } diff --git a/packages/backend-core/src/index.js b/packages/backend-core/src/index.js index 8450ba58d6..b255567639 100644 --- a/packages/backend-core/src/index.js +++ b/packages/backend-core/src/index.js @@ -1,4 +1,5 @@ const db = require("./db") +const errors = require("./errors") module.exports = { init(opts = {}) { @@ -11,15 +12,19 @@ module.exports = { redis: require("../redis"), objectStore: require("../objectStore"), utils: require("../utils"), + users: require("./users"), cache: require("../cache"), auth: require("../auth"), constants: require("../constants"), migrations: require("../migrations"), errors: require("./errors"), + ...errors.errors, env: require("./environment"), accounts: require("./cloud/accounts"), tenancy: require("./tenancy"), featureFlags: require("./featureFlags"), events: require("./events"), analytics: require("./analytics"), + sessions: require("./security/sessions"), + deprovisioning: require("./context/deprovision"), } diff --git a/packages/backend-core/src/middleware/passport/local.js b/packages/backend-core/src/middleware/passport/local.js index 2149bd3e18..9377d895da 100644 --- a/packages/backend-core/src/middleware/passport/local.js +++ b/packages/backend-core/src/middleware/passport/local.js @@ -2,7 +2,7 @@ const jwt = require("jsonwebtoken") const { UserStatus } = require("../../constants") const { compare } = require("../../hashing") const env = require("../../environment") -const { getGlobalUserByEmail } = require("../../utils") +const users = require("../../users") const { authError } = require("./utils") const { newid } = require("../../hashing") const { createASession } = require("../../security/sessions") @@ -28,7 +28,7 @@ exports.authenticate = async function (ctx, email, password, done) { if (!email) return authError(done, "Email Required") if (!password) return authError(done, "Password Required") - const dbUser = await getGlobalUserByEmail(email) + const dbUser = await users.getGlobalUserByEmail(email) if (dbUser == null) { return authError(done, "User not found") } diff --git a/packages/backend-core/src/middleware/passport/third-party-common.js b/packages/backend-core/src/middleware/passport/third-party-common.js index 3fbfb145bc..44d80f7ee7 100644 --- a/packages/backend-core/src/middleware/passport/third-party-common.js +++ b/packages/backend-core/src/middleware/passport/third-party-common.js @@ -4,7 +4,7 @@ const { generateGlobalUserID } = require("../../db/utils") const { authError } = require("./utils") const { newid } = require("../../hashing") const { createASession } = require("../../security/sessions") -const { getGlobalUserByEmail } = require("../../utils") +const users = require("../../users") const { getGlobalDB, getTenantId } = require("../../tenancy") const fetch = require("node-fetch") @@ -52,7 +52,7 @@ exports.authenticateThirdParty = async function ( // fallback to loading by email if (!dbUser) { - dbUser = await getGlobalUserByEmail(thirdPartyUser.email) + dbUser = await users.getGlobalUserByEmail(thirdPartyUser.email) } // exit early if there is still no user and auto creation is disabled @@ -81,7 +81,7 @@ exports.authenticateThirdParty = async function ( // create or sync the user let response try { - response = await saveUserFn(dbUser, getTenantId(), false, false) + response = await saveUserFn(dbUser, false, false) } catch (err) { return authError(done, err) } diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index c33111501f..ad7ca3fee9 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -62,6 +62,29 @@ jest.mock("../../../events", () => { import: jest.fn(), previewed: jest.fn(), }, + role: { + created: jest.fn(), + updated: jest.fn(), + deleted: jest.fn(), + assigned: jest.fn(), + unassigned: jest.fn(), + }, + user: { + created: jest.fn(), + updated: jest.fn(), + deleted: jest.fn(), + passwordForceReset: jest.fn(), + permissionAdminAssigned: jest.fn(), + permissionAdminRemoved: jest.fn(), + permissionBuilderAssigned: jest.fn(), + permissionBuilderRemoved: jest.fn(), + invited: jest.fn(), + inviteAccepted: jest.fn(), + selfUpdated: jest.fn(), + selfPasswordUpdated: jest.fn(), + passwordResetRequested: jest.fn(), + passwordReset: jest.fn(), + }, } }) diff --git a/packages/backend-core/src/users.js b/packages/backend-core/src/users.js new file mode 100644 index 0000000000..07a60f2884 --- /dev/null +++ b/packages/backend-core/src/users.js @@ -0,0 +1,19 @@ +const { ViewNames } = require("./db/utils") +const { queryGlobalView } = require("./db/views") + +/** + * Given an email address this will use a view to search through + * all the users to find one with this email address. + * @param {string} email the email to lookup the user by. + * @return {Promise} + */ +exports.getGlobalUserByEmail = async email => { + if (email == null) { + throw "Must supply an email address to view" + } + + return queryGlobalView(ViewNames.USER_BY_EMAIL, { + key: email.toLowerCase(), + include_docs: true, + }) +} diff --git a/packages/backend-core/src/utils.js b/packages/backend-core/src/utils.js index 302b4a7bf8..7d085d24c9 100644 --- a/packages/backend-core/src/utils.js +++ b/packages/backend-core/src/utils.js @@ -1,24 +1,10 @@ -const { - DocumentTypes, - SEPARATOR, - ViewNames, - generateGlobalUserID, -} = require("./db/utils") +const { DocumentTypes, SEPARATOR, ViewNames } = require("./db/utils") const jwt = require("jsonwebtoken") const { options } = require("./middleware/passport/jwt") const { queryGlobalView } = require("./db/views") -const { Headers, UserStatus, Cookies, MAX_VALID_DATE } = require("./constants") -const { - getGlobalDB, - updateTenantId, - getTenantUser, - tryAddTenant, -} = require("./tenancy") -const environment = require("./environment") -const accounts = require("./cloud/accounts") -const { hash } = require("./hashing") -const userCache = require("./cache/user") +const { Headers, Cookies, MAX_VALID_DATE } = require("./constants") const env = require("./environment") +const userCache = require("./cache/user") const { getUserSessions, invalidateSessions } = require("./security/sessions") const events = require("./events") @@ -106,8 +92,8 @@ exports.setCookie = (ctx, value, name = "builder", opts = { sign: true }) => { overwrite: true, } - if (environment.COOKIE_DOMAIN) { - config.domain = environment.COOKIE_DOMAIN + if (env.COOKIE_DOMAIN) { + config.domain = env.COOKIE_DOMAIN } ctx.cookies.set(name, value, config) @@ -130,23 +116,6 @@ exports.isClient = ctx => { return ctx.headers[Headers.TYPE] === "client" } -/** - * Given an email address this will use a view to search through - * all the users to find one with this email address. - * @param {string} email the email to lookup the user by. - * @return {Promise} - */ -exports.getGlobalUserByEmail = async email => { - if (email == null) { - throw "Must supply an email address to view" - } - - return queryGlobalView(ViewNames.USER_BY_EMAIL, { - key: email.toLowerCase(), - include_docs: true, - }) -} - exports.getBuildersCount = async () => { const builders = await queryGlobalView(ViewNames.USER_BY_BUILDERS, { include_docs: false, @@ -154,96 +123,6 @@ exports.getBuildersCount = async () => { return builders.length } -exports.saveUser = async ( - user, - tenantId, - hashPassword = true, - requirePassword = true -) => { - if (!tenantId) { - throw "No tenancy specified." - } - // need to set the context for this request, as specified - updateTenantId(tenantId) - // specify the tenancy incase we're making a new admin user (public) - const db = getGlobalDB(tenantId) - let { email, password, _id } = user - // make sure another user isn't using the same email - let dbUser - if (email) { - // check budibase users inside the tenant - dbUser = await exports.getGlobalUserByEmail(email) - if (dbUser != null && (dbUser._id !== _id || Array.isArray(dbUser))) { - throw `Email address ${email} already in use.` - } - - // check budibase users in other tenants - if (env.MULTI_TENANCY) { - const tenantUser = await getTenantUser(email) - if (tenantUser != null && tenantUser.tenantId !== tenantId) { - throw `Email address ${email} already in use.` - } - } - - // check root account users in account portal - if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { - const account = await accounts.getAccount(email) - if (account && account.verified && account.tenantId !== tenantId) { - throw `Email address ${email} already in use.` - } - } - } else { - dbUser = await db.get(_id) - } - - // get the password, make sure one is defined - let hashedPassword - if (password) { - hashedPassword = hashPassword ? await hash(password) : password - } else if (dbUser) { - hashedPassword = dbUser.password - } else if (requirePassword) { - throw "Password must be specified." - } - - _id = _id || generateGlobalUserID() - user = { - createdAt: Date.now(), - ...dbUser, - ...user, - _id, - password: hashedPassword, - tenantId, - } - // make sure the roles object is always present - if (!user.roles) { - user.roles = {} - } - // add the active status to a user if its not provided - if (user.status == null) { - user.status = UserStatus.ACTIVE - } - try { - const response = await db.put({ - password: hashedPassword, - ...user, - }) - await tryAddTenant(tenantId, _id, email) - await userCache.invalidateUser(response.id) - return { - _id: response.id, - _rev: response.rev, - email, - } - } catch (err) { - if (err.status === 409) { - throw "User exists already" - } else { - throw err - } - } -} - /** * Logs a user out from budibase. Re-used across account portal and builder. */ diff --git a/packages/server/src/api/controllers/query/import/index.ts b/packages/server/src/api/controllers/query/import/index.ts index f2cf764bf5..745b9ea068 100644 --- a/packages/server/src/api/controllers/query/import/index.ts +++ b/packages/server/src/api/controllers/query/import/index.ts @@ -84,7 +84,7 @@ export class RestImporter { const count = successQueries.length const importSource = this.source.getImportSource() const datasource = await db.get(datasourceId) - events.query.import({ datasource, importSource, count }) + events.query.import(datasource, importSource, count) for (let query of successQueries) { events.query.created(query) } diff --git a/packages/server/src/api/controllers/query/import/tests/index.spec.js b/packages/server/src/api/controllers/query/import/tests/index.spec.js index ad4dd3baed..40818ce945 100644 --- a/packages/server/src/api/controllers/query/import/tests/index.spec.js +++ b/packages/server/src/api/controllers/query/import/tests/index.spec.js @@ -109,8 +109,7 @@ describe("Rest Importer", () => { expect(importResult.errorQueries.length).toBe(0) expect(importResult.queries.length).toBe(assertions[key].count) expect(events.query.import).toBeCalledTimes(1) - const eventData = { datasource, importSource: assertions[key].source, count: assertions[key].count} - expect(events.query.import).toBeCalledWith(eventData) + expect(events.query.import).toBeCalledWith(datasource, assertions[key].source, assertions[key].count) jest.clearAllMocks() } diff --git a/packages/server/src/api/controllers/query/index.ts b/packages/server/src/api/controllers/query/index.ts index 4ee5c38fbf..a9bbdcf53c 100644 --- a/packages/server/src/api/controllers/query/index.ts +++ b/packages/server/src/api/controllers/query/index.ts @@ -216,9 +216,12 @@ const removeDynamicVariables = async (queryId: any) => { export async function destroy(ctx: any) { const db = getAppDB() - await removeDynamicVariables(ctx.params.queryId) + const queryId = ctx.params.queryId + await removeDynamicVariables(queryId) + const query = await db.get(queryId) + const datasource = await db.get(query.datasourceId) await db.remove(ctx.params.queryId, ctx.params.revId) ctx.message = `Query deleted.` ctx.status = 200 - events.query.deleted() + events.query.deleted(datasource, query) } diff --git a/packages/server/src/api/controllers/role.js b/packages/server/src/api/controllers/role.js index 11b4b9a520..8f28847266 100644 --- a/packages/server/src/api/controllers/role.js +++ b/packages/server/src/api/controllers/role.js @@ -10,6 +10,7 @@ const { InternalTables, } = require("../../db/utils") const { getAppDB } = require("@budibase/backend-core/context") +const { events } = require("@budibase/backend-core") const UpdateRolesOptions = { CREATED: "created", @@ -50,8 +51,10 @@ exports.find = async function (ctx) { exports.save = async function (ctx) { const db = getAppDB() let { _id, name, inherits, permissionId } = ctx.request.body + let isCreate = false if (!_id) { _id = generateRoleID() + isCreate = true } else if (isBuiltin(_id)) { ctx.throw(400, "Cannot update builtin roles.") } @@ -62,6 +65,11 @@ exports.save = async function (ctx) { role._rev = ctx.request.body._rev } const result = await db.put(role) + if (isCreate) { + events.role.created(role) + } else { + events.role.updated(role) + } await updateRolesOnUserTable(db, _id, UpdateRolesOptions.CREATED) role._rev = result.rev ctx.body = role @@ -71,6 +79,7 @@ exports.save = async function (ctx) { exports.destroy = async function (ctx) { const db = getAppDB() const roleId = ctx.params.roleId + const role = await db.get(roleId) if (isBuiltin(roleId)) { ctx.throw(400, "Cannot delete builtin role.") } @@ -88,6 +97,7 @@ exports.destroy = async function (ctx) { } await db.remove(roleId, ctx.params.rev) + events.role.deleted(role) await updateRolesOnUserTable( db, ctx.params.roleId, diff --git a/packages/server/src/api/routes/tests/query.spec.js b/packages/server/src/api/routes/tests/query.spec.js index 40d6890904..15c70f982f 100644 --- a/packages/server/src/api/routes/tests/query.spec.js +++ b/packages/server/src/api/routes/tests/query.spec.js @@ -189,6 +189,7 @@ describe("/queries", () => { expect(res.body).toEqual([]) expect(events.query.deleted).toBeCalledTimes(1) + expect(events.query.deleted).toBeCalledWith(datasource, query) }) it("should apply authorization to endpoint", async () => { diff --git a/packages/server/src/api/routes/tests/role.spec.js b/packages/server/src/api/routes/tests/role.spec.js index 31fd9997d8..9f44cbd136 100644 --- a/packages/server/src/api/routes/tests/role.spec.js +++ b/packages/server/src/api/routes/tests/role.spec.js @@ -4,6 +4,7 @@ const { } = require("@budibase/backend-core/permissions") const setup = require("./utilities") const { basicRole } = setup.structures +const { events } = require("@budibase/backend-core") describe("/roles", () => { let request = setup.getRequest() @@ -15,20 +16,48 @@ describe("/roles", () => { await config.init() }) + const createRole = async (role) => { + if (!role) { + role = basicRole() + } + + return request + .post(`/api/roles`) + .send(role) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + } + describe("create", () => { it("returns a success message when role is successfully created", async () => { - const res = await request - .post(`/api/roles`) - .send(basicRole()) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) + const res = await createRole() expect(res.res.statusMessage).toEqual( "Role 'NewRole' created successfully." ) expect(res.body._id).toBeDefined() expect(res.body._rev).toBeDefined() + expect(events.role.updated).not.toBeCalled() + expect(events.role.created).toBeCalledTimes(1) + expect(events.role.created).toBeCalledWith(res.body) + }) + }) + + describe("update", () => { + it("updates a role", async () => { + let res = await createRole() + jest.clearAllMocks() + res = await createRole(res.body) + + expect(res.res.statusMessage).toEqual( + "Role 'NewRole' created successfully." + ) + expect(res.body._id).toBeDefined() + expect(res.body._rev).toBeDefined() + expect(events.role.created).not.toBeCalled() + expect(events.role.updated).toBeCalledTimes(1) + expect(events.role.updated).toBeCalledWith(res.body) }) }) @@ -80,8 +109,10 @@ describe("/roles", () => { it("should delete custom roles", async () => { const customRole = await config.createRole({ name: "user", - permissionId: BUILTIN_PERMISSION_IDS.READ_ONLY + permissionId: BUILTIN_PERMISSION_IDS.READ_ONLY, + inherits: BUILTIN_ROLE_IDS.BASIC, }) + delete customRole._rev_tree await request .delete(`/api/roles/${customRole._id}/${customRole._rev}`) .set(config.defaultHeaders()) @@ -90,6 +121,8 @@ describe("/roles", () => { .get(`/api/roles/${customRole._id}`) .set(config.defaultHeaders()) .expect(404) + expect(events.role.deleted).toBeCalledTimes(1) + expect(events.role.deleted).toBeCalledWith(customRole) }) }) }) diff --git a/packages/worker/src/api/controllers/global/auth.ts b/packages/worker/src/api/controllers/global/auth.ts index 5e6413bece..bbd35f96d0 100644 --- a/packages/worker/src/api/controllers/global/auth.ts +++ b/packages/worker/src/api/controllers/global/auth.ts @@ -4,14 +4,7 @@ const { google } = require("@budibase/backend-core/middleware") const { oidc } = require("@budibase/backend-core/middleware") const { Configs, EmailTemplatePurpose } = require("../../../constants") const { sendEmail, isEmailConfigured } = require("../../../utilities/email") -const { - setCookie, - getCookie, - clearCookie, - getGlobalUserByEmail, - hash, - platformLogout, -} = core.utils +const { setCookie, getCookie, clearCookie, hash, platformLogout } = core.utils const { Cookies, Headers } = core.constants const { passport } = core.auth const { checkResetPasswordCode } = require("../../../utilities/redis") @@ -21,8 +14,8 @@ const { isMultiTenant, } = require("@budibase/backend-core/tenancy") const env = require("../../../environment") -import { users } from "@budibase/pro" -const { events } = require("@budibase/backend-core") +const { events, users: usersCore } = require("@budibase/backend-core") +import { users } from "../../../sdk" const ssoCallbackUrl = async (config: any, type: any) => { // incase there is a callback URL from before @@ -112,7 +105,7 @@ export const reset = async (ctx: any) => { ) } try { - const user = await getGlobalUserByEmail(email) + const user = await usersCore.getGlobalUserByEmail(email) // only if user exists, don't error though if they don't if (user) { await sendEmail(email, EmailTemplatePurpose.PASSWORD_RECOVERY, { diff --git a/packages/worker/src/api/controllers/global/roles.js b/packages/worker/src/api/controllers/global/roles.js index eb540860be..4b620f8691 100644 --- a/packages/worker/src/api/controllers/global/roles.js +++ b/packages/worker/src/api/controllers/global/roles.js @@ -7,7 +7,7 @@ const { const { doInAppContext, getAppDB } = require("@budibase/backend-core/context") const { user: userCache } = require("@budibase/backend-core/cache") const { getGlobalDB } = require("@budibase/backend-core/tenancy") -const { allUsers } = require("../../utilities") +const { users } = require("../../../sdk") exports.fetch = async ctx => { const tenantId = ctx.user.tenantId @@ -49,10 +49,10 @@ exports.find = async ctx => { exports.removeAppRole = async ctx => { const { appId } = ctx.params const db = getGlobalDB() - const users = await allUsers(ctx) + const allUsers = await users.allUsers(ctx) const bulk = [] const cacheInvalidations = [] - for (let user of users) { + for (let user of allUsers) { if (user.roles[appId]) { cacheInvalidations.push(userCache.invalidateUser(user._id)) delete user.roles[appId] diff --git a/packages/worker/src/api/controllers/global/self.js b/packages/worker/src/api/controllers/global/self.js index 935fc4259e..fd615ba795 100644 --- a/packages/worker/src/api/controllers/global/self.js +++ b/packages/worker/src/api/controllers/global/self.js @@ -13,7 +13,7 @@ const { } = require("@budibase/backend-core/utils") const { encrypt } = require("@budibase/backend-core/encryption") const { newid } = require("@budibase/backend-core/utils") -const { getUser } = require("../../utilities") +const { users } = require("../../../sdk") const { Cookies } = require("@budibase/backend-core/constants") function newApiKey() { @@ -103,7 +103,7 @@ exports.getSelf = async ctx => { checkCurrentApp(ctx) // get the main body of the user - ctx.body = await getUser(userId) + ctx.body = await users.getUser(userId) addSessionAttributesToUser(ctx) } diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index d13b08f94c..9f5bf08155 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -1,32 +1,18 @@ -const { - getGlobalUserParams, - StaticDatabases, -} = require("@budibase/backend-core/db") -const { getGlobalUserByEmail } = require("@budibase/backend-core/utils") import { EmailTemplatePurpose } from "../../../constants" import { checkInviteCode } from "../../../utilities/redis" import { sendEmail } from "../../../utilities/email" -const { user: userCache } = require("@budibase/backend-core/cache") -const { invalidateSessions } = require("@budibase/backend-core/sessions") -const accounts = require("@budibase/backend-core/accounts") +import { users } from "../../../sdk" + const { - getGlobalDB, - getTenantId, - getTenantUser, - doesTenantExist, -} = require("@budibase/backend-core/tenancy") -const { removeUserFromInfoDB } = require("@budibase/backend-core/deprovision") -import env from "../../../environment" -import { syncUserInApps } from "../../../utilities/appService" -import { quotas, users } from "@budibase/pro" -const { errors } = require("@budibase/backend-core") -import { allUsers, getUser } from "../../utilities" + errors, + users: usersCore, + tenancy, + db: dbUtils, +} = require("@budibase/backend-core") export const save = async (ctx: any) => { try { - const user: any = await users.save(ctx.request.body, getTenantId()) - // let server know to sync user - await syncUserInApps(user._id) + const user = await users.save(ctx.request.body) ctx.body = user } catch (err: any) { ctx.throw(err.status || 400, err) @@ -45,36 +31,19 @@ export const adminUser = async (ctx: any) => { // account portal sends no password for SSO users const requirePassword = parseBooleanParam(ctx.request.query.requirePassword) - if (await doesTenantExist(tenantId)) { + if (await tenancy.doesTenantExist(tenantId)) { ctx.throw(403, "Organisation already exists.") } - const db = getGlobalDB(tenantId) + const db = tenancy.getGlobalDB(tenantId) const response = await db.allDocs( - getGlobalUserParams(null, { + dbUtils.getGlobalUserParams(null, { include_docs: true, }) ) - // write usage quotas for cloud - if (!env.SELF_HOSTED) { - // could be a scenario where it exists, make sure its clean - try { - const usageQuota = await db.get(StaticDatabases.GLOBAL.docs.usageQuota) - if (usageQuota) { - await db.remove(usageQuota._id, usageQuota._rev) - } - } catch (err) { - // don't worry about errors - } - await db.put(quotas.generateNewQuotaUsage()) - } - if (response.rows.some((row: any) => row.doc.admin)) { - ctx.throw( - 403, - "You cannot initialise once an global user has been created." - ) + ctx.throw(403, "You cannot initialise once a global user has been created.") } const user = { @@ -91,44 +60,25 @@ export const adminUser = async (ctx: any) => { tenantId, } try { - ctx.body = await users.save(user, tenantId, hashPassword, requirePassword) + ctx.body = await tenancy.doInTenant(tenantId, async () => { + return users.save(user, hashPassword, requirePassword) + }) } catch (err: any) { ctx.throw(err.status || 400, err) } } export const destroy = async (ctx: any) => { - const db = getGlobalDB() - const dbUser = await db.get(ctx.params.id) - - if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { - // root account holder can't be deleted from inside budibase - const email = dbUser.email - const account = await accounts.getAccount(email) - if (account) { - if (email === ctx.user.email) { - ctx.throw(400, 'Please visit "Account" to delete this user') - } else { - ctx.throw(400, "Account holder cannot be deleted") - } - } - } - - await removeUserFromInfoDB(dbUser) - await db.remove(dbUser._id, dbUser._rev) - await quotas.removeUser(dbUser) - await userCache.invalidateUser(dbUser._id) - await invalidateSessions(dbUser._id) - // let server know to sync user - await syncUserInApps(dbUser._id) + const id = ctx.params.id + await users.destroy(id, ctx.user) ctx.body = { - message: `User ${ctx.params.id} deleted.`, + message: `User ${id} deleted.`, } } // called internally by app server user fetch export const fetch = async (ctx: any) => { - const all = await allUsers() + const all = await users.allUsers() // user hashed password shouldn't ever be returned for (let user of all) { if (user) { @@ -140,12 +90,12 @@ export const fetch = async (ctx: any) => { // called internally by app server user find export const find = async (ctx: any) => { - ctx.body = await getUser(ctx.params.id) + ctx.body = await users.getUser(ctx.params.id) } export const tenantUserLookup = async (ctx: any) => { const id = ctx.params.id - const user = await getTenantUser(id) + const user = await tenancy.getTenantUser(id) if (user) { ctx.body = user } else { @@ -155,14 +105,14 @@ export const tenantUserLookup = async (ctx: any) => { export const invite = async (ctx: any) => { let { email, userInfo } = ctx.request.body - const existing = await getGlobalUserByEmail(email) + const existing = await usersCore.getGlobalUserByEmail(email) if (existing) { ctx.throw(400, "Email address already in use.") } if (!userInfo) { userInfo = {} } - userInfo.tenantId = getTenantId() + userInfo.tenantId = tenancy.getTenantId() const opts: any = { subject: "{{ company }} platform invitation", info: userInfo, @@ -178,16 +128,15 @@ export const inviteAccept = async (ctx: any) => { try { // info is an extension of the user object that was stored by global const { email, info }: any = await checkInviteCode(inviteCode) - ctx.body = await users.save( - { + ctx.body = await tenancy.doInTenant(info.tenantId, () => { + return users.save({ firstName, lastName, password, email, ...info, - }, - info.tenantId - ) + }) + }) } catch (err: any) { if (err.code === errors.codes.USAGE_LIMIT_EXCEEDED) { // explicitly re-throw limit exceeded errors diff --git a/packages/worker/src/api/routes/global/self.js b/packages/worker/src/api/routes/global/self.js index eae16857a8..e1af7c2146 100644 --- a/packages/worker/src/api/routes/global/self.js +++ b/packages/worker/src/api/routes/global/self.js @@ -1,7 +1,7 @@ const Router = require("@koa/router") const controller = require("../../controllers/global/self") const builderOnly = require("../../../middleware/builderOnly") -const { buildUserSaveValidation } = require("../../utilities/validation") +const { users } = require("../validation") const router = Router() @@ -11,7 +11,7 @@ router .get("/api/global/self", controller.getSelf) .post( "/api/global/self", - buildUserSaveValidation(true), + users.buildUserSaveValidation(true), controller.updateSelf ) diff --git a/packages/worker/src/api/routes/global/users.js b/packages/worker/src/api/routes/global/users.js index 5d28d18eb7..3b5421e91f 100644 --- a/packages/worker/src/api/routes/global/users.js +++ b/packages/worker/src/api/routes/global/users.js @@ -4,7 +4,7 @@ const joiValidator = require("../../../middleware/joi-validator") const adminOnly = require("../../../middleware/adminOnly") const Joi = require("joi") const cloudRestricted = require("../../../middleware/cloudRestricted") -const { buildUserSaveValidation } = require("../../utilities/validation") +const { users } = require("../validation") const selfController = require("../../controllers/global/self") const router = Router() @@ -41,7 +41,7 @@ router .post( "/api/global/users", adminOnly, - buildUserSaveValidation(), + users.buildUserSaveValidation(), controller.save ) .get("/api/global/users", adminOnly, controller.fetch) @@ -72,7 +72,7 @@ router .get("/api/global/users/self", selfController.getSelf) .post( "/api/global/users/self", - buildUserSaveValidation(true), + users.buildUserSaveValidation(true), selfController.updateSelf ) diff --git a/packages/worker/src/api/routes/tests/users.spec.js b/packages/worker/src/api/routes/tests/users.spec.js index be9807c5d4..4d94aa0148 100644 --- a/packages/worker/src/api/routes/tests/users.spec.js +++ b/packages/worker/src/api/routes/tests/users.spec.js @@ -1,6 +1,7 @@ jest.mock("nodemailer") -const { config, request, mocks } = require("../../../tests") +const { config, request, mocks, structures } = require("../../../tests") const sendMailMock = mocks.email.mock() +const { events } = require("@budibase/backend-core") describe("/api/global/users", () => { let code @@ -48,4 +49,258 @@ describe("/api/global/users", () => { expect(user).toBeDefined() expect(user._id).toEqual(res.body._id) }) + + const createUser = async (user) => { + const existing = await config.getUser(user.email) + if (existing) { + await deleteUser(existing._id) + } + return saveUser(user) + } + + const updateUser = async (user) => { + const existing = await config.getUser(user.email) + user._id = existing._id + return saveUser(user) + } + + const saveUser = async (user) => { + const res = await request + .post(`/api/global/users`) + .send(user) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + // .expect(200) + return res.body + } + + const deleteUser = async (email) => { + const user = await config.getUser(email) + if (user) { + await request + .delete(`/api/global/users/${user._id}`) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + } + } + + describe("create", () => { + it("should be able to create a basic user", async () => { + jest.clearAllMocks() + const user = structures.users.user({ email: "basic@test.com" }) + await createUser(user) + + expect(events.user.created).toBeCalledTimes(1) + expect(events.user.updated).not.toBeCalled() + expect(events.user.permissionBuilderAssigned).not.toBeCalled() + expect(events.user.permissionAdminAssigned).not.toBeCalled() + }) + + it("should be able to create an admin user", async () => { + jest.clearAllMocks() + const user = structures.users.adminUser({ email: "admin@test.com" }) + await createUser(user) + + expect(events.user.created).toBeCalledTimes(1) + expect(events.user.updated).not.toBeCalled() + expect(events.user.permissionBuilderAssigned).not.toBeCalled() + expect(events.user.permissionAdminAssigned).toBeCalledTimes(1) + }) + + it("should be able to create a builder user", async () => { + jest.clearAllMocks() + const user = structures.users.builderUser({ email: "builder@test.com" }) + await createUser(user) + + expect(events.user.created).toBeCalledTimes(1) + expect(events.user.updated).not.toBeCalled() + expect(events.user.permissionBuilderAssigned).toBeCalledTimes(1) + expect(events.user.permissionAdminAssigned).not.toBeCalled() + }) + + it("should be able to assign app roles", async () => { + jest.clearAllMocks() + const user = structures.users.user({ email: "assign-roles@test.com" }) + user.roles = { + "app_123": "role1", + "app_456": "role2", + } + + await createUser(user) + + expect(events.user.created).toBeCalledTimes(1) + expect(events.user.updated).not.toBeCalled() + expect(events.role.assigned).toBeCalledTimes(2) + expect(events.role.assigned).toBeCalledWith("role1") + expect(events.role.assigned).toBeCalledWith("role2") + }) + }) + + describe("update", () => { + it("should be able to update a basic user", async () => { + let user = structures.users.user({ email: "basic-update@test.com" }) + await createUser(user) + jest.clearAllMocks() + + await updateUser(user) + + expect(events.user.created).not.toBeCalled() + expect(events.user.updated).toBeCalledTimes(1) + expect(events.user.permissionBuilderAssigned).not.toBeCalled() + expect(events.user.permissionAdminAssigned).not.toBeCalled() + }) + + it("should be able to update a basic user to an admin user", async () => { + let user = structures.users.user({ email: "basic-update-admin@test.com" }) + await createUser(user) + jest.clearAllMocks() + + await updateUser(structures.users.adminUser(user)) + + expect(events.user.created).not.toBeCalled() + expect(events.user.updated).toBeCalledTimes(1) + expect(events.user.permissionBuilderAssigned).not.toBeCalled() + expect(events.user.permissionAdminAssigned).toBeCalledTimes(1) + }) + + it("should be able to update a basic user to a builder user", async () => { + let user = structures.users.user({ email: "basic-update-builder@test.com" }) + await createUser(user) + jest.clearAllMocks() + + await updateUser(structures.users.builderUser(user)) + + expect(events.user.created).not.toBeCalled() + expect(events.user.updated).toBeCalledTimes(1) + expect(events.user.permissionBuilderAssigned).toBeCalledTimes(1) + expect(events.user.permissionAdminAssigned).not.toBeCalled() + }) + + it("should be able to update an admin user to a basic user", async () => { + let user = structures.users.adminUser({ email: "admin-update-basic@test.com" }) + await createUser(user) + jest.clearAllMocks() + + user.admin.global = false + await updateUser(user) + + expect(events.user.created).not.toBeCalled() + expect(events.user.updated).toBeCalledTimes(1) + expect(events.user.permissionAdminRemoved).toBeCalledTimes(1) + expect(events.user.permissionBuilderRemoved).not.toBeCalled() + }) + + it("should be able to update an builder user to a basic user", async () => { + let user = structures.users.builderUser({ email: "builder-update-basic@test.com" }) + await createUser(user) + jest.clearAllMocks() + + user.builder.global = false + await updateUser(user) + + expect(events.user.created).not.toBeCalled() + expect(events.user.updated).toBeCalledTimes(1) + expect(events.user.permissionBuilderRemoved).toBeCalledTimes(1) + expect(events.user.permissionAdminRemoved).not.toBeCalled() + }) + + it("should be able to assign app roles", async () => { + const user = structures.users.user({ email: "assign-roles-update@test.com" }) + await createUser(user) + jest.clearAllMocks() + + user.roles = { + "app_123": "role1", + "app_456": "role2", + } + await updateUser(user) + + expect(events.user.created).not.toBeCalled() + expect(events.user.updated).toBeCalledTimes(1) + expect(events.role.assigned).toBeCalledTimes(2) + expect(events.role.assigned).toBeCalledWith("role1") + expect(events.role.assigned).toBeCalledWith("role2") + }) + + it("should be able to unassign app roles", async () => { + const user = structures.users.user({ email: "unassign-roles@test.com" }) + user.roles = { + "app_123": "role1", + "app_456": "role2", + } + await createUser(user) + jest.clearAllMocks() + + user.roles = {} + await updateUser(user) + + expect(events.user.created).not.toBeCalled() + expect(events.user.updated).toBeCalledTimes(1) + expect(events.role.unassigned).toBeCalledTimes(2) + expect(events.role.unassigned).toBeCalledWith("role1") + expect(events.role.unassigned).toBeCalledWith("role2") + }) + + it("should be able to update existing app roles", async () => { + const user = structures.users.user({ email: "update-roles@test.com" }) + user.roles = { + "app_123": "role1", + "app_456": "role2", + } + await createUser(user) + jest.clearAllMocks() + + user.roles = { + "app_123": "role1", + "app_456": "role2-edit", + } + await updateUser(user) + + expect(events.user.created).not.toBeCalled() + expect(events.user.updated).toBeCalledTimes(1) + expect(events.role.unassigned).toBeCalledTimes(1) + expect(events.role.unassigned).toBeCalledWith("role2") + expect(events.role.assigned).toBeCalledTimes(1) + expect(events.role.assigned).toBeCalledWith("role2-edit") + }) + }) + + describe("destroy", () => { + it("should be able to destroy a basic user", async () => { + let user = structures.users.user({ email: "destroy@test.com" }) + await createUser(user) + jest.clearAllMocks() + + await deleteUser(user.email) + + expect(events.user.deleted).toBeCalledTimes(1) + expect(events.user.permissionBuilderRemoved).not.toBeCalled() + expect(events.user.permissionAdminRemoved).not.toBeCalled() + }) + + it("should be able to destroy an admin user", async () => { + let user = structures.users.adminUser({ email: "destroy-admin@test.com" }) + await createUser(user) + jest.clearAllMocks() + + await deleteUser(user.email) + + expect(events.user.deleted).toBeCalledTimes(1) + expect(events.user.permissionBuilderRemoved).not.toBeCalled() + expect(events.user.permissionAdminRemoved).toBeCalledTimes(1) + }) + + it("should be able to destroy a builder user", async () => { + let user = structures.users.builderUser({ email: "destroy-admin@test.com" }) + await createUser(user) + jest.clearAllMocks() + + await deleteUser(user.email) + + expect(events.user.deleted).toBeCalledTimes(1) + expect(events.user.permissionBuilderRemoved).toBeCalledTimes(1) + expect(events.user.permissionAdminRemoved).not.toBeCalled() + }) + }) }) \ No newline at end of file diff --git a/packages/worker/src/api/routes/validation/index.ts b/packages/worker/src/api/routes/validation/index.ts new file mode 100644 index 0000000000..1b1c47b924 --- /dev/null +++ b/packages/worker/src/api/routes/validation/index.ts @@ -0,0 +1 @@ +export * as users from "./users" diff --git a/packages/worker/src/api/utilities/validation.js b/packages/worker/src/api/routes/validation/users.ts similarity index 80% rename from packages/worker/src/api/utilities/validation.js rename to packages/worker/src/api/routes/validation/users.ts index d1b36a9206..81372358ff 100644 --- a/packages/worker/src/api/utilities/validation.js +++ b/packages/worker/src/api/routes/validation/users.ts @@ -1,8 +1,8 @@ -const joiValidator = require("../../middleware/joi-validator") -const Joi = require("joi") +import joiValidator from "../../../middleware/joi-validator" +import Joi from "joi" -exports.buildUserSaveValidation = (isSelf = false) => { - let schema = { +export const buildUserSaveValidation = (isSelf = false) => { + let schema: any = { email: Joi.string().allow(null, ""), password: Joi.string().allow(null, ""), forceResetPassword: Joi.boolean().optional(), diff --git a/packages/worker/src/api/utilities/index.js b/packages/worker/src/api/utilities/index.js deleted file mode 100644 index 2b3605481d..0000000000 --- a/packages/worker/src/api/utilities/index.js +++ /dev/null @@ -1,33 +0,0 @@ -const { getGlobalDB } = require("@budibase/backend-core/tenancy") -const { getGlobalUserParams } = require("@budibase/backend-core/db") - -/** - * Retrieves all users from the current tenancy. - */ -exports.allUsers = async () => { - const db = getGlobalDB() - const response = await db.allDocs( - getGlobalUserParams(null, { - include_docs: true, - }) - ) - return response.rows.map(row => row.doc) -} - -/** - * Gets a user by ID from the global database, based on the current tenancy. - */ -exports.getUser = async userId => { - const db = getGlobalDB() - let user - try { - user = await db.get(userId) - } catch (err) { - // no user found, just return nothing - user = {} - } - if (user) { - delete user.password - } - return user -} diff --git a/packages/worker/src/sdk/index.ts b/packages/worker/src/sdk/index.ts new file mode 100644 index 0000000000..1b1c47b924 --- /dev/null +++ b/packages/worker/src/sdk/index.ts @@ -0,0 +1 @@ +export * as users from "./users" diff --git a/packages/worker/src/sdk/users/events.ts b/packages/worker/src/sdk/users/events.ts new file mode 100644 index 0000000000..adebfbff46 --- /dev/null +++ b/packages/worker/src/sdk/users/events.ts @@ -0,0 +1,136 @@ +const { events } = require("@budibase/backend-core") + +export const handleDeleteEvents = (user: any) => { + events.user.deleted(user) + + if (isBuilder(user)) { + events.user.permissionBuilderRemoved(user) + } + + if (isAdmin(user)) { + events.user.permissionAdminRemoved(user) + } +} + +const assignAppRoleEvents = (roles: any, existingRoles: any) => { + for (const [appId, role] of Object.entries(roles)) { + // app role in existing is not same as new + if (!existingRoles || existingRoles[appId] !== role) { + events.role.assigned(role) + } + } +} + +const unassignAppRoleEvents = (roles: any, existingRoles: any) => { + if (!existingRoles) { + return + } + for (const [appId, role] of Object.entries(existingRoles)) { + // app role in new is not same as existing + if (!roles || roles[appId] !== role) { + events.role.unassigned(role) + } + } +} + +const handleAppRoleEvents = (user: any, existingUser: any) => { + const roles = user.roles + const existingRoles = existingUser?.roles + + assignAppRoleEvents(roles, existingRoles) + unassignAppRoleEvents(roles, existingRoles) +} + +export const handleSaveEvents = (user: any, existingUser: any) => { + if (existingUser) { + events.user.updated(user) + + if (isRemovingBuilder(user, existingUser)) { + events.user.permissionBuilderRemoved(user) + } + + if (isRemovingAdmin(user, existingUser)) { + events.user.permissionAdminRemoved(user) + } + } else { + events.user.created(user) + } + + if (isAddingBuilder(user, existingUser)) { + events.user.permissionBuilderAssigned(user) + } + + if (isAddingAdmin(user, existingUser)) { + events.user.permissionAdminAssigned(user) + } + + handleAppRoleEvents(user, existingUser) +} + +const isBuilder = (user: any) => user.builder && user.builder.global +const isAdmin = (user: any) => user.admin && user.admin.global + +export const isAddingBuilder = (user: any, existingUser: any) => { + return isAddingPermission(user, existingUser, isBuilder) +} + +export const isRemovingBuilder = (user: any, existingUser: any) => { + return isRemovingPermission(user, existingUser, isBuilder) +} + +const isAddingAdmin = (user: any, existingUser: any) => { + return isAddingPermission(user, existingUser, isAdmin) +} + +const isRemovingAdmin = (user: any, existingUser: any) => { + return isRemovingPermission(user, existingUser, isAdmin) +} + +/** + * Check if a permission is being added to a new or existing user. + */ +const isAddingPermission = ( + user: any, + existingUser: any, + hasPermission: any +) => { + // new user doesn't have the permission + if (!hasPermission(user)) { + return false + } + + // existing user has the permission + if (existingUser && hasPermission(existingUser)) { + return false + } + + // permission is being added + return true +} + +/** + * Check if a permission is being removed from an existing user. + */ +const isRemovingPermission = ( + user: any, + existingUser: any, + hasPermission: any +) => { + // new user has the permission + if (hasPermission(user)) { + return false + } + + // no existing user or existing user doesn't have the permission + if (!existingUser) { + return false + } + + // existing user doesn't have the permission + if (!hasPermission(existingUser)) { + return false + } + + // permission is being removed + return true +} diff --git a/packages/worker/src/sdk/users/index.ts b/packages/worker/src/sdk/users/index.ts new file mode 100644 index 0000000000..056d6e5675 --- /dev/null +++ b/packages/worker/src/sdk/users/index.ts @@ -0,0 +1 @@ +export * from "./users" diff --git a/packages/worker/src/sdk/users/users.ts b/packages/worker/src/sdk/users/users.ts new file mode 100644 index 0000000000..dbaee8baaa --- /dev/null +++ b/packages/worker/src/sdk/users/users.ts @@ -0,0 +1,180 @@ +import env from "../../environment" +import { quotas } from "@budibase/pro" +import * as apps from "../../utilities/appService" +const { events } = require("@budibase/backend-core") +import * as eventHelpers from "./events" + +const { + tenancy, + accounts, + utils, + db: dbUtils, + constants, + cache, + users: usersCore, + deprovisioning, + sessions, + HTTPError, +} = require("@budibase/backend-core") + +/** + * Retrieves all users from the current tenancy. + */ +export const allUsers = async () => { + const db = tenancy.getGlobalDB() + const response = await db.allDocs( + dbUtils.getGlobalUserParams(null, { + include_docs: true, + }) + ) + return response.rows.map((row: any) => row.doc) +} + +/** + * Gets a user by ID from the global database, based on the current tenancy. + */ +export const getUser = async (userId: string) => { + const db = tenancy.getGlobalDB() + let user + try { + user = await db.get(userId) + } catch (err: any) { + // no user found, just return nothing + if (err.status === 404) { + return {} + } + throw err + } + if (user) { + delete user.password + } + return user +} + +export const save = async ( + user: any, + hashPassword = true, + requirePassword = true +) => { + const tenantId = tenancy.getTenantId() + + // specify the tenancy incase we're making a new admin user (public) + const db = tenancy.getGlobalDB(tenantId) + let { email, password, _id } = user + // make sure another user isn't using the same email + let dbUser + if (email) { + // check budibase users inside the tenant + dbUser = await usersCore.getGlobalUserByEmail(email) + if (dbUser != null && (dbUser._id !== _id || Array.isArray(dbUser))) { + throw `Email address ${email} already in use.` + } + + // check budibase users in other tenants + if (env.MULTI_TENANCY) { + const tenantUser = await tenancy.getTenantUser(email) + if (tenantUser != null && tenantUser.tenantId !== tenantId) { + throw `Email address ${email} already in use.` + } + } + + // check root account users in account portal + if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { + const account = await accounts.getAccount(email) + if (account && account.verified && account.tenantId !== tenantId) { + throw `Email address ${email} already in use.` + } + } + } else if (_id) { + dbUser = await db.get(_id) + } + + // get the password, make sure one is defined + let hashedPassword + if (password) { + hashedPassword = hashPassword ? await utils.hash(password) : password + } else if (dbUser) { + hashedPassword = dbUser.password + } else if (requirePassword) { + throw "Password must be specified." + } + + if (!_id) { + _id = dbUtils.generateGlobalUserID(email) + } + + user = { + createdAt: Date.now(), + ...dbUser, + ...user, + _id, + password: hashedPassword, + tenantId, + } + // make sure the roles object is always present + if (!user.roles) { + user.roles = {} + } + // add the active status to a user if its not provided + if (user.status == null) { + user.status = constants.UserStatus.ACTIVE + } + try { + // save the user to db + let response + const putUserFn = () => { + return db.put(user) + } + if (await eventHelpers.isAddingBuilder(user, dbUser)) { + response = await quotas.addDeveloper(putUserFn) + } else { + response = await putUserFn() + } + + eventHelpers.handleSaveEvents(user, dbUser) + + await tenancy.tryAddTenant(tenantId, _id, email) + await cache.user.invalidateUser(response.id) + // let server know to sync user + await apps.syncUserInApps(user._id) + + return { + _id: response.id, + _rev: response.rev, + email, + } + } catch (err: any) { + if (err.status === 409) { + throw "User exists already" + } else { + throw err + } + } +} + +export const destroy = async (id: string, currentUser: any) => { + const db = tenancy.getGlobalDB() + const dbUser = await db.get(id) + + if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { + // root account holder can't be deleted from inside budibase + const email = dbUser.email + const account = await accounts.getAccount(email) + if (account) { + if (email === currentUser.email) { + throw new HTTPError('Please visit "Account" to delete this user', 400) + } else { + throw new HTTPError("Account holder cannot be deleted", 400) + } + } + } + + await deprovisioning.removeUserFromInfoDB(dbUser) + await db.remove(dbUser._id, dbUser._rev) + eventHelpers.handleDeleteEvents(dbUser) + await quotas.removeUser(dbUser) + await cache.user.invalidateUser(dbUser._id) + await sessions.invalidateSessions(dbUser._id) + // let server know to sync user + await apps.syncUserInApps(dbUser._id) +} diff --git a/packages/worker/src/tests/TestConfiguration.js b/packages/worker/src/tests/TestConfiguration.js index a718f7b8b9..9c45217e3e 100644 --- a/packages/worker/src/tests/TestConfiguration.js +++ b/packages/worker/src/tests/TestConfiguration.js @@ -6,7 +6,7 @@ const supertest = require("supertest") const { jwt } = require("@budibase/backend-core/auth") const { Cookies, Headers } = require("@budibase/backend-core/constants") const { Configs } = require("../constants") -const { getGlobalUserByEmail } = require("@budibase/backend-core/utils") +const { users } = require("@budibase/backend-core") const { createASession } = require("@budibase/backend-core/sessions") const { TENANT_ID, CSRF_TOKEN } = require("./structures") const structures = require("./structures") @@ -112,20 +112,17 @@ class TestConfiguration { async getUser(email) { return doInTenant(TENANT_ID, () => { - return getGlobalUserByEmail(email) + return users.getGlobalUserByEmail(email) }) } - async createUser(email = "test@test.com", password = "test") { - const user = await this.getUser(email) + async createUser(email, password) { + const user = await this.getUser(structures.users.email) if (user) { return user } await this._req( - { - email, - password, - }, + structures.users.user({ email, password }), null, controllers.users.save ) @@ -133,11 +130,7 @@ class TestConfiguration { async saveAdminUser() { await this._req( - { - email: "testuser@test.com", - password: "test@test.com", - tenantId: TENANT_ID, - }, + structures.users.user({ tenantId: TENANT_ID }), null, controllers.users.adminUser ) diff --git a/packages/worker/src/tests/structures/index.js b/packages/worker/src/tests/structures/index.js index 0d82b67d5e..115d22e731 100644 --- a/packages/worker/src/tests/structures/index.js +++ b/packages/worker/src/tests/structures/index.js @@ -1,10 +1,12 @@ const configs = require("./configs") +const users = require("./users") const TENANT_ID = "default" const CSRF_TOKEN = "e3727778-7af0-4226-b5eb-f43cbe60a306" module.exports = { configs, + users, TENANT_ID, CSRF_TOKEN, } diff --git a/packages/worker/src/tests/structures/users.ts b/packages/worker/src/tests/structures/users.ts new file mode 100644 index 0000000000..dce771aaa7 --- /dev/null +++ b/packages/worker/src/tests/structures/users.ts @@ -0,0 +1,28 @@ +export const email = "test@test.com" + +export const user = (userProps: any) => { + return { + email: "test@test.com", + password: "test", + roles: {}, + ...userProps, + } +} + +export const adminUser = (userProps: any) => { + return { + ...user(userProps), + admin: { + global: true, + }, + } +} + +export const builderUser = (userProps: any) => { + return { + ...user(userProps), + builder: { + global: true, + }, + } +} From c316a27f26f5b9479f0768eb87defcbd8bc6f8f0 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Fri, 8 Apr 2022 10:55:39 +0100 Subject: [PATCH 21/75] screen events --- .../src/tests/utilities/mocks/events.js | 8 +++++ packages/server/src/api/controllers/screen.js | 14 +++++++- .../src/api/routes/tests/screen.spec.js | 34 ++++++++++++++++--- 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index ad7ca3fee9..14a9012d1f 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -69,6 +69,11 @@ jest.mock("../../../events", () => { assigned: jest.fn(), unassigned: jest.fn(), }, + row: {}, + screen: { + created: jest.fn(), + deleted: jest.fn(), + }, user: { created: jest.fn(), updated: jest.fn(), @@ -85,6 +90,9 @@ jest.mock("../../../events", () => { passwordResetRequested: jest.fn(), passwordReset: jest.fn(), }, + serve: {}, + table: {}, + view: {}, } }) diff --git a/packages/server/src/api/controllers/screen.js b/packages/server/src/api/controllers/screen.js index e166ab3eb8..01e6a22611 100644 --- a/packages/server/src/api/controllers/screen.js +++ b/packages/server/src/api/controllers/screen.js @@ -1,6 +1,7 @@ const { getScreenParams, generateScreenID } = require("../../db/utils") const { AccessController } = require("@budibase/backend-core/roles") const { getAppDB } = require("@budibase/backend-core/context") +const { events } = require("@budibase/backend-core") exports.fetch = async ctx => { const db = getAppDB() @@ -23,11 +24,17 @@ exports.save = async ctx => { const db = getAppDB() let screen = ctx.request.body + let eventFn if (!screen._id) { screen._id = generateScreenID() + eventFn = events.screen.created } + const response = await db.put(screen) + if (eventFn) { + eventFn(screen) + } ctx.message = `Screen ${screen.name} saved.` ctx.body = { ...screen, @@ -38,7 +45,12 @@ exports.save = async ctx => { exports.destroy = async ctx => { const db = getAppDB() - await db.remove(ctx.params.screenId, ctx.params.screenRev) + const id = ctx.params.screenId + const screen = await db.get(id) + + await db.remove(id, ctx.params.screenRev) + + events.screen.deleted(screen) ctx.body = { message: "Screen deleted successfully", } diff --git a/packages/server/src/api/routes/tests/screen.spec.js b/packages/server/src/api/routes/tests/screen.spec.js index c2d60ec006..1266724320 100644 --- a/packages/server/src/api/routes/tests/screen.spec.js +++ b/packages/server/src/api/routes/tests/screen.spec.js @@ -1,6 +1,7 @@ const { checkBuilderEndpoint } = require("./utilities/TestFunctions") const setup = require("./utilities") const { basicScreen } = setup.structures +const { events } = require("@budibase/backend-core") describe("/screens", () => { let request = setup.getRequest() @@ -35,16 +36,40 @@ describe("/screens", () => { }) describe("save", () => { - it("should be able to save a screen", async () => { - const screenCfg = basicScreen() + const saveScreen = async (screen) => { const res = await request .post(`/api/screens`) - .send(screenCfg) + .send(screen) .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) + return res + } + + it("should be able to create a screen", async () => { + jest.clearAllMocks() + + const screen = basicScreen() + const res = await saveScreen(screen) + expect(res.body._rev).toBeDefined() - expect(res.body.name).toEqual(screenCfg.name) + expect(res.body.name).toEqual(screen.name) + expect(events.screen.created).toBeCalledTimes(1) + }) + + it("should be able to update a screen", async () => { + const screen = basicScreen() + let res = await saveScreen(screen) + screen._id = res.body._id + screen._rev = res.body._rev + screen.name = "edit" + jest.clearAllMocks() + + res = await saveScreen(screen) + + expect(res.body._rev).toBeDefined() + expect(res.body.name).toEqual(screen.name) + expect(events.screen.created).not.toBeCalled() }) it("should apply authorization to endpoint", async () => { @@ -64,6 +89,7 @@ describe("/screens", () => { .expect("Content-Type", /json/) .expect(200) expect(res.body.message).toBeDefined() + expect(events.screen.deleted).toBeCalledTimes(1) }) it("should apply authorization to endpoint", async () => { From d41037a85974627551d8e42c0be70557b01d05b5 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Fri, 8 Apr 2022 14:07:11 +0100 Subject: [PATCH 22/75] builder / app / app preview served events --- packages/backend-core/src/events/constants.js | 9 +- .../src/events/handlers/screen.js | 2 - .../backend-core/src/events/handlers/serve.js | 17 +- .../src/tests/utilities/mocks/events.js | 6 +- .../src/api/controllers/static/index.js | 44 ++-- packages/server/src/api/index.js | 6 +- .../src/api/routes/tests/static.spec.js | 190 ++++++++++++------ packages/server/src/environment.js | 6 +- 8 files changed, 182 insertions(+), 98 deletions(-) diff --git a/packages/backend-core/src/events/constants.js b/packages/backend-core/src/events/constants.js index c35fc71ab8..7ea68c5a98 100644 --- a/packages/backend-core/src/events/constants.js +++ b/packages/backend-core/src/events/constants.js @@ -64,8 +64,10 @@ exports.Events = { ROLE_ASSIGNED: "role:assigned", ROLE_UNASSIGNED: "role:unassigned", - // APP / CLIENT - CLIENT_SERVED: "client:served", + // SERVE + SERVED_BUILDER: "served:builder", + SERVED_APP: "served:app", + SERVED_APP_PREVIEW: "served:app:preview", // DATASOURCE DATASOURCE_CREATED: "datasource:created", @@ -102,9 +104,6 @@ exports.Events = { // ROW // ROW_CREATED: "row:created", - // BUILDER - BUILDER_SERVED: "builder:served", - // COMPONENT COMPONENT_CREATED: "component:created", COMPONENT_DELETED: "component:deleted", diff --git a/packages/backend-core/src/events/handlers/screen.js b/packages/backend-core/src/events/handlers/screen.js index 53bc2b4fc7..0e2b4d268d 100644 --- a/packages/backend-core/src/events/handlers/screen.js +++ b/packages/backend-core/src/events/handlers/screen.js @@ -1,13 +1,11 @@ const events = require("../events") const { Events } = require("../constants") -// TODO exports.created = () => { const properties = {} events.processEvent(Events.SCREEN_CREATED, properties) } -// TODO exports.deleted = () => { const properties = {} events.processEvent(Events.SCREEN_DELETED, properties) diff --git a/packages/backend-core/src/events/handlers/serve.js b/packages/backend-core/src/events/handlers/serve.js index 29c67d00ab..169d7338c2 100644 --- a/packages/backend-core/src/events/handlers/serve.js +++ b/packages/backend-core/src/events/handlers/serve.js @@ -1,14 +1,19 @@ const events = require("../events") const { Events } = require("../constants") -// TODO -exports.builderServed = () => { +/* eslint-disable */ + +exports.servedBuilder = version => { const properties = {} - events.processEvent(Events.BUILDER_SERVED, properties) + events.processEvent(Events.SERVED_BUILDER, properties) } -// TODO -exports.clientServed = () => { +exports.servedApp = appMetadata => { const properties = {} - events.processEvent(Events.CLIENT_SERVED, properties) + events.processEvent(Events.SERVED_APP, properties) +} + +exports.servedAppPreview = appMetadata => { + const properties = {} + events.processEvent(Events.SERVED_APP_PREVIEW, properties) } diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index 14a9012d1f..0489d0d250 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -90,7 +90,11 @@ jest.mock("../../../events", () => { passwordResetRequested: jest.fn(), passwordReset: jest.fn(), }, - serve: {}, + serve: { + servedBuilder: jest.fn(), + servedApp: jest.fn(), + servedAppPreview: jest.fn(), + }, table: {}, view: {}, } diff --git a/packages/server/src/api/controllers/static/index.js b/packages/server/src/api/controllers/static/index.js index 82e66ab545..73790db3ac 100644 --- a/packages/server/src/api/controllers/static/index.js +++ b/packages/server/src/api/controllers/static/index.js @@ -15,10 +15,12 @@ const env = require("../../../environment") const { clientLibraryPath } = require("../../../utilities") const { upload } = require("../../../utilities/fileSystem") const { attachmentsRelativeURL } = require("../../../utilities") -const { DocumentTypes } = require("../../../db/utils") +const { DocumentTypes, isDevAppID } = require("../../../db/utils") const { getAppDB, updateAppId } = require("@budibase/backend-core/context") const AWS = require("aws-sdk") const AWS_REGION = env.AWS_REGION ? env.AWS_REGION : "eu-west-1" +const { events } = require("@budibase/backend-core") +const version = require("../../../../package.json").version async function prepareUpload({ s3Key, bucket, metadata, file }) { const response = await upload({ @@ -57,6 +59,7 @@ async function getAppIdFromUrl(ctx) { exports.serveBuilder = async function (ctx) { let builderPath = resolve(TOP_LEVEL_PATH, "builder") await send(ctx, ctx.file, { root: builderPath }) + events.serve.servedBuilder(version) } exports.uploadFile = async function (ctx) { @@ -82,24 +85,35 @@ exports.uploadFile = async function (ctx) { exports.serveApp = async function (ctx) { let appId = await getAppIdFromUrl(ctx) - const App = require("./templates/BudibaseApp.svelte").default const db = getAppDB({ skip_setup: true }) const appInfo = await db.get(DocumentTypes.APP_METADATA) - const { head, html, css } = App.render({ - title: appInfo.name, - production: env.isProd(), - appId, - clientLibPath: clientLibraryPath(appId, appInfo.version), - }) + if (!env.isJest()) { + const App = require("./templates/BudibaseApp.svelte").default + const { head, html, css } = App.render({ + title: appInfo.name, + production: env.isProd(), + appId, + clientLibPath: clientLibraryPath(appId, appInfo.version), + }) - const appHbs = loadHandlebarsFile(`${__dirname}/templates/app.hbs`) - ctx.body = await processString(appHbs, { - head, - body: html, - style: css.code, - appId, - }) + const appHbs = loadHandlebarsFile(`${__dirname}/templates/app.hbs`) + ctx.body = await processString(appHbs, { + head, + body: html, + style: css.code, + appId, + }) + } else { + // just return the app info for jest to assert on + ctx.body = appInfo + } + + if (isDevAppID(appInfo.appId)) { + events.serve.servedAppPreview(appInfo) + } else { + events.serve.servedApp(appInfo) + } } exports.serveClientLibrary = async function (ctx) { diff --git a/packages/server/src/api/index.js b/packages/server/src/api/index.js index bcb111db50..582bb1eef9 100644 --- a/packages/server/src/api/index.js +++ b/packages/server/src/api/index.js @@ -72,10 +72,8 @@ router.use(async (ctx, next) => { validationErrors: err.validation, error, } - if (env.NODE_ENV !== "jest") { - ctx.log.error(err) - console.trace(err) - } + ctx.log.error(err) + console.trace(err) } }) diff --git a/packages/server/src/api/routes/tests/static.spec.js b/packages/server/src/api/routes/tests/static.spec.js index 2ba9a6e8c8..d0a89d1fa4 100644 --- a/packages/server/src/api/routes/tests/static.spec.js +++ b/packages/server/src/api/routes/tests/static.spec.js @@ -14,8 +14,10 @@ jest.mock("aws-sdk", () => ({ })) const setup = require("./utilities") +const { events } = require("@budibase/backend-core") +const version = require("../../../../package.json").version -describe("/attachments", () => { +describe("/static", () => { let request = setup.getRequest() let config = setup.getConfig() let app @@ -26,73 +28,133 @@ describe("/attachments", () => { app = await config.init() }) - describe("generateSignedUrls", () => { - let datasource - - beforeEach(async () => { - datasource = await config.createDatasource({ - datasource: { - type: "datasource", - name: "Test", - source: "S3", - config: {}, - }, - }) - }) - - it("should be able to generate a signed upload URL", async () => { - const bucket = "foo" - const key = "bar" + describe("/builder", () => { + it("should serve the builder", async () => { const res = await request - .post(`/api/attachments/${datasource._id}/url`) - .send({ bucket, key }) + .get("/builder/portal") .set(config.defaultHeaders()) - .expect("Content-Type", /json/) + .expect("Content-Type", /text\/html/) .expect(200) - expect(res.body.signedUrl).toEqual("my-url") - expect(res.body.publicUrl).toEqual( - `https://${bucket}.s3.eu-west-1.amazonaws.com/${key}` - ) - }) - it("should handle an invalid datasource ID", async () => { - const res = await request - .post(`/api/attachments/foo/url`) - .send({ - bucket: "foo", - key: "bar", - }) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(400) - expect(res.body.message).toEqual( - "The specified datasource could not be found" - ) - }) - - it("should require a bucket parameter", async () => { - const res = await request - .post(`/api/attachments/${datasource._id}/url`) - .send({ - bucket: undefined, - key: "bar", - }) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(400) - expect(res.body.message).toEqual("bucket and key values are required") - }) - - it("should require a key parameter", async () => { - const res = await request - .post(`/api/attachments/${datasource._id}/url`) - .send({ - bucket: "foo", - }) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(400) - expect(res.body.message).toEqual("bucket and key values are required") + expect(res.text).toContain("Budibase") + expect(events.serve.servedBuilder).toBeCalledTimes(1) + expect(events.serve.servedBuilder).toBeCalledWith(version) }) }) + + describe("/app", () => { + + beforeEach(() => { + jest.clearAllMocks() + }) + + it("should serve the app by id", async () => { + const res = await request + .get(`/${config.prodAppId}`) + .set(config.defaultHeaders()) + .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 () => { + const res = await request + .get(`${config.prodApp.url}`) + .set(config.defaultHeaders()) + .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 () => { + const res = await request + .get(`/${config.appId}`) + .set(config.defaultHeaders()) + .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() + }) + }) + + describe("/attachments", () => { + describe("generateSignedUrls", () => { + let datasource + + beforeEach(async () => { + datasource = await config.createDatasource({ + datasource: { + type: "datasource", + name: "Test", + source: "S3", + config: {}, + }, + }) + }) + + it("should be able to generate a signed upload URL", async () => { + const bucket = "foo" + const key = "bar" + const res = await request + .post(`/api/attachments/${datasource._id}/url`) + .send({ bucket, key }) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + expect(res.body.signedUrl).toEqual("my-url") + expect(res.body.publicUrl).toEqual( + `https://${bucket}.s3.eu-west-1.amazonaws.com/${key}` + ) + }) + + it("should handle an invalid datasource ID", async () => { + const res = await request + .post(`/api/attachments/foo/url`) + .send({ + bucket: "foo", + key: "bar", + }) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(400) + expect(res.body.message).toEqual( + "The specified datasource could not be found" + ) + }) + + it("should require a bucket parameter", async () => { + const res = await request + .post(`/api/attachments/${datasource._id}/url`) + .send({ + bucket: undefined, + key: "bar", + }) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(400) + expect(res.body.message).toEqual("bucket and key values are required") + }) + + it("should require a key parameter", async () => { + const res = await request + .post(`/api/attachments/${datasource._id}/url`) + .send({ + bucket: "foo", + }) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(400) + expect(res.body.message).toEqual("bucket and key values are required") + }) + }) + }) + }) diff --git a/packages/server/src/environment.js b/packages/server/src/environment.js index 62d3d70cea..60648b48b2 100644 --- a/packages/server/src/environment.js +++ b/packages/server/src/environment.js @@ -1,7 +1,10 @@ function isTest() { + return isCypress() || isJest() +} + +function isJest() { return ( process.env.NODE_ENV === "jest" || - process.env.NODE_ENV === "cypress" || (process.env.JEST_WORKER_ID != null && process.env.JEST_WORKER_ID !== "null") ) @@ -73,6 +76,7 @@ module.exports = { module.exports[key] = value }, isTest, + isJest, isCypress, isDev, isProd: () => { From 5ab9f1a9c5262af7c285f5e027216c59515bb5b1 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Mon, 11 Apr 2022 14:39:31 +0100 Subject: [PATCH 23/75] Table CRUD events + add timekeeper for tests --- packages/backend-core/package.json | 3 +- packages/backend-core/scripts/jestSetup.js | 6 + .../backend-core/src/db/tests/index.spec.js | 2 - .../backend-core/src/events/handlers/table.js | 2 - .../src/migrations/tests/index.spec.js | 2 - .../src/tests/utilities/mocks/date.js | 11 - .../src/tests/utilities/mocks/events.js | 9 +- packages/backend-core/yarn.lock | 5 + packages/client/yarn.lock | 58 +- packages/server/package.json | 1 + packages/server/scripts/jestSetup.js | 6 + .../query/import/tests/index.spec.js | 2 - .../server/src/api/controllers/table/index.js | 7 + .../src/api/controllers/table/internal.ts | 2 +- .../src/api/routes/tests/automation.spec.js | 2 - .../src/api/routes/tests/datasource.spec.js | 2 - .../server/src/api/routes/tests/query.spec.js | 2 - .../server/src/api/routes/tests/table.spec.js | 81 +- .../src/automations/tests/delay.spec.js | 4 + packages/server/yarn.lock | 5 + packages/worker/package.json | 1 + packages/worker/scripts/jestSetup.js | 6 + packages/worker/yarn.lock | 1193 ++++++++++++++++- 23 files changed, 1338 insertions(+), 74 deletions(-) diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 8639fa6dac..64c6d61bda 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -43,7 +43,8 @@ "jest": "^26.6.3", "pouchdb": "^7.2.1", "pouchdb-adapter-memory": "^7.2.2", - "pouchdb-all-dbs": "^1.0.2" + "pouchdb-all-dbs": "^1.0.2", + "timekeeper": "^2.2.0" }, "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc" } diff --git a/packages/backend-core/scripts/jestSetup.js b/packages/backend-core/scripts/jestSetup.js index 93dbf3fd5a..fcb00e64e3 100644 --- a/packages/backend-core/scripts/jestSetup.js +++ b/packages/backend-core/scripts/jestSetup.js @@ -1,4 +1,10 @@ const env = require("../src/environment") +const { mocks } = require("../testUtils") + +// mock all dates to 2020-01-01T00:00:00.000Z +// use tk.reset() to use real dates in individual tests +const tk = require("timekeeper") +tk.freeze(mocks.date.MOCK_DATE) env._set("SELF_HOSTED", "1") env._set("NODE_ENV", "jest") diff --git a/packages/backend-core/src/db/tests/index.spec.js b/packages/backend-core/src/db/tests/index.spec.js index adb651aab5..fadaa890df 100644 --- a/packages/backend-core/src/db/tests/index.spec.js +++ b/packages/backend-core/src/db/tests/index.spec.js @@ -1,6 +1,4 @@ require("../../tests/utilities/TestConfiguration") -const { mocks } = require("../../tests/utilities") -mocks.date.mock() const { getDB, allDbs } = require("../") describe("db", () => { diff --git a/packages/backend-core/src/events/handlers/table.js b/packages/backend-core/src/events/handlers/table.js index b6ddb44fd1..891701ef7d 100644 --- a/packages/backend-core/src/events/handlers/table.js +++ b/packages/backend-core/src/events/handlers/table.js @@ -6,13 +6,11 @@ exports.created = () => { events.processEvent(Events.TABLE_CREATED, properties) } -// TODO exports.updated = () => { const properties = {} events.processEvent(Events.TABLE_UPDATED, properties) } -// TODO exports.deleted = () => { const properties = {} events.processEvent(Events.TABLE_DELETED, properties) diff --git a/packages/backend-core/src/migrations/tests/index.spec.js b/packages/backend-core/src/migrations/tests/index.spec.js index eceda08db4..e5587eede1 100644 --- a/packages/backend-core/src/migrations/tests/index.spec.js +++ b/packages/backend-core/src/migrations/tests/index.spec.js @@ -1,6 +1,4 @@ require("../../tests/utilities/TestConfiguration") -const { mocks } = require("../../tests/utilities") -mocks.date.mock() const { runMigrations, getMigrationsDoc } = require("../index") const { getDB } = require("../../db") const { diff --git a/packages/backend-core/src/tests/utilities/mocks/date.js b/packages/backend-core/src/tests/utilities/mocks/date.js index 2d7cf4c698..19248c6f11 100644 --- a/packages/backend-core/src/tests/utilities/mocks/date.js +++ b/packages/backend-core/src/tests/utilities/mocks/date.js @@ -1,13 +1,2 @@ exports.MOCK_DATE = new Date("2020-01-01T00:00:00.000Z") exports.MOCK_DATE_TIMESTAMP = 1577836800000 - -exports.mock = () => { - // eslint-disable-next-line no-global-assign - Date = jest.fn(() => exports.MOCK_DATE) - Date.now = jest.fn(() => exports.MOCK_DATE_TIMESTAMP) - - return { - MOCK_DATE: exports.MOCK_DATE, - MOCK_DATE_TIMESTAMP: exports.MOCK_DATE_TIMESTAMP, - } -} diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index 0489d0d250..1176bbb3ee 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -95,7 +95,14 @@ jest.mock("../../../events", () => { servedApp: jest.fn(), servedAppPreview: jest.fn(), }, - table: {}, + table: { + created: jest.fn(), + updated: jest.fn(), + deleted: jest.fn(), + exported: jest.fn(), + imported: jest.fn(), + permissionUpdated: jest.fn(), + }, view: {}, } }) diff --git a/packages/backend-core/yarn.lock b/packages/backend-core/yarn.lock index 148d208879..2e06376084 100644 --- a/packages/backend-core/yarn.lock +++ b/packages/backend-core/yarn.lock @@ -5161,6 +5161,11 @@ through@~2.3.4: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= +timekeeper@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/timekeeper/-/timekeeper-2.2.0.tgz#9645731fce9e3280a18614a57a9d1b72af3ca368" + integrity sha512-W3AmPTJWZkRwu+iSNxPIsLZ2ByADsOLbbLxe46UJyWj3mlYLlwucKiq+/dPm0l9wTzqoF3/2PH0AGFCebjq23A== + tiny-queue@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/tiny-queue/-/tiny-queue-0.2.1.tgz#25a67f2c6e253b2ca941977b5ef7442ef97a6046" diff --git a/packages/client/yarn.lock b/packages/client/yarn.lock index 1dbd1e1d88..6e75172fa1 100644 --- a/packages/client/yarn.lock +++ b/packages/client/yarn.lock @@ -597,6 +597,16 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +htmlparser2@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + icss-replace-symbols@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" @@ -661,6 +671,11 @@ is-module@^1.0.0: resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + is-reference@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" @@ -701,6 +716,11 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +leaflet@^1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.7.1.tgz#10d684916edfe1bf41d688a3b97127c0322a2a19" + integrity sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw== + lilconfig@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082" @@ -769,6 +789,11 @@ nanoid@^3.1.30, nanoid@^3.1.32: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c" integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA== +nanoid@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.2.tgz#c89622fafb4381cd221421c69ec58547a1eec557" + integrity sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA== + node-releases@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" @@ -822,6 +847,11 @@ p-timeout@^3.2.0: dependencies: p-finally "^1.0.0" +parse-srcset@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1" + integrity sha1-8r0iH2zJcKk42IVWq8WJyqqiveE= + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -1124,6 +1154,15 @@ postcss@^8.2.10: picocolors "^1.0.0" source-map-js "^1.0.1" +postcss@^8.3.11: + version "8.4.12" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.12.tgz#1e7de78733b28970fa4743f7da6f3763648b1905" + integrity sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg== + dependencies: + nanoid "^3.3.1" + picocolors "^1.0.0" + source-map-js "^1.0.2" + promise.series@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/promise.series/-/promise.series-0.2.0.tgz#2cc7ebe959fc3a6619c04ab4dbdc9e452d864bbd" @@ -1269,6 +1308,23 @@ safe-identifier@^0.4.2: resolved "https://registry.yarnpkg.com/safe-identifier/-/safe-identifier-0.4.2.tgz#cf6bfca31c2897c588092d1750d30ef501d59fcb" integrity sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w== +sanitize-html@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.7.0.tgz#e106205b468aca932e2f9baf241f24660d34e279" + integrity sha512-jfQelabOn5voO7FAfnQF7v+jsA6z9zC/O4ec0z3E35XPEtHYJT/OdUziVWlKW4irCr2kXaQAyXTXDHWAibg1tA== + dependencies: + deepmerge "^4.2.2" + escape-string-regexp "^4.0.0" + htmlparser2 "^6.0.0" + is-plain-object "^5.0.0" + parse-srcset "^1.0.2" + postcss "^8.3.11" + +screenfull@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-6.0.1.tgz#3b71e6f06b72d817a8d3be73c45ebe71fa8da1ce" + integrity sha512-yzQW+j4zMUBQC51xxWaoDYjxOtl8Kn+xvue3p6v/fv2pIi1jH4AldgVLU8TBfFVgH2x3VXlf3+YiA/AYIPlaew== + serialize-javascript@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" @@ -1288,7 +1344,7 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -source-map-js@^1.0.1: +source-map-js@^1.0.1, source-map-js@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== diff --git a/packages/server/package.json b/packages/server/package.json index c60e490a47..72bb61c456 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -170,6 +170,7 @@ "rimraf": "^3.0.2", "supertest": "^4.0.2", "swagger-jsdoc": "^6.1.0", + "timekeeper": "^2.2.0", "ts-jest": "^27.0.3", "ts-node": "^10.0.0", "typescript": "^4.5.5", diff --git a/packages/server/scripts/jestSetup.js b/packages/server/scripts/jestSetup.js index 6999d92315..ef5ab8e9b5 100644 --- a/packages/server/scripts/jestSetup.js +++ b/packages/server/scripts/jestSetup.js @@ -1,5 +1,11 @@ const { tmpdir } = require("os") const env = require("../src/environment") +const { mocks } = require("@budibase/backend-core/testUtils") + +// mock all dates to 2020-01-01T00:00:00.000Z +// use tk.reset() to use real dates in individual tests +const tk = require("timekeeper") +tk.freeze(mocks.date.MOCK_DATE) env._set("SELF_HOSTED", "1") env._set("NODE_ENV", "jest") diff --git a/packages/server/src/api/controllers/query/import/tests/index.spec.js b/packages/server/src/api/controllers/query/import/tests/index.spec.js index 40818ce945..ae7f6e3264 100644 --- a/packages/server/src/api/controllers/query/import/tests/index.spec.js +++ b/packages/server/src/api/controllers/query/import/tests/index.spec.js @@ -3,8 +3,6 @@ const { RestImporter } = require("../index") const fs = require("fs") const path = require('path') const { events} = require("@budibase/backend-core") -const { mocks } = require("@budibase/backend-core/testUtils") -mocks.date.mock() const getData = (file) => { return fs.readFileSync(path.join(__dirname, `../sources/tests/${file}`), "utf8") diff --git a/packages/server/src/api/controllers/table/index.js b/packages/server/src/api/controllers/table/index.js index f3ece8b5cc..1f88172d0a 100644 --- a/packages/server/src/api/controllers/table/index.js +++ b/packages/server/src/api/controllers/table/index.js @@ -5,6 +5,7 @@ const { isExternalTable, isSQL } = require("../../../integrations/utils") const { getDatasourceParams } = require("../../../db/utils") const { getAppDB } = require("@budibase/backend-core/context") const { getTable, getAllInternalTables } = require("./utils") +const { events } = require("@budibase/backend-core") function pickApi({ tableId, table }) { if (table && !tableId) { @@ -56,6 +57,11 @@ exports.save = async function (ctx) { const appId = ctx.appId const table = ctx.request.body const savedTable = await pickApi({ table }).save(ctx) + if (!table._id) { + events.table.created(savedTable) + } else { + events.table.updated(savedTable) + } ctx.status = 200 ctx.message = `Table ${table.name} saved successfully.` ctx.eventEmitter && @@ -67,6 +73,7 @@ exports.destroy = async function (ctx) { const appId = ctx.appId const tableId = ctx.params.tableId const deletedTable = await pickApi({ tableId }).destroy(ctx) + events.table.deleted(deletedTable) ctx.eventEmitter && ctx.eventEmitter.emitTable(`table:delete`, appId, deletedTable) ctx.status = 200 diff --git a/packages/server/src/api/controllers/table/internal.ts b/packages/server/src/api/controllers/table/internal.ts index 887f4fed0f..0e15b648c2 100644 --- a/packages/server/src/api/controllers/table/internal.ts +++ b/packages/server/src/api/controllers/table/internal.ts @@ -11,7 +11,7 @@ const { getAppDB } = require("@budibase/backend-core/context") import { isTest } from "../../../environment" import { cleanupAttachments } from "../../../utilities/rowProcessor" import { runStaticFormulaChecks } from "./bulkFormula" -import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro" +import { quotas } from "@budibase/pro" export async function save(ctx: any) { const db = getAppDB() diff --git a/packages/server/src/api/routes/tests/automation.spec.js b/packages/server/src/api/routes/tests/automation.spec.js index aef2927889..616424d9d7 100644 --- a/packages/server/src/api/routes/tests/automation.spec.js +++ b/packages/server/src/api/routes/tests/automation.spec.js @@ -6,8 +6,6 @@ const { } = require("./utilities/TestFunctions") const setup = require("./utilities") const { basicAutomation, newAutomation, automationTrigger, automationStep } = setup.structures -const { mocks } = require("@budibase/backend-core/testUtils") -mocks.date.mock() const MAX_RETRIES = 4 const { TRIGGER_DEFINITIONS, ACTION_DEFINITIONS } = require("../../../automations") const { events } = require("@budibase/backend-core") diff --git a/packages/server/src/api/routes/tests/datasource.spec.js b/packages/server/src/api/routes/tests/datasource.spec.js index 97ee0c3c9c..bc7e6cf4af 100644 --- a/packages/server/src/api/routes/tests/datasource.spec.js +++ b/packages/server/src/api/routes/tests/datasource.spec.js @@ -6,8 +6,6 @@ let { checkBuilderEndpoint } = require("./utilities/TestFunctions") const pg = require("pg") const { checkCacheForDynamicVariable } = require("../../../threads/utils") const { events } = require("@budibase/backend-core") -const { mocks } = require("@budibase/backend-core/testUtils") -mocks.date.mock() describe("/datasources", () => { let request = setup.getRequest() diff --git a/packages/server/src/api/routes/tests/query.spec.js b/packages/server/src/api/routes/tests/query.spec.js index 15c70f982f..dd2c62e028 100644 --- a/packages/server/src/api/routes/tests/query.spec.js +++ b/packages/server/src/api/routes/tests/query.spec.js @@ -13,8 +13,6 @@ const setup = require("./utilities") const { checkBuilderEndpoint } = require("./utilities/TestFunctions") const { checkCacheForDynamicVariable } = require("../../../threads/utils") const { basicQuery, basicDatasource } = setup.structures -const { mocks } = require("@budibase/backend-core/testUtils") -mocks.date.mock() const { events } = require("@budibase/backend-core") describe("/queries", () => { diff --git a/packages/server/src/api/routes/tests/table.spec.js b/packages/server/src/api/routes/tests/table.spec.js index 794e35e040..a7d6f3037d 100644 --- a/packages/server/src/api/routes/tests/table.spec.js +++ b/packages/server/src/api/routes/tests/table.spec.js @@ -1,6 +1,7 @@ -const { checkBuilderEndpoint, getDB } = require("./utilities/TestFunctions") const setup = require("./utilities") +const { checkBuilderEndpoint, getDB } = require("./utilities/TestFunctions") const { basicTable } = setup.structures +const { events } = require("@budibase/backend-core") describe("/tables", () => { let request = setup.getRequest() @@ -28,9 +29,42 @@ describe("/tables", () => { .expect(200) expect(res.res.statusMessage).toEqual("Table TestTable saved successfully.") expect(res.body.name).toEqual("TestTable") + expect(events.table.created).toBeCalledTimes(1) + expect(events.table.created).toBeCalledWith(res.body) }) - it("renames all the row fields for a table when a schema key is renamed", async () => { + it("should apply authorization to endpoint", async () => { + await checkBuilderEndpoint({ + config, + method: "POST", + url: `/api/tables`, + body: { + name: "TestTable", + key: "name", + schema: { + name: {type: "string"} + } + } + }) + }) + }) + + describe("update", () => { + it("updates a table", async () => { + const testTable = await config.createTable() + + const res = await request + .post(`/api/tables`) + .send(testTable) + .set(config.defaultHeaders()) + .expect('Content-Type', /json/) + .expect(200) + + expect(events.table.updated).toBeCalledTimes(1) + expect(events.table.updated).toBeCalledWith(res.body) + }) + + it("updates all the row fields for a table when a schema key is renamed", async () => { const testTable = await config.createTable() const testRow = await request @@ -73,18 +107,19 @@ describe("/tables", () => { expect(res.body.name).toBeUndefined() }) - it("should apply authorization to endpoint", async () => { - await checkBuilderEndpoint({ - config, - method: "POST", - url: `/api/tables`, - body: { - name: "TestTable", - key: "name", - schema: { - name: {type: "string"} - } - } + describe("user table", () => { + it("should add roleId and email field when adjusting user table schema", async () => { + const res = await request + .post(`/api/tables`) + .send({ + ...basicTable(), + _id: "ta_users", + }) + .set(config.defaultHeaders()) + .expect('Content-Type', /json/) + .expect(200) + expect(res.body.schema.email).toBeDefined() + expect(res.body.schema.roleId).toBeDefined() }) }) }) @@ -152,22 +187,6 @@ describe("/tables", () => { }) }) - describe("updating user table", () => { - it("should add roleId and email field when adjusting user table schema", async () => { - const res = await request - .post(`/api/tables`) - .send({ - ...basicTable(), - _id: "ta_users", - }) - .set(config.defaultHeaders()) - .expect('Content-Type', /json/) - .expect(200) - expect(res.body.schema.email).toBeDefined() - expect(res.body.schema.roleId).toBeDefined() - }) - }) - describe("validate csv", () => { it("should be able to validate a CSV layout", async () => { const res = await request @@ -204,6 +223,8 @@ describe("/tables", () => { .expect('Content-Type', /json/) .expect(200) expect(res.body.message).toEqual(`Table ${testTable._id} deleted.`) + expect(events.table.deleted).toBeCalledTimes(1) + expect(events.table.deleted).toBeCalledWith(testTable) }) it("deletes linked references to the table after deletion", async () => { diff --git a/packages/server/src/automations/tests/delay.spec.js b/packages/server/src/automations/tests/delay.spec.js index 6120cf1af8..ad67c74606 100644 --- a/packages/server/src/automations/tests/delay.spec.js +++ b/packages/server/src/automations/tests/delay.spec.js @@ -1,5 +1,9 @@ const setup = require("./utilities") +// need real Date for this test +const tk = require('timekeeper'); +tk.reset() + describe("test the delay logic", () => { it("should be able to run the delay", async () => { const time = 100 diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 9861fece01..2361c5be5d 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -12105,6 +12105,11 @@ timed-out@^4.0.1: resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= +timekeeper@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/timekeeper/-/timekeeper-2.2.0.tgz#9645731fce9e3280a18614a57a9d1b72af3ca368" + integrity sha512-W3AmPTJWZkRwu+iSNxPIsLZ2ByADsOLbbLxe46UJyWj3mlYLlwucKiq+/dPm0l9wTzqoF3/2PH0AGFCebjq23A== + timm@^1.6.1: version "1.7.1" resolved "https://registry.yarnpkg.com/timm/-/timm-1.7.1.tgz#96bab60c7d45b5a10a8a4d0f0117c6b7e5aff76f" diff --git a/packages/worker/package.json b/packages/worker/package.json index a624a7c041..8fe81c3626 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -77,6 +77,7 @@ "prettier": "2.3.1", "rimraf": "^3.0.2", "supertest": "^6.1.3", + "timekeeper": "^2.2.0", "ts-jest": "^27.0.3", "ts-node": "^10.0.0", "typescript": "4.5.5", diff --git a/packages/worker/scripts/jestSetup.js b/packages/worker/scripts/jestSetup.js index 89a517279a..9cf398ca63 100644 --- a/packages/worker/scripts/jestSetup.js +++ b/packages/worker/scripts/jestSetup.js @@ -1,4 +1,10 @@ const env = require("../src/environment") +const { mocks } = require("@budibase/backend-core/testUtils") + +// mock all dates to 2020-01-01T00:00:00.000Z +// use tk.reset() to use real dates in individual tests +const tk = require("timekeeper") +tk.freeze(mocks.date.MOCK_DATE) env._set("SELF_HOSTED", "1") env._set("NODE_ENV", "jest") diff --git a/packages/worker/yarn.lock b/packages/worker/yarn.lock index 1dc1850e2a..f163f6df59 100644 --- a/packages/worker/yarn.lock +++ b/packages/worker/yarn.lock @@ -286,6 +286,67 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@budibase/backend-core@^1.0.91-alpha.17": + version "1.0.111" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.111.tgz#7af3cc89792e99ee8ce2e4c7ed4a19355db64a10" + integrity sha512-vVE6pH1BYnxt4Z+KHEfPDEaHFZPoZafE8c5Ij2Zr1y7Yk1psY1PtzWat4831624/UrdqEPpEJ+PWFleyfLVdXw== + dependencies: + "@techpass/passport-openidconnect" "^0.3.0" + aws-sdk "^2.901.0" + bcryptjs "^2.4.3" + cls-hooked "^4.2.2" + ioredis "^4.27.1" + jsonwebtoken "^8.5.1" + koa-passport "^4.1.4" + lodash "^4.17.21" + lodash.isarguments "^3.1.0" + node-fetch "^2.6.1" + passport-google-auth "^1.0.2" + passport-google-oauth "^2.0.0" + passport-jwt "^4.0.0" + passport-local "^1.0.0" + sanitize-s3-objectkey "^0.0.1" + tar-fs "^2.1.1" + uuid "^8.3.2" + zlib "^1.0.5" + +"@budibase/handlebars-helpers@^0.11.8": + version "0.11.8" + resolved "https://registry.yarnpkg.com/@budibase/handlebars-helpers/-/handlebars-helpers-0.11.8.tgz#6953d29673a8c5c407e096c0a84890465c7ce841" + integrity sha512-ggWJUt0GqsHFAEup5tlWlcrmYML57nKhpNGGLzVsqXVYN8eVmf3xluYmmMe7fDYhQH0leSprrdEXmsdFQF3HAQ== + dependencies: + array-sort "^1.0.0" + define-property "^2.0.2" + extend-shallow "^3.0.2" + for-in "^1.0.2" + get-object "^0.2.0" + get-value "^3.0.1" + handlebars "^4.7.7" + handlebars-utils "^1.0.6" + has-value "^2.0.2" + helper-md "^0.2.2" + html-tag "^2.0.0" + is-even "^1.0.0" + is-glob "^4.0.1" + kind-of "^6.0.3" + micromatch "^3.1.5" + relative "^3.0.2" + striptags "^3.1.1" + to-gfm-code-block "^0.1.1" + year "^0.2.1" + +"@budibase/string-templates@^1.0.91-alpha.17": + version "1.0.111" + resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-1.0.111.tgz#34915e38351cd77b3ef238b845fc46eda40f310c" + integrity sha512-cyfSI0ksNw/EUT/pTHxyUunPfNl1SW/yTZvvb+AOecaHC/h/dY6VJCdJCYMt7CW/q4dK2dTI65AAxrJFGqLriw== + dependencies: + "@budibase/handlebars-helpers" "^0.11.8" + dayjs "^1.10.4" + handlebars "^4.7.6" + handlebars-utils "^1.0.6" + lodash "^4.17.20" + vm2 "^3.9.4" + "@cspotcode/source-map-consumer@0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" @@ -1090,7 +1151,7 @@ acorn-walk@^7.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -acorn-walk@^8.1.1: +acorn-walk@^8.1.1, acorn-walk@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== @@ -1105,7 +1166,7 @@ acorn@^7.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.2.4, acorn@^8.4.1: +acorn@^8.2.4, acorn@^8.4.1, acorn@^8.7.0: version "8.7.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== @@ -1188,7 +1249,7 @@ arg@^4.1.0: resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== -argparse@^1.0.7: +argparse@^1.0.10, argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== @@ -1210,11 +1271,40 @@ argsarray@0.0.1: resolved "https://registry.yarnpkg.com/argsarray/-/argsarray-0.0.1.tgz#6e7207b4ecdb39b0af88303fa5ae22bda8df61cb" integrity sha1-bnIHtOzbObCviDA/pa4ivajfYcs= +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-sort@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-sort/-/array-sort-1.0.0.tgz#e4c05356453f56f53512a7d1d6123f2c54c0a88a" + integrity sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg== + dependencies: + default-compare "^1.0.0" + get-value "^2.0.6" + kind-of "^5.0.2" + array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + asap@^2.0.0: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" @@ -1232,6 +1322,11 @@ assert-plus@1.0.0, assert-plus@^1.0.0: resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + ast-types@0.9.6: version "0.9.6" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9" @@ -1242,16 +1337,42 @@ astral-regex@^1.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== +async-hook-jl@^1.7.6: + version "1.7.6" + resolved "https://registry.yarnpkg.com/async-hook-jl/-/async-hook-jl-1.7.6.tgz#4fd25c2f864dbaf279c610d73bf97b1b28595e68" + integrity sha512-gFaHkFfSxTjvoxDMYqDuGHlcRyUuamF8s+ZTtJdDzqjws4mCt7v0vuV79/E2Wr2/riMQgtG4/yUtXWs1gZ7JMg== + dependencies: + stack-chain "^1.3.7" + +async@~2.1.4: + version "2.1.5" + resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc" + integrity sha1-5YfGhYCZSsZ/xW/4bTrFa9voELw= + dependencies: + lodash "^4.14.0" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + atomic-sleep@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== +autolinker@~0.28.0: + version "0.28.1" + resolved "https://registry.yarnpkg.com/autolinker/-/autolinker-0.28.1.tgz#0652b491881879f0775dace0cdca3233942a4e47" + integrity sha1-BlK0kYgYefB3XazgzcoyM5QqTkc= + dependencies: + gulp-header "^1.7.1" + aws-sdk@^2.811.0: version "2.1066.0" resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1066.0.tgz#2a9b00d983f3c740a7adda18d4e9a5c34d4d3887" @@ -1267,6 +1388,21 @@ aws-sdk@^2.811.0: uuid "3.3.2" xml2js "0.4.19" +aws-sdk@^2.901.0: + version "2.1111.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1111.0.tgz#02b1e5c530ef8140235ee7c48c710bb2dbd7dc84" + integrity sha512-WRyNcCckzmu1djTAWfR2r+BuI/PbuLrhG3oa+oH39v4NZ4EecYWFL1CoCPlC2kRUML4maSba5T4zlxjcNl7ELQ== + dependencies: + buffer "4.9.2" + events "1.1.1" + ieee754 "1.1.13" + jmespath "0.16.0" + querystring "0.2.0" + sax "1.2.1" + url "0.10.3" + uuid "3.3.2" + xml2js "0.4.19" + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -1358,6 +1494,19 @@ base64url@3.x.x, base64url@^3.0.1: resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" @@ -1375,6 +1524,15 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + boxen@^5.0.0: version "5.1.2" resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" @@ -1397,6 +1555,22 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + braces@^3.0.1, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -1476,6 +1650,21 @@ bytes@^3.0.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + cache-content-type@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-content-type/-/cache-content-type-1.0.1.tgz#035cde2b08ee2129f4a8315ea8f00a00dba1453c" @@ -1595,6 +1784,11 @@ chokidar@^3.5.2: optionalDependencies: fsevents "~2.3.2" +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -1610,6 +1804,16 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + cli-boxes@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" @@ -1648,6 +1852,20 @@ clone-response@^1.0.2: dependencies: mimic-response "^1.0.0" +cls-hooked@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908" + integrity sha512-J4Xj5f5wq/4jAvcdgoGsL3G103BtWpZrMo8NEinRltN+xpTZdI+M38pyQqhuFU/P792xkMFvnKSf+Lm81U1bxw== + dependencies: + async-hook-jl "^1.7.6" + emitter-listener "^1.0.1" + semver "^5.4.1" + +cluster-key-slot@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz#30474b2a981fb12172695833052bc0d01336d10d" + integrity sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw== + co-body@^5.1.1: version "5.2.0" resolved "https://registry.yarnpkg.com/co-body/-/co-body-5.2.0.tgz#5a0a658c46029131e0e3a306f67647302f71c124" @@ -1668,6 +1886,14 @@ collect-v8-coverage@^1.0.0: resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -1719,7 +1945,7 @@ commoner@^0.10.1: q "^1.1.2" recast "^0.11.17" -component-emitter@^1.3.0: +component-emitter@^1.2.1, component-emitter@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== @@ -1736,6 +1962,13 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +concat-with-sourcemaps@*: + version "1.1.0" + resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz#d4ea93f05ae25790951b99e7b3b09e3908a4082e" + integrity sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg== + dependencies: + source-map "^0.6.1" + configstore@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" @@ -1785,6 +2018,11 @@ cookies@~0.8.0: depd "~2.0.0" keygrip "~1.1.0" +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + copyfiles@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/copyfiles/-/copyfiles-2.4.1.tgz#d2dcff60aaad1015f09d0b66e7f0f1c5cd3c5da5" @@ -1883,6 +2121,11 @@ dateformat@^4.5.1: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.6.3.tgz#556fa6497e5217fedb78821424f8a1c22fa3f4b5" integrity sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA== +dayjs@^1.10.4: + version "1.11.0" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.0.tgz#009bf7ef2e2ea2d5db2e6583d2d39a4b5061e805" + integrity sha512-JLC809s6Y948/FuCZPm5IX8rRhQwOiyMb2TfVVQEixG7P8Lm/gt5S7yoQZmC8x1UehI9Pb7sksEt4xx14m+7Ug== + debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3: version "4.3.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" @@ -1890,6 +2133,13 @@ debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3: dependencies: ms "2.1.2" +debug@^2.2.0, debug@^2.3.3: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + debug@^3.1.0, debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -1897,11 +2147,23 @@ debug@^3.1.0, debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.3.1: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + decimal.js@^10.2.1: version "10.3.1" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + decompress-response@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" @@ -1941,6 +2203,13 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== +default-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/default-compare/-/default-compare-1.0.0.tgz#cb61131844ad84d84788fb68fd01681ca7781a2f" + integrity sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ== + dependencies: + kind-of "^5.0.2" + defer-to-connect@^1.0.1: version "1.1.3" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" @@ -1959,6 +2228,28 @@ deferred-leveldown@~5.3.0: abstract-leveldown "~6.2.1" inherits "^2.0.3" +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + defined@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" @@ -1974,6 +2265,11 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= +denque@^1.1.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.1.tgz#07f670e29c9a78f8faecb2566a1e2c11929c5cbf" + integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw== + depd@^2.0.0, depd@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" @@ -2093,6 +2389,13 @@ electron-to-chromium@^1.4.17: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.58.tgz#cd980b08338210b591c25492857a518fe286b1d4" integrity sha512-7LXwnKyqcEaMFVXOer+2JPfFs1D+ej7yRRrfZoIH1YlLQZ81OvBNwSCBBLtExVkoMQQgOWwO0FbZVge6U/8rhQ== +emitter-listener@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/emitter-listener/-/emitter-listener-1.1.2.tgz#56b140e8f6992375b3d7cb2cab1cc7432d9632e8" + integrity sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ== + dependencies: + shimmer "^1.2.0" + emittery@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" @@ -2123,7 +2426,7 @@ encoding-down@^6.3.0: level-codec "^9.0.0" level-errors "^2.0.0" -end-of-stream@^1.1.0: +end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== @@ -2137,6 +2440,11 @@ end-stream@~0.1.0: dependencies: write-stream "~0.4.3" +ent@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" + integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0= + errno@~0.1.1: version "0.1.8" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" @@ -2346,6 +2654,19 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + expect@^27.4.6: version "27.4.6" resolved "https://registry.yarnpkg.com/expect/-/expect-27.4.6.tgz#f335e128b0335b6ceb4fcab67ece7cbd14c942e6" @@ -2356,6 +2677,21 @@ expect@^27.4.6: jest-matcher-utils "^27.4.6" jest-message-util "^27.4.6" +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" @@ -2370,6 +2706,20 @@ external-editor@^3.0.3: iconv-lite "^0.4.24" tmp "^0.0.33" +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -2458,6 +2808,16 @@ file-entry-cache@^5.0.1: dependencies: flat-cache "^2.0.1" +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -2492,6 +2852,11 @@ flatted@^2.0.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -2539,11 +2904,28 @@ formidable@^2.0.1: once "1.4.0" qs "6.9.3" +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + fresh@~0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-exists-sync@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" + integrity sha1-mC1ok6+RjnLQjeyehnP/K1qNat0= + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -2583,6 +2965,14 @@ get-intrinsic@^1.0.2: has "^1.0.3" has-symbols "^1.0.1" +get-object@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/get-object/-/get-object-0.2.0.tgz#d92ff7d5190c64530cda0543dac63a3d47fe8c0c" + integrity sha1-2S/31RkMZFMM2gVD2sY6PUf+jAw= + dependencies: + is-number "^2.0.2" + isobject "^0.2.0" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -2607,6 +2997,18 @@ get-stream@^6.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +get-value@^3.0.0, get-value@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-3.0.1.tgz#5efd2a157f1d6a516d7524e124ac52d0a39ef5a8" + integrity sha512-mKZj9JLQrwMBtj5wxi6MH8Z5eSKaERpAwjg43dPtlGI1ZVEgH/qC7T8/6R2OBSUA+zzHBZgICsVJaEIV2tKTDA== + dependencies: + isobject "^3.0.1" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -2675,6 +3077,32 @@ globby@^11.0.4: merge2 "^1.4.1" slash "^3.0.0" +google-auth-library@~0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-0.10.0.tgz#6e15babee85fd1dd14d8d128a295b6838d52136e" + integrity sha1-bhW6vuhf0d0U2NEoopW2g41SE24= + dependencies: + gtoken "^1.2.1" + jws "^3.1.4" + lodash.noop "^3.0.1" + request "^2.74.0" + +google-p12-pem@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-0.1.2.tgz#33c46ab021aa734fa0332b3960a9a3ffcb2f3177" + integrity sha1-M8RqsCGqc0+gMys5YKmj/8svMXc= + dependencies: + node-forge "^0.7.1" + +googleapis@^16.0.0: + version "16.1.0" + resolved "https://registry.yarnpkg.com/googleapis/-/googleapis-16.1.0.tgz#0f19f2d70572d918881a0f626e3b1a2fa8629576" + integrity sha1-Dxny1wVy2RiIGg9ibjsaL6hilXY= + dependencies: + async "~2.1.4" + google-auth-library "~0.10.0" + string-template "~1.0.0" + got@^11.8.1: version "11.8.3" resolved "https://registry.yarnpkg.com/got/-/got-11.8.3.tgz#f496c8fdda5d729a90b4905d2b07dbd148170770" @@ -2714,6 +3142,45 @@ graceful-fs@^4.1.2, graceful-fs@^4.2.4: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== +gtoken@^1.2.1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-1.2.3.tgz#5509571b8afd4322e124cf66cf68115284c476d8" + integrity sha512-wQAJflfoqSgMWrSBk9Fg86q+sd6s7y6uJhIvvIPz++RElGlMtEqsdAR2oWwZ/WTEtp7P9xFbJRrT976oRgzJ/w== + dependencies: + google-p12-pem "^0.1.0" + jws "^3.0.0" + mime "^1.4.1" + request "^2.72.0" + +gulp-header@^1.7.1: + version "1.8.12" + resolved "https://registry.yarnpkg.com/gulp-header/-/gulp-header-1.8.12.tgz#ad306be0066599127281c4f8786660e705080a84" + integrity sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ== + dependencies: + concat-with-sourcemaps "*" + lodash.template "^4.4.0" + through2 "^2.0.0" + +handlebars-utils@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/handlebars-utils/-/handlebars-utils-1.0.6.tgz#cb9db43362479054782d86ffe10f47abc76357f9" + integrity sha512-d5mmoQXdeEqSKMtQQZ9WkiUcO1E3tPbWxluCK9hVgIDPzQa9WsKo3Lbe/sGflTe7TomHEeZaOgwIkyIr1kfzkw== + dependencies: + kind-of "^6.0.0" + typeof-article "^0.1.1" + +handlebars@^4.7.6, handlebars@^4.7.7: + version "4.7.7" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" + integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.0" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -2749,6 +3216,52 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-value@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-2.0.2.tgz#d0f12e8780ba8e90e66ad1a21c707fdb67c25658" + integrity sha512-ybKOlcRsK2MqrM3Hmz/lQxXHZ6ejzSPzpNabKB45jb5qDgJvKPa3SdapTsTLwEb9WltgWpOmNax7i+DzNOk4TA== + dependencies: + get-value "^3.0.0" + has-values "^2.0.1" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has-values@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-2.0.1.tgz#3876200ff86d8a8546a9264a952c17d5fc17579d" + integrity sha512-+QdH3jOmq9P8GfdjFg0eJudqx1FqU62NQJ4P16rOEHeRdl7ckgwn6uqQjzYE0ZoHVV/e5E2esuJ5Gl5+HUW19w== + dependencies: + kind-of "^6.0.2" + has-yarn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" @@ -2761,6 +3274,16 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +helper-md@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/helper-md/-/helper-md-0.2.2.tgz#c1f59d7e55bbae23362fd8a0e971607aec69d41f" + integrity sha1-wfWdflW7riM2L9ig6XFgeuxp1B8= + dependencies: + ent "^2.2.0" + extend-shallow "^2.0.1" + fs-exists-sync "^0.1.0" + remarkable "^1.6.2" + hexoid@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18" @@ -2778,6 +3301,14 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +html-tag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/html-tag/-/html-tag-2.0.0.tgz#36c3bc8d816fd30b570d5764a497a641640c2fed" + integrity sha512-XxzooSo6oBoxBEUazgjdXj7VwTn/iSTSZzTYKzYY6I916tkaYzypHxy+pbVU1h+0UQ9JlVf5XkNQyxOAiiQO1g== + dependencies: + is-self-closing "^1.0.1" + kind-of "^6.0.0" + http-assert@^1.3.0: version "1.5.0" resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.5.0.tgz#c389ccd87ac16ed2dfa6246fd73b926aa00e6b8f" @@ -2971,6 +3502,37 @@ inquirer@^7.0.0: strip-ansi "^6.0.0" through "^2.3.6" +ioredis@^4.27.1: + version "4.28.5" + resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.28.5.tgz#5c149e6a8d76a7f8fa8a504ffc85b7d5b6797f9f" + integrity sha512-3GYo0GJtLqgNXj4YhrisLaNNvWSNwSS2wS4OELGfGxH8I69+XfNdnmV1AyN+ZqMh0i7eX+SWjrwFKDBDgfBC1A== + dependencies: + cluster-key-slot "^1.1.0" + debug "^4.3.1" + denque "^1.1.0" + lodash.defaults "^4.2.0" + lodash.flatten "^4.4.0" + lodash.isarguments "^3.1.0" + p-map "^2.1.0" + redis-commands "1.7.0" + redis-errors "^1.2.0" + redis-parser "^3.0.0" + standard-as-callback "^2.1.0" + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -2978,6 +3540,11 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + is-ci@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" @@ -2997,6 +3564,57 @@ is-core-module@^2.8.1: dependencies: has "^1.0.3" +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-even@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-even/-/is-even-1.0.0.tgz#76b5055fbad8d294a86b6a949015e1c97b717c06" + integrity sha1-drUFX7rY0pSoa2qUkBXhyXtxfAY= + dependencies: + is-odd "^0.1.2" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -3044,6 +3662,20 @@ is-npm@^5.0.0: resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== +is-number@^2.0.2: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= + dependencies: + kind-of "^3.0.2" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -3054,16 +3686,37 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== +is-odd@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-0.1.2.tgz#bc573b5ce371ef2aad6e6f49799b72bef13978a7" + integrity sha1-vFc7XONx7yqtbm9JeZtyvvE5eKc= + dependencies: + is-number "^3.0.0" + is-path-inside@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + is-potential-custom-element-name@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== +is-self-closing@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-self-closing/-/is-self-closing-1.0.1.tgz#5f406b527c7b12610176320338af0fa3896416e4" + integrity sha512-E+60FomW7Blv5GXTlYee2KDrnG6srxF7Xt1SjrhWUGUEsTFIqY/nq2y3DaftCsgUMdh89V07IVfhY9KIJhLezg== + dependencies: + self-closing-tags "^1.0.1" + is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" @@ -3083,6 +3736,11 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + is-yarn-global@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" @@ -3093,7 +3751,7 @@ isarray@0.0.1: resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= -isarray@^1.0.0, isarray@~1.0.0: +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= @@ -3103,6 +3761,23 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +isobject@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-0.2.0.tgz#a3432192f39b910b5f02cc989487836ec70aa85e" + integrity sha1-o0MhkvObkQtfAsyYlIeDbscKqF4= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -3688,7 +4363,7 @@ json5@2.x, json5@^2.1.2: dependencies: minimist "^1.2.5" -jsonwebtoken@^8.2.0: +jsonwebtoken@^8.2.0, jsonwebtoken@^8.5.1: version "8.5.1" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== @@ -3734,7 +4409,7 @@ jwa@^1.4.1: ecdsa-sig-formatter "1.0.11" safe-buffer "^5.0.1" -jws@^3.2.2: +jws@^3.0.0, jws@^3.1.4, jws@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== @@ -3763,6 +4438,30 @@ keyv@^4.0.0: dependencies: json-buffer "3.0.1" +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.1.0, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0, kind-of@^5.0.2: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -4004,11 +4703,31 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +lodash._reinterpolate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= + +lodash.defaults@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= + +lodash.flatten@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= + lodash.includes@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= +lodash.isarguments@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo= + lodash.isboolean@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" @@ -4039,12 +4758,32 @@ lodash.memoize@4.x: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= +lodash.noop@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash.noop/-/lodash.noop-3.0.1.tgz#38188f4d650a3a474258439b96ec45b32617133c" + integrity sha1-OBiPTWUKOkdCWEObluxFsyYXEzw= + lodash.once@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= -lodash@^4.17.14, lodash@^4.17.19, lodash@^4.7.0: +lodash.template@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" + integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.templatesettings "^4.0.0" + +lodash.templatesettings@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" + integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== + dependencies: + lodash._reinterpolate "^3.0.0" + +lodash@^4.14.0, lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -4095,6 +4834,18 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -4127,6 +4878,25 @@ methods@^1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= +micromatch@^3.1.5: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + micromatch@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" @@ -4147,6 +4917,11 @@ mime-types@^2.1.12, mime-types@^2.1.18, mime-types@~2.1.19, mime-types@~2.1.24: dependencies: mime-db "1.51.0" +mime@^1.4.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + mime@^2.5.0: version "2.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" @@ -4179,6 +4954,19 @@ minimist@^1.2.0, minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp-classic@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + mkdirp@^0.5.0, mkdirp@^0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" @@ -4196,6 +4984,11 @@ mri@1.1.4: resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.4.tgz#7cb1dd1b9b40905f1fac053abe25b6720f44744a" integrity sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w== +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -4211,6 +5004,23 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + napi-macros@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b" @@ -4226,6 +5036,11 @@ negotiator@0.6.2: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== +neo-async@^2.6.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -4243,6 +5058,11 @@ node-fetch@^2.6.1: dependencies: whatwg-url "^5.0.0" +node-forge@^0.7.1: + version "0.7.6" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac" + integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw== + node-gyp-build@~4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.1.1.tgz#d7270b5d86717068d114cc57fff352f96d745feb" @@ -4336,11 +5156,34 @@ object-assign@^2.0.0: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-2.1.1.tgz#43c36e5d569ff8e4816c4efa8be02d26967c18aa" integrity sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo= +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + object-inspect@^1.9.0: version "1.12.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + on-finished@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -4408,6 +5251,11 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-map@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -4440,6 +5288,19 @@ parseurl@^1.3.2: resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +passport-google-auth@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/passport-google-auth/-/passport-google-auth-1.0.2.tgz#8b300b5aa442ef433de1d832ed3112877d0b2938" + integrity sha1-izALWqRC70M94dgy7TESh30LKTg= + dependencies: + googleapis "^16.0.0" + passport-strategy "1.x" + passport-google-oauth1@1.x.x: version "1.0.0" resolved "https://registry.yarnpkg.com/passport-google-oauth1/-/passport-google-oauth1-1.0.0.tgz#af74a803df51ec646f66a44d82282be6f108e0cc" @@ -4497,7 +5358,7 @@ passport-oauth2@1.x.x: uid2 "0.0.x" utils-merge "1.x.x" -passport-strategy@1.x.x, passport-strategy@^1.0.0: +passport-strategy@1.x, passport-strategy@1.x.x, passport-strategy@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" integrity sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ= @@ -4629,6 +5490,11 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +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" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + pouchdb-adapter-leveldb-core@7.2.2: version "7.2.2" resolved "https://registry.yarnpkg.com/pouchdb-adapter-leveldb-core/-/pouchdb-adapter-leveldb-core-7.2.2.tgz#e0aa6a476e2607d7ae89f4a803c9fba6e6d05a8a" @@ -4951,7 +5817,7 @@ readable-stream@1.1.14: isarray "0.0.1" string_decoder "~0.10.x" -"readable-stream@2 || 3", readable-stream@^3.0.0, readable-stream@^3.4.0, readable-stream@^3.6.0: +"readable-stream@2 || 3", readable-stream@^3.0.0, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -5005,6 +5871,31 @@ recast@^0.11.17: private "~0.1.5" source-map "~0.5.0" +redis-commands@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.7.0.tgz#15a6fea2d58281e27b1cd1acfb4b293e278c3a89" + integrity sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ== + +redis-errors@^1.0.0, redis-errors@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" + integrity sha1-62LSrbFeTq9GEMBK/hUpOEJQq60= + +redis-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4" + integrity sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ= + dependencies: + redis-errors "^1.0.0" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + regexpp@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" @@ -5024,7 +5915,32 @@ registry-url@^5.0.0: dependencies: rc "^1.2.8" -request@^2.88.0: +relative@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/relative/-/relative-3.0.2.tgz#0dcd8ec54a5d35a3c15e104503d65375b5a5367f" + integrity sha1-Dc2OxUpdNaPBXhBFA9ZTdbWlNn8= + dependencies: + isobject "^2.0.0" + +remarkable@^1.6.2: + version "1.7.4" + resolved "https://registry.yarnpkg.com/remarkable/-/remarkable-1.7.4.tgz#19073cb960398c87a7d6546eaa5e50d2022fcd00" + integrity sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg== + dependencies: + argparse "^1.0.10" + autolinker "~0.28.0" + +repeat-element@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +request@^2.72.0, request@^2.74.0, request@^2.88.0: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -5085,6 +6001,11 @@ resolve-path@^1.4.0: http-errors "~1.6.2" path-is-absolute "1.0.1" +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + resolve.exports@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" @@ -5121,6 +6042,11 @@ restore-cursor@^3.1.0: onetime "^5.1.0" signal-exit "^3.0.2" +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -5174,11 +6100,23 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sanitize-s3-objectkey@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/sanitize-s3-objectkey/-/sanitize-s3-objectkey-0.0.1.tgz#efa9887cd45275b40234fb4bb12fc5754fe64e7e" + integrity sha512-ZTk7aqLxy4sD40GWcYWoLfbe05XLmkKvh6vGKe13ADlei24xlezcvjgKy1qRArlaIbIMYaqK7PCalvZtulZlaQ== + sax@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" @@ -5196,6 +6134,11 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" +self-closing-tags@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/self-closing-tags/-/self-closing-tags-1.0.1.tgz#6c5fa497994bb826b484216916371accee490a5d" + integrity sha512-7t6hNbYMxM+VHXTgJmxwgZgLGktuXtVVD5AivWzNTdJBM4DBjnDKDzkf2SrNjihaArpeJYNjxkELBu1evI4lQA== + semver-diff@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" @@ -5210,7 +6153,7 @@ semver@7.x, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: dependencies: lru-cache "^6.0.0" -semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: +semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -5225,6 +6168,16 @@ server-destroy@^1.0.1: resolved "https://registry.yarnpkg.com/server-destroy/-/server-destroy-1.0.1.tgz#f13bf928e42b9c3e79383e61cc3998b5d14e6cdd" integrity sha1-8Tv5KOQrnD55OD5hzDmYtdFObN0= +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + setprototypeof@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" @@ -5259,6 +6212,11 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +shimmer@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" + integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== + side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -5292,6 +6250,36 @@ slice-ansi@^2.1.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + sonic-boom@^1.0.2: version "1.4.1" resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-1.4.1.tgz#d35d6a74076624f12e6f917ade7b9d75e918f53e" @@ -5300,6 +6288,17 @@ sonic-boom@^1.0.2: atomic-sleep "^1.0.0" flatstr "^1.0.12" +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + source-map-support@^0.5.6: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -5308,6 +6307,11 @@ source-map-support@^0.5.6: buffer-from "^1.0.0" source-map "^0.6.0" +source-map-url@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" + integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== + source-map@^0.4.2: version "0.4.4" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" @@ -5315,7 +6319,7 @@ source-map@^0.4.2: dependencies: amdefine ">=0.0.4" -source-map@^0.5.0, source-map@~0.5.0: +source-map@^0.5.0, source-map@^0.5.6, source-map@~0.5.0: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= @@ -5335,6 +6339,13 @@ spark-md5@3.0.1: resolved "https://registry.yarnpkg.com/spark-md5/-/spark-md5-3.0.1.tgz#83a0e255734f2ab4e5c466e5a2cfc9ba2aa2124d" integrity sha512-0tF3AGSD1ppQeuffsLDIOWlKUd3lS92tFxcsrh5Pe3ZphhnoK+oXIBTzOAThZCiuINZLvpiLH/1VS1/ANEJVig== +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + split2@^3.1.1: version "3.2.2" resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" @@ -5362,6 +6373,11 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" +stack-chain@^1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285" + integrity sha1-0ZLJ/06moiyUxN1FkXHj8AzqEoU= + stack-utils@^2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" @@ -5369,6 +6385,19 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" +standard-as-callback@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45" + integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A== + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + "statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" @@ -5392,6 +6421,11 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +string-template@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/string-template/-/string-template-1.0.0.tgz#9e9f2233dc00f218718ec379a28a5673ecca8b96" + integrity sha1-np8iM9wA8hhxjsN5oopWc+zKi5Y= + string-width@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" @@ -5463,6 +6497,11 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= +striptags@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/striptags/-/striptags-3.2.0.tgz#cc74a137db2de8b0b9a370006334161f7dd67052" + integrity sha512-g45ZOGzHDMe2bdYMdIvdAfCQkCTDMGBazSw1ypMowwGIee7ZQ5dU0rBJ8Jqgl+jAKIv4dbeE1jscZq9wid1Tkw== + sublevel-pouchdb@7.2.2: version "7.2.2" resolved "https://registry.yarnpkg.com/sublevel-pouchdb/-/sublevel-pouchdb-7.2.2.tgz#49e46cd37883bf7ff5006d7c5b9bcc7bcc1f422f" @@ -5547,6 +6586,27 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" +tar-fs@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + +tar-stream@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + terminal-link@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" @@ -5582,7 +6642,7 @@ through2@3.0.2: inherits "^2.0.4" readable-stream "2 || 3" -through2@^2.0.1: +through2@^2.0.0, through2@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== @@ -5595,6 +6655,11 @@ through@^2.3.6, through@~2.3.4: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= +timekeeper@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/timekeeper/-/timekeeper-2.2.0.tgz#9645731fce9e3280a18614a57a9d1b72af3ca368" + integrity sha512-W3AmPTJWZkRwu+iSNxPIsLZ2ByADsOLbbLxe46UJyWj3mlYLlwucKiq+/dPm0l9wTzqoF3/2PH0AGFCebjq23A== + tiny-queue@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/tiny-queue/-/tiny-queue-0.2.1.tgz#25a67f2c6e253b2ca941977b5ef7442ef97a6046" @@ -5617,11 +6682,31 @@ to-fast-properties@^2.0.0: resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= +to-gfm-code-block@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/to-gfm-code-block/-/to-gfm-code-block-0.1.1.tgz#25d045a5fae553189e9637b590900da732d8aa82" + integrity sha1-JdBFpfrlUxielje1kJANpzLYqoI= + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + to-readable-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -5629,6 +6714,16 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + toidentifier@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" @@ -5773,11 +6868,23 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" +typeof-article@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/typeof-article/-/typeof-article-0.1.1.tgz#9f07e733c3fbb646ffa9e61c08debacd460e06af" + integrity sha1-nwfnM8P7tkb/qeYcCN66zUYOBq8= + dependencies: + kind-of "^3.1.0" + typescript@4.5.5: version "4.5.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3" integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA== +uglify-js@^3.1.4: + version "3.15.4" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.15.4.tgz#fa95c257e88f85614915b906204b9623d4fa340d" + integrity sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA== + uid2@0.0.x: version "0.0.4" resolved "https://registry.yarnpkg.com/uid2/-/uid2-0.0.4.tgz#033f3b1d5d32505f5ce5f888b9f3b667123c0a44" @@ -5788,6 +6895,16 @@ undefsafe@^2.0.5: resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + unique-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" @@ -5805,6 +6922,14 @@ unpipe@1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + untildify@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" @@ -5847,6 +6972,11 @@ urijs@^1.19.2: resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.8.tgz#ee0407a18528934d3c383e691912f47043a58feb" integrity sha512-iIXHrjomQ0ZCuDRy44wRbyTZVnfVNLVo3Ksz1yxNyE5wV1IDZW2S5Jszy45DTlw/UdsnRT7DyDhIz7Gy+vJumw== +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + url-parse-lax@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" @@ -5862,6 +6992,11 @@ url@0.10.3: punycode "1.3.2" querystring "0.2.0" +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -5887,6 +7022,11 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + v8-compile-cache@^2.0.3: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" @@ -5915,6 +7055,14 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +vm2@^3.9.4: + version "3.9.9" + resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.9.tgz#c0507bc5fbb99388fad837d228badaaeb499ddc5" + integrity sha512-xwTm7NLh/uOjARRBs8/95H0e8fT3Ukw5D/JJWhxMbhKzNh1Nu981jQKvkep9iKYNxzlVrdzD0mlBGkDKZWprlw== + dependencies: + acorn "^8.7.0" + acorn-walk "^8.2.0" + vuvuzela@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/vuvuzela/-/vuvuzela-1.0.3.tgz#3be145e58271c73ca55279dd851f12a682114b0b" @@ -6019,6 +7167,11 @@ word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -6130,6 +7283,11 @@ yargs@^16.1.0, yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" +year@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/year/-/year-0.2.1.tgz#4083ae520a318b23ec86037f3000cb892bdf9bb0" + integrity sha1-QIOuUgoxiyPshgN/MADLiSvfm7A= + ylru@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/ylru/-/ylru-1.2.1.tgz#f576b63341547989c1de7ba288760923b27fe84f" @@ -6139,3 +7297,8 @@ yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +zlib@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/zlib/-/zlib-1.0.5.tgz#6e7c972fc371c645a6afb03ab14769def114fcc0" + integrity sha1-bnyXL8NxxkWmr7A6sUdp3vEU/MA= From 6db5c62e48e2b69f25e04ae64c0872c1850dbb06 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Mon, 11 Apr 2022 22:32:12 +0100 Subject: [PATCH 24/75] View/Table/Row Import/Export events --- packages/backend-core/src/events/constants.js | 1 + .../backend-core/src/events/handlers/index.js | 4 +- .../backend-core/src/events/handlers/row.js | 11 ++- .../backend-core/src/events/handlers/table.js | 14 +-- .../backend-core/src/events/handlers/view.js | 5 +- .../src/tests/utilities/mocks/events.js | 8 +- .../src/api/controllers/table/external.js | 2 + .../server/src/api/controllers/table/index.js | 5 + .../server/src/api/controllers/table/utils.ts | 8 +- .../server/src/api/controllers/view/index.js | 12 ++- .../server/src/api/routes/tests/table.spec.js | 75 +++++++++++--- .../server/src/api/routes/tests/view.spec.js | 97 ++++++++++++++----- 12 files changed, 180 insertions(+), 62 deletions(-) diff --git a/packages/backend-core/src/events/constants.js b/packages/backend-core/src/events/constants.js index 7ea68c5a98..354f92f56b 100644 --- a/packages/backend-core/src/events/constants.js +++ b/packages/backend-core/src/events/constants.js @@ -103,6 +103,7 @@ exports.Events = { // ROW // ROW_CREATED: "row:created", + ROW_IMPORT: "row:import", // COMPONENT COMPONENT_CREATED: "component:created", diff --git a/packages/backend-core/src/events/handlers/index.js b/packages/backend-core/src/events/handlers/index.js index c9007ae28f..a1b92b0332 100644 --- a/packages/backend-core/src/events/handlers/index.js +++ b/packages/backend-core/src/events/handlers/index.js @@ -9,7 +9,8 @@ const layout = require("./layout") const org = require("./org") const query = require("./query") const role = require("./role") -const row = require("./screen") +const screen = require("./screen") +const row = require("./row") const table = require("./table") const serve = require("./serve") const user = require("./user") @@ -27,6 +28,7 @@ module.exports = { org, query, role, + screen, row, table, serve, diff --git a/packages/backend-core/src/events/handlers/row.js b/packages/backend-core/src/events/handlers/row.js index 07c18e241f..28cb45645a 100644 --- a/packages/backend-core/src/events/handlers/row.js +++ b/packages/backend-core/src/events/handlers/row.js @@ -1,7 +1,14 @@ -// const events = require("../events") -// const { Events } = require("../constants") +const events = require("../events") +const { Events } = require("../constants") + +/* eslint-disable */ // exports.created = () => { // const properties = {} // events.processEvent(Events.ROW_CREATED, properties) // } + +exports.import = (table, format, count) => { + const properties = {} + events.processEvent(Events.ROW_IMPORT, properties) +} diff --git a/packages/backend-core/src/events/handlers/table.js b/packages/backend-core/src/events/handlers/table.js index 891701ef7d..e212faad15 100644 --- a/packages/backend-core/src/events/handlers/table.js +++ b/packages/backend-core/src/events/handlers/table.js @@ -1,29 +1,29 @@ const events = require("../events") const { Events } = require("../constants") -exports.created = () => { +/* eslint-disable */ + +exports.created = table => { const properties = {} events.processEvent(Events.TABLE_CREATED, properties) } -exports.updated = () => { +exports.updated = table => { const properties = {} events.processEvent(Events.TABLE_UPDATED, properties) } -exports.deleted = () => { +exports.deleted = table => { const properties = {} events.processEvent(Events.TABLE_DELETED, properties) } -// TODO -exports.exported = () => { +exports.exported = (table, format) => { const properties = {} events.processEvent(Events.TABLE_EXPORTED, properties) } -// TODO -exports.imported = () => { +exports.imported = (table, format) => { const properties = {} events.processEvent(Events.TABLE_IMPORTED, properties) } diff --git a/packages/backend-core/src/events/handlers/view.js b/packages/backend-core/src/events/handlers/view.js index 551960fcb0..785eaa0434 100644 --- a/packages/backend-core/src/events/handlers/view.js +++ b/packages/backend-core/src/events/handlers/view.js @@ -1,6 +1,8 @@ const events = require("../events") const { Events } = require("../constants") +/* eslint-disable */ + exports.created = () => { const properties = {} events.processEvent(Events.VIEW_CREATED, properties) @@ -18,8 +20,7 @@ exports.deleted = () => { events.processEvent(Events.VIEW_DELETED, properties) } -// TODO -exports.exported = () => { +exports.exported = (table, format) => { const properties = {} events.processEvent(Events.VIEW_EXPORTED, properties) } diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index 1176bbb3ee..1d0fdc3f8c 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -69,7 +69,9 @@ jest.mock("../../../events", () => { assigned: jest.fn(), unassigned: jest.fn(), }, - row: {}, + row: { + import: jest.fn(), + }, screen: { created: jest.fn(), deleted: jest.fn(), @@ -103,7 +105,9 @@ jest.mock("../../../events", () => { imported: jest.fn(), permissionUpdated: jest.fn(), }, - view: {}, + view: { + exported: jest.fn(), + }, } }) diff --git a/packages/server/src/api/controllers/table/external.js b/packages/server/src/api/controllers/table/external.js index 86d855a28e..f61a31dac1 100644 --- a/packages/server/src/api/controllers/table/external.js +++ b/packages/server/src/api/controllers/table/external.js @@ -19,6 +19,7 @@ const { cloneDeep } = require("lodash/fp") const csvParser = require("../../../utilities/csvParser") const { handleRequest } = require("../row/external") const { getAppDB } = require("@budibase/backend-core/context") +const { events } = require("@budibase/backend-core") async function makeTableRequest( datasource, @@ -314,5 +315,6 @@ exports.bulkImport = async function (ctx) { await handleRequest(DataSourceOperation.BULK_CREATE, table._id, { rows, }) + events.row.import(table, "csv", rows.length) return table } diff --git a/packages/server/src/api/controllers/table/index.js b/packages/server/src/api/controllers/table/index.js index 1f88172d0a..701b805b50 100644 --- a/packages/server/src/api/controllers/table/index.js +++ b/packages/server/src/api/controllers/table/index.js @@ -56,12 +56,17 @@ exports.find = async function (ctx) { exports.save = async function (ctx) { const appId = ctx.appId const table = ctx.request.body + const importFormat = + table.dataImport && table.dataImport.csvString ? "csv" : undefined const savedTable = await pickApi({ table }).save(ctx) if (!table._id) { events.table.created(savedTable) } else { events.table.updated(savedTable) } + if (importFormat) { + events.table.imported(savedTable, importFormat) + } ctx.status = 200 ctx.message = `Table ${table.name} saved successfully.` ctx.eventEmitter && diff --git a/packages/server/src/api/controllers/table/utils.ts b/packages/server/src/api/controllers/table/utils.ts index 20bd3cb090..d72c8788fc 100644 --- a/packages/server/src/api/controllers/table/utils.ts +++ b/packages/server/src/api/controllers/table/utils.ts @@ -26,7 +26,8 @@ import { getViews, saveView } from "../view/utils" import viewTemplate from "../view/viewBuilder" const { getAppDB } = require("@budibase/backend-core/context") import { cloneDeep } from "lodash/fp" -import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro" +import { quotas } from "@budibase/pro" +import { events } from "@budibase/backend-core" export async function clearColumns(table: any, columnNames: any) { const db = getAppDB() @@ -148,8 +149,7 @@ export async function handleDataImport(user: any, table: any, dataImport: any) { } await quotas.addRows(finalData.length, () => db.bulkDocs(finalData)) - let response = await db.put(table) - table._rev = response._rev + events.row.import(table, "csv", finalData.length) return table } @@ -245,7 +245,7 @@ class TableSaveFunctions { // after saving async after(table: any) { table = await handleSearchIndexes(table) - table = await handleDataImport(this.user, table, this.dataImport) + await handleDataImport(this.user, table, this.dataImport) return table } diff --git a/packages/server/src/api/controllers/view/index.js b/packages/server/src/api/controllers/view/index.js index fd6b32f3d6..1174abc904 100644 --- a/packages/server/src/api/controllers/view/index.js +++ b/packages/server/src/api/controllers/view/index.js @@ -6,6 +6,8 @@ const { fetchView } = require("../row") const { getTable } = require("../table/utils") const { FieldTypes } = require("../../../constants") const { getAppDB } = require("@budibase/backend-core/context") +const { events } = require("@budibase/backend-core") +const { DocumentTypes } = require("../../../db/utils") exports.fetch = async ctx => { ctx.body = await getViews() @@ -79,9 +81,9 @@ exports.exportView = async ctx => { let rows = ctx.body let schema = view && view.meta && view.meta.schema + const tableId = ctx.params.tableId || view.meta.tableId + const table = await getTable(tableId) if (!schema) { - const tableId = ctx.params.tableId || view.meta.tableId - const table = await getTable(tableId) schema = table.schema } @@ -116,4 +118,10 @@ exports.exportView = async ctx => { // send down the file ctx.attachment(filename) ctx.body = apiFileReturn(exporter(headers, rows)) + + if (viewName.startsWith(DocumentTypes.TABLE)) { + events.table.exported(table, format) + } else { + events.view.exported(table, format) + } } diff --git a/packages/server/src/api/routes/tests/table.spec.js b/packages/server/src/api/routes/tests/table.spec.js index a7d6f3037d..293b37c7b9 100644 --- a/packages/server/src/api/routes/tests/table.spec.js +++ b/packages/server/src/api/routes/tests/table.spec.js @@ -14,37 +14,56 @@ describe("/tables", () => { }) describe("create", () => { - it("returns a success message when the table is successfully created", async () => { - const res = await request + + beforeEach(() => { + jest.clearAllMocks() + }) + + const createTable = (table) => { + if (!table) { + table = basicTable() + } + return request .post(`/api/tables`) - .send({ - name: "TestTable", - key: "name", - schema: { - name: {type: "string"} - } - }) + .send(table) .set(config.defaultHeaders()) .expect('Content-Type', /json/) .expect(200) + + } + + it("returns a success message when the table is successfully created", async () => { + const res = await createTable() + expect(res.res.statusMessage).toEqual("Table TestTable saved successfully.") expect(res.body.name).toEqual("TestTable") expect(events.table.created).toBeCalledTimes(1) expect(events.table.created).toBeCalledWith(res.body) }) + it("creates a table via data import CSV", async () => { + const table = basicTable() + table.dataImport = { + csvString: "\"name\",\"description\"\n\"test-name\",\"test-desc\"", + } + table.dataImport.schema = table.schema + + const res = await createTable(table) + + expect(events.table.created).toBeCalledTimes(1) + expect(events.table.created).toBeCalledWith(res.body) + expect(events.table.imported).toBeCalledTimes(1) + expect(events.table.imported).toBeCalledWith(res.body, "csv") + expect(events.row.import).toBeCalledTimes(1) + expect(events.row.import).toBeCalledWith(res.body, "csv", 1) + }) + it("should apply authorization to endpoint", async () => { await checkBuilderEndpoint({ config, method: "POST", url: `/api/tables`, - body: { - name: "TestTable", - key: "name", - schema: { - name: {type: "string"} - } - } + body: basicTable() }) }) }) @@ -124,6 +143,30 @@ describe("/tables", () => { }) }) + describe("import", () => { + it("imports rows successfully", async () => { + const table = await config.createTable() + const importRequest = { + dataImport: { + csvString: "\"name\",\"description\"\n\"test-name\",\"test-desc\"", + schema: table.schema + } + } + jest.clearAllMocks() + + await request + .post(`/api/tables/${table._id}/import`) + .send(importRequest) + .set(config.defaultHeaders()) + .expect('Content-Type', /json/) + .expect(200) + + expect(events.table.created).not.toHaveBeenCalled() + expect(events.row.import).toBeCalledTimes(1) + expect(events.row.import).toBeCalledWith(table, "csv", 1) + }) + }) + describe("fetch", () => { let testTable diff --git a/packages/server/src/api/routes/tests/view.spec.js b/packages/server/src/api/routes/tests/view.spec.js index b1c5f655c6..cf5c81b0f1 100644 --- a/packages/server/src/api/routes/tests/view.spec.js +++ b/packages/server/src/api/routes/tests/view.spec.js @@ -1,4 +1,5 @@ const setup = require("./utilities") +const { events } = require("@budibase/backend-core") function priceTable() { return { @@ -205,33 +206,77 @@ describe("/views", () => { }) describe("exportView", () => { - it("should be able to export a view", async () => { - await config.createTable(priceTable()) - await config.createRow() + + beforeEach(() => { + jest.clearAllMocks() + }) + + const setupExport = async () => { + const table = await config.createTable() + await config.createRow({ name: "test-name", description: "test-desc" }) + return table + } + + const exportView = async (viewName, format) => { + return request + .get(`/api/views/export?view=${viewName}&format=${format}`) + .set(config.defaultHeaders()) + .expect(200) + } + + const assertJsonExport = (res) => { + const rows = JSON.parse(res.text) + expect(rows.length).toBe(1) + expect(rows[0].name).toBe("test-name") + expect(rows[0].description).toBe("test-desc") + } + + const assertCSVExport = (res) => { + expect(res.text).toBe("\"name\",\"description\"\n\"test-name\",\"test-desc\"") + } + + it("should be able to export a table as JSON", async () => { + const table = await setupExport() + + const res = await exportView(table._id, "json") + + assertJsonExport(res) + expect(events.table.exported).toBeCalledTimes(1) + expect(events.table.exported).toBeCalledWith(table, "json") + }) + + it("should be able to export a table as CSV", async () => { + const table = await setupExport() + + const res = await exportView(table._id, "csv") + + assertCSVExport(res) + expect(events.table.exported).toBeCalledTimes(1) + expect(events.table.exported).toBeCalledWith(table, "csv") + }) + + it("should be able to export a view as JSON", async () => { + let table = await setupExport() const view = await config.createView() - let res = await request - .get(`/api/views/export?view=${view.name}&format=json`) - .set(config.defaultHeaders()) - .expect(200) - let error - try { - const obj = JSON.parse(res.text) - expect(obj.length).toBe(1) - } catch (err) { - error = err - } - expect(error).toBeUndefined() - res = await request - .get(`/api/views/export?view=${view.name}&format=csv`) - .set(config.defaultHeaders()) - .expect(200) - // this shouldn't be JSON - try { - JSON.parse(res.text) - } catch (err) { - error = err - } - expect(error).toBeDefined() + table = await config.getTable(table._id) + + let res = await exportView(view.name, "json") + + assertJsonExport(res) + expect(events.view.exported).toBeCalledTimes(1) + expect(events.view.exported).toBeCalledWith(table, "json") + }) + + it("should be able to export a view as CSV", async () => { + let table = await setupExport() + const view = await config.createView() + table = await config.getTable(table._id) + + let res = await exportView(view.name, "csv") + + assertCSVExport(res) + expect(events.view.exported).toBeCalledTimes(1) + expect(events.view.exported).toBeCalledWith(table, "csv") }) }) }) From 957e90fe86f6930b1420cec45b29be750bb650d0 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 12 Apr 2022 00:19:52 +0100 Subject: [PATCH 25/75] View/Filter/Calculation events --- packages/backend-core/src/events/constants.js | 6 +- .../backend-core/src/events/handlers/view.js | 16 +- .../src/tests/utilities/mocks/events.js | 9 + .../server/src/api/controllers/view/index.js | 70 +++++++- .../server/src/api/routes/tests/view.spec.js | 170 ++++++++++++++++-- 5 files changed, 241 insertions(+), 30 deletions(-) diff --git a/packages/backend-core/src/events/constants.js b/packages/backend-core/src/events/constants.js index 354f92f56b..6be537b8de 100644 --- a/packages/backend-core/src/events/constants.js +++ b/packages/backend-core/src/events/constants.js @@ -97,9 +97,11 @@ exports.Events = { VIEW_DELETED: "view:deleted", VIEW_EXPORTED: "view:exported", VIEW_FILTER_CREATED: "view:filter:created", - VIEW_FILTER_DELETED: "view:filter:created", + VIEW_FILTER_UPDATED: "view:filter:updated", + VIEW_FILTER_DELETED: "view:filter:deleted", VIEW_CALCULATION_CREATED: "view:calculation:created", - VIEW_CALCULATION_DELETED: "view:calculation:created", + VIEW_CALCULATION_UPDATED: "view:calculation:updated", + VIEW_CALCULATION_DELETED: "view:calculation:deleted", // ROW // ROW_CREATED: "row:created", diff --git a/packages/backend-core/src/events/handlers/view.js b/packages/backend-core/src/events/handlers/view.js index 785eaa0434..9c1c77ad63 100644 --- a/packages/backend-core/src/events/handlers/view.js +++ b/packages/backend-core/src/events/handlers/view.js @@ -8,13 +8,11 @@ exports.created = () => { events.processEvent(Events.VIEW_CREATED, properties) } -// TODO exports.updated = () => { const properties = {} events.processEvent(Events.VIEW_UPDATED, properties) } -// TODO exports.deleted = () => { const properties = {} events.processEvent(Events.VIEW_DELETED, properties) @@ -25,25 +23,31 @@ exports.exported = (table, format) => { events.processEvent(Events.VIEW_EXPORTED, properties) } -// TODO exports.filterCreated = () => { const properties = {} events.processEvent(Events.VIEW_FILTER_CREATED, properties) } -// TODO +exports.filterUpdated = () => { + const properties = {} + events.processEvent(Events.VIEW_FILTER_UPDATED, properties) +} + exports.filterDeleted = () => { const properties = {} events.processEvent(Events.VIEW_FILTER_DELETED, properties) } -// TODO exports.calculationCreated = () => { const properties = {} events.processEvent(Events.VIEW_CALCULATION_CREATED, properties) } -// TODO +exports.calculationUpdated = () => { + const properties = {} + events.processEvent(Events.VIEW_CALCULATION_UPDATED, properties) +} + exports.calculationDeleted = () => { const properties = {} events.processEvent(Events.VIEW_CALCULATION_DELETED, properties) diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index 1d0fdc3f8c..5c5738adab 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -106,7 +106,16 @@ jest.mock("../../../events", () => { permissionUpdated: jest.fn(), }, view: { + created: jest.fn(), + updated: jest.fn(), + deleted: jest.fn(), exported: jest.fn(), + filterCreated: jest.fn(), + filterUpdated: jest.fn(), + filterDeleted: jest.fn(), + calculationCreated: jest.fn(), + calculationUpdated: jest.fn(), + calculationDeleted: jest.fn(), }, } }) diff --git a/packages/server/src/api/controllers/view/index.js b/packages/server/src/api/controllers/view/index.js index 1174abc904..16d30d350d 100644 --- a/packages/server/src/api/controllers/view/index.js +++ b/packages/server/src/api/controllers/view/index.js @@ -8,6 +8,7 @@ const { FieldTypes } = require("../../../constants") const { getAppDB } = require("@budibase/backend-core/context") const { events } = require("@budibase/backend-core") const { DocumentTypes } = require("../../../db/utils") +const { cloneDeep, isEqual } = require("lodash") exports.fetch = async ctx => { ctx.body = await getViews() @@ -17,24 +18,28 @@ exports.save = async ctx => { const db = getAppDB() const { originalName, ...viewToSave } = ctx.request.body const view = viewTemplate(viewToSave) + const viewName = viewToSave.name - if (!viewToSave.name) { + if (!viewName) { ctx.throw(400, "Cannot create view without a name") } - await saveView(originalName, viewToSave.name, view) + await saveView(originalName, viewName, view) // add views to table document - const table = await db.get(ctx.request.body.tableId) + const existingTable = await db.get(ctx.request.body.tableId) + const table = cloneDeep(existingTable) if (!table.views) table.views = {} if (!view.meta.schema) { view.meta.schema = table.schema } - table.views[viewToSave.name] = view.meta + table.views[viewName] = view.meta if (originalName) { delete table.views[originalName] + existingTable.views[viewName] = existingTable.views[originalName] } await db.put(table) + handleViewEvents(existingTable.views[viewName], table.views[viewName]) ctx.body = { ...table.views[viewToSave.name], @@ -42,6 +47,62 @@ exports.save = async ctx => { } } +const calculationEvents = (existingView, newView) => { + const existingCalculation = existingView && existingView.calculation + const newCalculation = newView && newView.calculation + + if (existingCalculation && !newCalculation) { + events.view.calculationDeleted() + } + + if (!existingCalculation && newCalculation) { + events.view.calculationCreated() + } + + if ( + existingCalculation && + newCalculation && + existingCalculation !== newCalculation + ) { + events.view.calculationUpdated() + } +} + +const filterEvents = (existingView, newView) => { + const hasExistingFilters = !!( + existingView && + existingView.filters && + existingView.filters.length + ) + const hasNewFilters = !!(newView && newView.filters && newView.filters.length) + + if (hasExistingFilters && !hasNewFilters) { + events.view.filterDeleted() + } + + if (!hasExistingFilters && hasNewFilters) { + events.view.filterCreated() + } + + if ( + hasExistingFilters && + hasNewFilters && + !isEqual(existingView.filters, newView.filters) + ) { + events.view.filterUpdated() + } +} + +const handleViewEvents = (existingView, newView) => { + if (!existingView) { + events.view.created() + } else { + events.view.updated() + } + calculationEvents(existingView, newView) + filterEvents(existingView, newView) +} + exports.destroy = async ctx => { const db = getAppDB() const viewName = decodeURI(ctx.params.viewName) @@ -49,6 +110,7 @@ exports.destroy = async ctx => { const table = await db.get(view.meta.tableId) delete table.views[viewName] await db.put(table) + events.view.deleted() ctx.body = view } diff --git a/packages/server/src/api/routes/tests/view.spec.js b/packages/server/src/api/routes/tests/view.spec.js index cf5c81b0f1..2ea90ce32d 100644 --- a/packages/server/src/api/routes/tests/view.spec.js +++ b/packages/server/src/api/routes/tests/view.spec.js @@ -30,27 +30,70 @@ describe("/views", () => { beforeEach(async () => { await config.init() + table = await config.createTable(priceTable()) }) + const saveView = async (view) => { + const viewToSave = { + name: "TestView", + field: "Price", + calculation: "stats", + tableId: table._id, + ...view + } + return request + .post(`/api/views`) + .send(viewToSave) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + } + describe("create", () => { - beforeEach(async () => { - table = await config.createTable(priceTable()) - }) it("returns a success message when the view is successfully created", async () => { - const res = await request - .post(`/api/views`) - .send({ - name: "TestView", - field: "Price", - calculation: "stats", - tableId: table._id, - }) - .set(config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) + const res = await saveView() + expect(res.body.tableId).toBe(table._id) + expect(events.view.created).toBeCalledTimes(1) + }) + + it("creates a view with a calculation", async () => { + jest.clearAllMocks() + + const res = await saveView({ calculation: "count" }) expect(res.body.tableId).toBe(table._id) + expect(events.view.created).toBeCalledTimes(1) + expect(events.view.updated).not.toBeCalled() + expect(events.view.calculationCreated).toBeCalledTimes(1) + expect(events.view.calculationUpdated).not.toBeCalled() + expect(events.view.calculationDeleted).not.toBeCalled() + expect(events.view.filterCreated).not.toBeCalled() + expect(events.view.filterUpdated).not.toBeCalled() + expect(events.view.filterDeleted).not.toBeCalled() + }) + + it("creates a view with a filter", async () => { + jest.clearAllMocks() + + const res = await saveView({ + calculation: null, + filters: [{ + value: "1", + condition: "EQUALS", + key: "price" + }], + }) + + expect(res.body.tableId).toBe(table._id) + expect(events.view.created).toBeCalledTimes(1) + expect(events.view.updated).not.toBeCalled() + expect(events.view.calculationCreated).not.toBeCalled() + expect(events.view.calculationUpdated).not.toBeCalled() + expect(events.view.calculationDeleted).not.toBeCalled() + expect(events.view.filterCreated).toBeCalledTimes(1) + expect(events.view.filterUpdated).not.toBeCalled() + expect(events.view.filterDeleted).not.toBeCalled() }) it("updates the table row with the new view metadata", async () => { @@ -102,6 +145,100 @@ describe("/views", () => { }) }) + describe("update", () => { + it("updates a view with no calculation or filter changed", async () => { + await saveView() + jest.clearAllMocks() + + await saveView() + + expect(events.view.created).not.toBeCalled() + expect(events.view.updated).toBeCalledTimes(1) + expect(events.view.calculationCreated).not.toBeCalled() + expect(events.view.calculationUpdated).not.toBeCalled() + expect(events.view.calculationDeleted).not.toBeCalled() + expect(events.view.filterCreated).not.toBeCalled() + expect(events.view.filterUpdated).not.toBeCalled() + expect(events.view.filterDeleted).not.toBeCalled() + }) + + it("updates a view calculation", async () => { + await saveView({ calculation: "sum" }) + jest.clearAllMocks() + + await saveView({ calculation: "count" }) + + expect(events.view.created).not.toBeCalled() + expect(events.view.updated).toBeCalledTimes(1) + expect(events.view.calculationCreated).not.toBeCalled() + expect(events.view.calculationUpdated).toBeCalledTimes(1) + expect(events.view.calculationDeleted).not.toBeCalled() + expect(events.view.filterCreated).not.toBeCalled() + expect(events.view.filterUpdated).not.toBeCalled() + expect(events.view.filterDeleted).not.toBeCalled() + }) + + it("deletes a view calculation", async () => { + await saveView({ calculation: "sum" }) + jest.clearAllMocks() + + await saveView({ calculation: null }) + + expect(events.view.created).not.toBeCalled() + expect(events.view.updated).toBeCalledTimes(1) + expect(events.view.calculationCreated).not.toBeCalled() + expect(events.view.calculationUpdated).not.toBeCalled() + expect(events.view.calculationDeleted).toBeCalledTimes(1) + expect(events.view.filterCreated).not.toBeCalled() + expect(events.view.filterUpdated).not.toBeCalled() + expect(events.view.filterDeleted).not.toBeCalled() + }) + + it("updates a view filter", async () => { + await saveView({ filters: [{ + value: "1", + condition: "EQUALS", + key: "price" + }] }) + jest.clearAllMocks() + + await saveView({ filters: [{ + value: "2", + condition: "EQUALS", + key: "price" + }] }) + + expect(events.view.created).not.toBeCalled() + expect(events.view.updated).toBeCalledTimes(1) + expect(events.view.calculationCreated).not.toBeCalled() + expect(events.view.calculationUpdated).not.toBeCalled() + expect(events.view.calculationDeleted).not.toBeCalled() + expect(events.view.filterCreated).not.toBeCalled() + expect(events.view.filterUpdated).toBeCalledTimes(1) + expect(events.view.filterDeleted).not.toBeCalled() + }) + + it("deletes a view filter", async () => { + await saveView({ filters: [{ + value: "1", + condition: "EQUALS", + key: "price" + }] }) + jest.clearAllMocks() + + await saveView({ filters: [] }) + + expect(events.view.created).not.toBeCalled() + expect(events.view.updated).toBeCalledTimes(1) + expect(events.view.calculationCreated).not.toBeCalled() + expect(events.view.calculationUpdated).not.toBeCalled() + expect(events.view.calculationDeleted).not.toBeCalled() + expect(events.view.filterCreated).not.toBeCalled() + expect(events.view.filterUpdated).not.toBeCalled() + expect(events.view.filterDeleted).toBeCalledTimes(1) + }) + }) + describe("fetch", () => { beforeEach(async () => { table = await config.createTable(priceTable()) @@ -125,10 +262,6 @@ describe("/views", () => { }) describe("query", () => { - beforeEach(async () => { - table = await config.createTable(priceTable()) - }) - it("returns data for the created view", async () => { await config.createView({ name: "TestView", @@ -202,6 +335,7 @@ describe("/views", () => { .expect(200) expect(res.body.map).toBeDefined() expect(res.body.meta.tableId).toEqual(table._id) + expect(events.view.deleted).toBeCalledTimes(1) }) }) From d99f579bf61acc27297f0fd56f33d53a73ca62c2 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 12 Apr 2022 12:34:36 +0100 Subject: [PATCH 26/75] User management events --- packages/backend-core/src/events/constants.js | 7 +-- .../backend-core/src/events/handlers/user.js | 24 ++------ .../src/tests/utilities/mocks/events.js | 5 +- packages/backend-core/src/users.js | 14 ++++- .../worker/src/api/controllers/global/auth.ts | 4 ++ .../worker/src/api/controllers/global/self.js | 10 ++++ .../src/api/controllers/global/users.ts | 8 ++- .../worker/src/api/routes/tests/auth.spec.js | 42 +++++++++----- .../worker/src/api/routes/tests/self.spec.js | 58 +++++++++++++++++++ .../worker/src/api/routes/tests/users.spec.js | 44 +++++++++++--- packages/worker/src/sdk/users/events.ts | 8 +++ packages/worker/src/sdk/users/users.ts | 3 +- 12 files changed, 174 insertions(+), 53 deletions(-) create mode 100644 packages/worker/src/api/routes/tests/self.spec.js diff --git a/packages/backend-core/src/events/constants.js b/packages/backend-core/src/events/constants.js index 6be537b8de..99bd8a6ada 100644 --- a/packages/backend-core/src/events/constants.js +++ b/packages/backend-core/src/events/constants.js @@ -3,7 +3,6 @@ exports.Events = { USER_CREATED: "user:created", USER_UPDATED: "user:updated", USER_DELETED: "user:deleted", - USER_PASSWORD_FORCE_RESET: "user:password:force:reset", // USER / PERMISSIONS USER_PERMISSION_ADMIN_ASSIGNED: "user:admin:assigned", @@ -15,9 +14,9 @@ exports.Events = { USER_INVITED: "user:invited", USER_INVITED_ACCEPTED: "user:invite:accepted", - // USER / SELF - USER_SELF_UPDATED: "user:self:updated", - USER_SELF_PASSWORD_UPDATED: "user:self:password:updated", + // USER / PASSWORD + USER_PASSWORD_FORCE_RESET: "user:password:force:reset", + USER_PASSWORD_UPDATED: "user:password:updated", USER_PASSWORD_RESET_REQUESTED: "user:password:reset:requested", USER_PASSWORD_RESET: "user:password:reset", diff --git a/packages/backend-core/src/events/handlers/user.js b/packages/backend-core/src/events/handlers/user.js index 8cbb627797..ca6d541a7d 100644 --- a/packages/backend-core/src/events/handlers/user.js +++ b/packages/backend-core/src/events/handlers/user.js @@ -18,12 +18,6 @@ exports.deleted = user => { events.processEvent(Events.USER_DELETED, properties) } -// TODO -exports.passwordForceReset = user => { - const properties = {} - events.processEvent(Events.USER_PASSWORD_FORCE_RESET, properties) -} - // PERMISSIONS exports.permissionAdminAssigned = user => { @@ -48,39 +42,33 @@ exports.permissionBuilderRemoved = user => { // INVITE -// TODO -exports.invited = user => { +exports.invited = userInfo => { const properties = {} events.processEvent(Events.USER_INVITED, properties) } -// TODO exports.inviteAccepted = user => { const properties = {} events.processEvent(Events.USER_INVITED_ACCEPTED, properties) } -// SELF +// PASSWORD -// TODO -exports.selfUpdated = user => { +exports.passwordForceReset = user => { const properties = {} - events.processEvent(Events.USER_SELF_UPDATED, properties) + events.processEvent(Events.USER_PASSWORD_FORCE_RESET, properties) } -// TODO -exports.selfPasswordUpdated = user => { +exports.passwordUpdated = user => { const properties = {} - events.processEvent(Events.USER_SELF_PASSWORD_UPDATED, properties) + events.processEvent(Events.USER_PASSWORD_UPDATED, properties) } -// TODO exports.passwordResetRequested = user => { const properties = {} events.processEvent(Events.USER_PASSWORD_RESET_REQUESTED, properties) } -// TODO exports.passwordReset = user => { const properties = {} events.processEvent(Events.USER_PASSWORD_RESET, properties) diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index 5c5738adab..4ec2da5835 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -80,15 +80,14 @@ jest.mock("../../../events", () => { created: jest.fn(), updated: jest.fn(), deleted: jest.fn(), - passwordForceReset: jest.fn(), permissionAdminAssigned: jest.fn(), permissionAdminRemoved: jest.fn(), permissionBuilderAssigned: jest.fn(), permissionBuilderRemoved: jest.fn(), invited: jest.fn(), inviteAccepted: jest.fn(), - selfUpdated: jest.fn(), - selfPasswordUpdated: jest.fn(), + passwordForceReset: jest.fn(), + passwordUpdated: jest.fn(), passwordResetRequested: jest.fn(), passwordReset: jest.fn(), }, diff --git a/packages/backend-core/src/users.js b/packages/backend-core/src/users.js index 07a60f2884..7f18205c2b 100644 --- a/packages/backend-core/src/users.js +++ b/packages/backend-core/src/users.js @@ -12,8 +12,20 @@ exports.getGlobalUserByEmail = async email => { throw "Must supply an email address to view" } - return queryGlobalView(ViewNames.USER_BY_EMAIL, { + const response = await queryGlobalView(ViewNames.USER_BY_EMAIL, { key: email.toLowerCase(), include_docs: true, }) + + if (response) { + if (Array.isArray(response)) { + for (let user of response) { + delete user.password + } + } else { + delete response.password + } + } + + return response } diff --git a/packages/worker/src/api/controllers/global/auth.ts b/packages/worker/src/api/controllers/global/auth.ts index bbd35f96d0..247a45c0a3 100644 --- a/packages/worker/src/api/controllers/global/auth.ts +++ b/packages/worker/src/api/controllers/global/auth.ts @@ -112,6 +112,7 @@ export const reset = async (ctx: any) => { user, subject: "{{ company }} platform password reset", }) + events.user.passwordResetRequested(user) } } catch (err) { console.log(err) @@ -136,6 +137,9 @@ export const resetUpdate = async (ctx: any) => { ctx.body = { message: "password reset successfully.", } + // remove password from the user before sending events + delete user.password + events.user.passwordReset(user) } catch (err) { ctx.throw(400, "Cannot reset password.") } diff --git a/packages/worker/src/api/controllers/global/self.js b/packages/worker/src/api/controllers/global/self.js index fd615ba795..1c85c511cb 100644 --- a/packages/worker/src/api/controllers/global/self.js +++ b/packages/worker/src/api/controllers/global/self.js @@ -15,6 +15,7 @@ const { encrypt } = require("@budibase/backend-core/encryption") const { newid } = require("@budibase/backend-core/utils") const { users } = require("../../../sdk") const { Cookies } = require("@budibase/backend-core/constants") +const { events } = require("@budibase/backend-core") function newApiKey() { return encrypt(`${getTenantId()}${SEPARATOR}${newid()}`) @@ -110,8 +111,10 @@ exports.getSelf = async ctx => { exports.updateSelf = async ctx => { const db = getGlobalDB() const user = await db.get(ctx.user._id) + let passwordChange = false if (ctx.request.body.password) { // changing password + passwordChange = true ctx.request.body.password = await hash(ctx.request.body.password) // Log all other sessions out apart from the current one await platformLogout({ @@ -134,4 +137,11 @@ exports.updateSelf = async ctx => { _id: response.id, _rev: response.rev, } + + // remove the old password from the user before sending events + delete user.password + events.user.updated(user) + if (passwordChange) { + events.user.passwordUpdated(user) + } } diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index 9f5bf08155..5f38b659d2 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -8,6 +8,7 @@ const { users: usersCore, tenancy, db: dbUtils, + events, } = require("@budibase/backend-core") export const save = async (ctx: any) => { @@ -121,6 +122,7 @@ export const invite = async (ctx: any) => { ctx.body = { message: "Invitation has been sent.", } + events.user.invited(userInfo) } export const inviteAccept = async (ctx: any) => { @@ -128,14 +130,16 @@ export const inviteAccept = async (ctx: any) => { try { // info is an extension of the user object that was stored by global const { email, info }: any = await checkInviteCode(inviteCode) - ctx.body = await tenancy.doInTenant(info.tenantId, () => { - return users.save({ + ctx.body = await tenancy.doInTenant(info.tenantId, async () => { + const user = await users.save({ firstName, lastName, password, email, ...info, }) + events.user.inviteAccepted(user) + return user }) } catch (err: any) { if (err.code === errors.codes.USAGE_LIMIT_EXCEEDED) { diff --git a/packages/worker/src/api/routes/tests/auth.spec.js b/packages/worker/src/api/routes/tests/auth.spec.js index 9426b9f779..1c33ea750a 100644 --- a/packages/worker/src/api/routes/tests/auth.spec.js +++ b/packages/worker/src/api/routes/tests/auth.spec.js @@ -6,7 +6,6 @@ const { events } = require("@budibase/backend-core") const TENANT_ID = structures.TENANT_ID describe("/api/global/auth", () => { - let code beforeAll(async () => { await config.beforeAll() @@ -20,16 +19,7 @@ describe("/api/global/auth", () => { jest.clearAllMocks() }) - it("should logout", async () => { - await request - .post("/api/global/auth/logout") - .set(config.defaultHeaders()) - .expect(200) - expect(events.auth.logout.mock.calls.length).toBe(1) - }) - - it("should be able to generate password reset email", async () => { - // initially configure settings + const requestPasswordReset = async () => { await config.saveSmtpConfig() await config.saveSettingsConfig() await config.createUser() @@ -40,16 +30,36 @@ describe("/api/global/auth", () => { }) .expect("Content-Type", /json/) .expect(200) + const emailCall = sendMailMock.mock.calls[0][0] + const parts = emailCall.html.split(`http://localhost:10000/builder/auth/reset?code=`) + const code = parts[1].split("\"")[0].split("&")[0] + return { code, res } + } + + it("should logout", async () => { + await request + .post("/api/global/auth/logout") + .set(config.defaultHeaders()) + .expect(200) + expect(events.auth.logout.mock.calls.length).toBe(1) + }) + + it("should be able to generate password reset email", async () => { + const { res, code } = await requestPasswordReset() + const user = await config.getUser("test@test.com") + expect(res.body).toEqual({ message: "Please check your email for a reset link." }) expect(sendMailMock).toHaveBeenCalled() - const emailCall = sendMailMock.mock.calls[0][0] - // after this URL there should be a code - const parts = emailCall.html.split(`http://localhost:10000/builder/auth/reset?code=`) - code = parts[1].split("\"")[0].split("&")[0] + expect(code).toBeDefined() + expect(events.user.passwordResetRequested).toBeCalledTimes(1) + expect(events.user.passwordResetRequested).toBeCalledWith(user) }) it("should allow resetting user password with code", async () => { + const { code } = await requestPasswordReset() + const user = await config.getUser("test@test.com") + const res = await request .post(`/api/global/auth/${TENANT_ID}/reset/update`) .send({ @@ -59,6 +69,8 @@ describe("/api/global/auth", () => { .expect("Content-Type", /json/) .expect(200) expect(res.body).toEqual({ message: "password reset successfully." }) + expect(events.user.passwordReset).toBeCalledTimes(1) + expect(events.user.passwordReset).toBeCalledWith(user) }) describe("oidc", () => { diff --git a/packages/worker/src/api/routes/tests/self.spec.js b/packages/worker/src/api/routes/tests/self.spec.js new file mode 100644 index 0000000000..471dbff98d --- /dev/null +++ b/packages/worker/src/api/routes/tests/self.spec.js @@ -0,0 +1,58 @@ +jest.mock("nodemailer") +const { config, request } = require("../../../tests") +const { events, utils } = require("@budibase/backend-core") + +describe("/api/global/self", () => { + + beforeAll(async () => { + await config.beforeAll() + }) + + afterAll(async () => { + await config.afterAll() + }) + + beforeEach(() => { + jest.clearAllMocks() + }) + + const updateSelf = async (user) => { + const res = await request + .post(`/api/global/self`) + .send(user) + .set(config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + return res + } + + describe("update", () => { + + it("should update self", async () => { + const user = await config.createUser() + + const res = await updateSelf(user) + + delete user.password + expect(res.body._id).toBe(user._id) + expect(events.user.updated).toBeCalledTimes(1) + expect(events.user.updated).toBeCalledWith(user) + expect(events.user.passwordUpdated).not.toBeCalled() + }) + + it("should update password", async () => { + const user = await config.createUser() + const password = "newPassword" + user.password = password + + const res = await updateSelf(user) + + delete user.password + expect(res.body._id).toBe(user._id) + expect(events.user.updated).toBeCalledTimes(1) + expect(events.user.updated).toBeCalledWith(user) + expect(events.user.passwordUpdated).toBeCalledTimes(1) + expect(events.user.passwordUpdated).toBeCalledWith(user) + }) + }) +}) \ No newline at end of file diff --git a/packages/worker/src/api/routes/tests/users.spec.js b/packages/worker/src/api/routes/tests/users.spec.js index 4d94aa0148..cf4426665b 100644 --- a/packages/worker/src/api/routes/tests/users.spec.js +++ b/packages/worker/src/api/routes/tests/users.spec.js @@ -4,7 +4,6 @@ const sendMailMock = mocks.email.mock() const { events } = require("@budibase/backend-core") describe("/api/global/users", () => { - let code beforeAll(async () => { await config.beforeAll() @@ -14,8 +13,7 @@ describe("/api/global/users", () => { await config.afterAll() }) - it("should be able to generate an invitation", async () => { - // initially configure settings + const sendUserInvite = async () => { await config.saveSmtpConfig() await config.saveSettingsConfig() const res = await request @@ -26,16 +24,27 @@ describe("/api/global/users", () => { .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) + + const emailCall = sendMailMock.mock.calls[0][0] + // after this URL there should be a code + const parts = emailCall.html.split("http://localhost:10000/builder/invite?code=") + const code = parts[1].split("\"")[0].split("&")[0] + return { code, res } + } + + it("should be able to generate an invitation", async () => { + const { code, res } = await sendUserInvite() + expect(res.body).toEqual({ message: "Invitation has been sent." }) expect(sendMailMock).toHaveBeenCalled() - const emailCall = sendMailMock.mock.calls[0][0] - // after this URL there should be a code - const parts = emailCall.html.split("http://localhost:10000/builder/invite?code=") - code = parts[1].split("\"")[0].split("&")[0] expect(code).toBeDefined() + expect(events.user.invited).toBeCalledTimes(1) + expect(events.user.invited).toBeCalledWith({ tenantId: structures.TENANT_ID }) }) it("should be able to create new user from invite", async () => { + const { code } = await sendUserInvite() + const res = await request .post(`/api/global/users/invite/accept`) .send({ @@ -48,6 +57,8 @@ describe("/api/global/users", () => { const user = await config.getUser("invite@test.com") expect(user).toBeDefined() expect(user._id).toEqual(res.body._id) + expect(events.user.inviteAccepted).toBeCalledTimes(1) + expect(events.user.inviteAccepted).toBeCalledWith(res.body) }) const createUser = async (user) => { @@ -70,7 +81,7 @@ describe("/api/global/users", () => { .send(user) .set(config.defaultHeaders()) .expect("Content-Type", /json/) - // .expect(200) + .expect(200) return res.body } @@ -149,6 +160,23 @@ describe("/api/global/users", () => { expect(events.user.updated).toBeCalledTimes(1) expect(events.user.permissionBuilderAssigned).not.toBeCalled() expect(events.user.permissionAdminAssigned).not.toBeCalled() + expect(events.user.passwordForceReset).not.toBeCalled() + }) + + it("should be able to force reset password", async () => { + let user = structures.users.user({ email: "basic-password-update@test.com" }) + await createUser(user) + jest.clearAllMocks() + + user.forceResetPassword = true + user.password = "tempPassword" + await updateUser(user) + + expect(events.user.created).not.toBeCalled() + expect(events.user.updated).toBeCalledTimes(1) + expect(events.user.permissionBuilderAssigned).not.toBeCalled() + expect(events.user.permissionAdminAssigned).not.toBeCalled() + expect(events.user.passwordForceReset).toBeCalledTimes(1) }) it("should be able to update a basic user to an admin user", async () => { diff --git a/packages/worker/src/sdk/users/events.ts b/packages/worker/src/sdk/users/events.ts index adebfbff46..98fb68efda 100644 --- a/packages/worker/src/sdk/users/events.ts +++ b/packages/worker/src/sdk/users/events.ts @@ -52,6 +52,14 @@ export const handleSaveEvents = (user: any, existingUser: any) => { if (isRemovingAdmin(user, existingUser)) { events.user.permissionAdminRemoved(user) } + + if ( + !existingUser.forceResetPassword && + user.forceResetPassword && + user.password + ) { + events.user.passwordForceReset(user) + } } else { events.user.created(user) } diff --git a/packages/worker/src/sdk/users/users.ts b/packages/worker/src/sdk/users/users.ts index dbaee8baaa..bf871b732b 100644 --- a/packages/worker/src/sdk/users/users.ts +++ b/packages/worker/src/sdk/users/users.ts @@ -1,7 +1,6 @@ import env from "../../environment" import { quotas } from "@budibase/pro" import * as apps from "../../utilities/appService" -const { events } = require("@budibase/backend-core") import * as eventHelpers from "./events" const { @@ -125,7 +124,7 @@ export const save = async ( const putUserFn = () => { return db.put(user) } - if (await eventHelpers.isAddingBuilder(user, dbUser)) { + if (eventHelpers.isAddingBuilder(user, dbUser)) { response = await quotas.addDeveloper(putUserFn) } else { response = await putUserFn() From a6107dc7f97c068c49216df91b2886c98f6515bf Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 12 Apr 2022 15:37:49 +0100 Subject: [PATCH 27/75] Test updates --- packages/backend-core/src/tests/utils.spec.js | 2 +- .../src/api/routes/tests/application.spec.js | 20 +++++++++---------- .../src/api/routes/tests/datasource.spec.js | 6 +++--- .../worker/src/api/routes/tests/auth.spec.js | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/backend-core/src/tests/utils.spec.js b/packages/backend-core/src/tests/utils.spec.js index a1415a21b9..bb0f24fad6 100644 --- a/packages/backend-core/src/tests/utils.spec.js +++ b/packages/backend-core/src/tests/utils.spec.js @@ -7,7 +7,7 @@ describe("utils", () => { it("should call platform logout", async () => { const ctx = structures.koa.newContext() await utils.platformLogout({ ctx, userId: "test" }) - expect(events.auth.logout.mock.calls.length).toBe(1) + expect(events.auth.logout).toBeCalledTimes(1) }) }) }) \ No newline at end of file diff --git a/packages/server/src/api/routes/tests/application.spec.js b/packages/server/src/api/routes/tests/application.spec.js index 4bf6aa8282..a0fca7a6c8 100644 --- a/packages/server/src/api/routes/tests/application.spec.js +++ b/packages/server/src/api/routes/tests/application.spec.js @@ -41,7 +41,7 @@ describe("/applications", () => { .expect("Content-Type", /json/) .expect(200) expect(res.body._id).toBeDefined() - expect(events.app.created.mock.calls.length).toBe(1) + expect(events.app.created).toBeCalledTimes(1) }) it("creates app from template", async () => { @@ -55,8 +55,8 @@ describe("/applications", () => { .expect("Content-Type", /json/) .expect(200) expect(res.body._id).toBeDefined() - expect(events.app.created.mock.calls.length).toBe(1) - expect(events.app.templateImported.mock.calls.length).toBe(1) + expect(events.app.created).toBeCalledTimes(1) + expect(events.app.templateImported).toBeCalledTimes(1) }) @@ -70,8 +70,8 @@ describe("/applications", () => { .expect("Content-Type", /json/) .expect(200) expect(res.body._id).toBeDefined() - expect(events.app.created.mock.calls.length).toBe(1) - expect(events.app.fileImported.mock.calls.length).toBe(1) + expect(events.app.created).toBeCalledTimes(1) + expect(events.app.fileImported).toBeCalledTimes(1) }) it("should apply authorization to endpoint", async () => { @@ -135,7 +135,7 @@ describe("/applications", () => { .expect("Content-Type", /json/) .expect(200) expect(res.body.rev).toBeDefined() - expect(events.app.updated.mock.calls.length).toBe(1) + expect(events.app.updated).toBeCalledTimes(1) }) }) @@ -148,7 +148,7 @@ describe("/applications", () => { .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) - expect(events.app.deleted.mock.calls.length).toBe(1) + expect(events.app.deleted).toBeCalledTimes(1) }) it("should unpublish app", async () => { @@ -159,7 +159,7 @@ describe("/applications", () => { .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) - expect(events.app.unpublished.mock.calls.length).toBe(1) + expect(events.app.unpublished).toBeCalledTimes(1) }) }) @@ -171,7 +171,7 @@ describe("/applications", () => { .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) - expect(events.app.versionUpdated.mock.calls.length).toBe(1) + expect(events.app.versionUpdated).toBeCalledTimes(1) }) it("should be able to revert the app client library version", async () => { // We need to first update the version so that we can then revert @@ -185,7 +185,7 @@ describe("/applications", () => { .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) - expect(events.app.versionReverted.mock.calls.length).toBe(1) + expect(events.app.versionReverted).toBeCalledTimes(1) }) }) diff --git a/packages/server/src/api/routes/tests/datasource.spec.js b/packages/server/src/api/routes/tests/datasource.spec.js index bc7e6cf4af..520a17015c 100644 --- a/packages/server/src/api/routes/tests/datasource.spec.js +++ b/packages/server/src/api/routes/tests/datasource.spec.js @@ -31,7 +31,7 @@ describe("/datasources", () => { expect(res.body.datasource.name).toEqual("Test") expect(res.body.errors).toBeUndefined() - expect(events.datasource.created.mock.calls.length).toBe(1) + expect(events.datasource.created).toBeCalledTimes(1) }) }) @@ -47,7 +47,7 @@ describe("/datasources", () => { expect(res.body.datasource.name).toEqual("Updated Test") expect(res.body.errors).toBeUndefined() - expect(events.datasource.updated.mock.calls.length).toBe(1) + expect(events.datasource.updated).toBeCalledTimes(1) }) describe("dynamic variables", () => { @@ -164,7 +164,7 @@ describe("/datasources", () => { .expect(200) expect(res.body.length).toEqual(1) - expect(events.datasource.deleted.mock.calls.length).toBe(1) + expect(events.datasource.deleted).toBeCalledTimes(1) }) it("should apply authorization to endpoint", async () => { diff --git a/packages/worker/src/api/routes/tests/auth.spec.js b/packages/worker/src/api/routes/tests/auth.spec.js index 1c33ea750a..0d3b228827 100644 --- a/packages/worker/src/api/routes/tests/auth.spec.js +++ b/packages/worker/src/api/routes/tests/auth.spec.js @@ -41,7 +41,7 @@ describe("/api/global/auth", () => { .post("/api/global/auth/logout") .set(config.defaultHeaders()) .expect(200) - expect(events.auth.logout.mock.calls.length).toBe(1) + expect(events.auth.logout).toBeCalledTimes(1) }) it("should be able to generate password reset email", async () => { From fd6a223ad87c2233025c22d17d9ffb368a1aaf51 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 12 Apr 2022 16:03:20 +0100 Subject: [PATCH 28/75] Test updates --- packages/server/src/api/routes/tests/row.spec.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.js b/packages/server/src/api/routes/tests/row.spec.js index 0a1481520d..281b7ef4e2 100644 --- a/packages/server/src/api/routes/tests/row.spec.js +++ b/packages/server/src/api/routes/tests/row.spec.js @@ -2,7 +2,6 @@ const { outputProcessing } = require("../../../utilities/rowProcessor") const setup = require("./utilities") const { basicRow } = setup.structures const { doInAppContext } = require("@budibase/backend-core/context") -const { testUtils } = require("@budibase/backend-core") describe("/rows", () => { let request = setup.getRequest() @@ -67,15 +66,13 @@ describe("/rows", () => { .expect('Content-Type', /json/) .expect(200) - // can't mock dates due to coercion test requiring real Date clas - delete res.body.createdAt - delete res.body.updatedAt - expect(res.body).toEqual({ ...row, _id: existing._id, _rev: existing._rev, type: "row", + createdAt: "2020-01-01T00:00:00.000Z", + updatedAt: "2020-01-01T00:00:00.000Z", }) }) From 0d084d9c039a1b9b5610598f8efd1dad16e039ec Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 12 Apr 2022 17:23:30 +0100 Subject: [PATCH 29/75] Replace analytics with bbtel in client error exclusion --- packages/client/src/api/api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/api/api.js b/packages/client/src/api/api.js index 26e92d2018..6c83633a0d 100644 --- a/packages/client/src/api/api.js +++ b/packages/client/src/api/api.js @@ -40,7 +40,7 @@ export const API = createAPIClient({ // Don't notify if the URL contains the word analytics as it may be // blocked by browser extensions // todo: this can be removed now? - if (!url?.includes("analytics")) { + if (!url?.includes("bbtel")) { notificationStore.actions.error(message) } } From cfa75a8af3bc81073c3df5fdd9f9e7b4240e12bc Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Thu, 28 Apr 2022 22:39:21 +0100 Subject: [PATCH 30/75] backend-core typescript building successfully --- packages/backend-core/package.json | 6 + .../src/db/{Replication.js => Replication.ts} | 18 +- .../src/db/{utils.js => utils.ts} | 158 ++++++++---------- .../backend-core/src/objectStore/index.js | 26 +-- packages/backend-core/tsconfig.build.json | 10 ++ packages/backend-core/tsconfig.json | 35 ++++ packages/backend-core/yarn.lock | 70 ++++++++ packages/server/src/module.d.ts | 2 +- packages/server/yarn.lock | 18 +- packages/worker/yarn.lock | 18 +- 10 files changed, 234 insertions(+), 127 deletions(-) rename packages/backend-core/src/db/{Replication.js => Replication.ts} (81%) rename packages/backend-core/src/db/{utils.js => utils.ts} (73%) create mode 100644 packages/backend-core/tsconfig.build.json create mode 100644 packages/backend-core/tsconfig.json diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 465b98b07f..18dded7a2d 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -3,9 +3,11 @@ "version": "1.0.126-alpha.0", "description": "Budibase backend core libraries used in server and worker", "main": "src/index.js", + "types": "dist/src/index.d.ts", "author": "Budibase", "license": "GPL-3.0", "scripts": { + "build": "rimraf dist/ && tsc -p tsconfig.build.json", "test": "jest", "test:watch": "jest --watchAll" }, @@ -39,6 +41,10 @@ ] }, "devDependencies": { + "@types/jest": "^27.4.1", + "@types/node": "^15.12.4", + "@types/node-fetch": "^2.6.1", + "typescript": "^4.5.5", "ioredis-mock": "^5.5.5", "jest": "^26.6.3", "pouchdb-adapter-memory": "^7.2.2", diff --git a/packages/backend-core/src/db/Replication.js b/packages/backend-core/src/db/Replication.ts similarity index 81% rename from packages/backend-core/src/db/Replication.js rename to packages/backend-core/src/db/Replication.ts index 437d07e536..b46f6072be 100644 --- a/packages/backend-core/src/db/Replication.js +++ b/packages/backend-core/src/db/Replication.ts @@ -1,12 +1,16 @@ -const { dangerousGetDB, closeDB } = require(".") +import { dangerousGetDB, closeDB } from "." class Replication { + source: any + target: any + replication: any + /** * * @param {String} source - the DB you want to replicate or rollback to * @param {String} target - the DB you want to replicate to, or rollback from */ - constructor({ source, target }) { + constructor({ source, target }: any) { this.source = dangerousGetDB(source) this.target = dangerousGetDB(target) } @@ -15,17 +19,17 @@ class Replication { return Promise.all([closeDB(this.source), closeDB(this.target)]) } - promisify(operation, opts = {}) { + promisify(operation: any, opts = {}) { return new Promise(resolve => { operation(this.target, opts) - .on("denied", function (err) { + .on("denied", function (err: any) { // a document failed to replicate (e.g. due to permissions) throw new Error(`Denied: Document failed to replicate ${err}`) }) - .on("complete", function (info) { + .on("complete", function (info: any) { return resolve(info) }) - .on("error", function (err) { + .on("error", function (err: any) { throw new Error(`Replication Error: ${err}`) }) }) @@ -64,4 +68,4 @@ class Replication { } } -module.exports = Replication +export default Replication diff --git a/packages/backend-core/src/db/utils.js b/packages/backend-core/src/db/utils.ts similarity index 73% rename from packages/backend-core/src/db/utils.js rename to packages/backend-core/src/db/utils.ts index 9e2a06d065..fe5bfe52e3 100644 --- a/packages/backend-core/src/db/utils.js +++ b/packages/backend-core/src/db/utils.ts @@ -1,47 +1,26 @@ -const { newid } = require("../hashing") -const Replication = require("./Replication") -const { DEFAULT_TENANT_ID, Configs } = require("../constants") -const env = require("../environment") -const { - StaticDatabases, - SEPARATOR, - DocumentTypes, - APP_PREFIX, - APP_DEV, -} = require("./constants") -const { getTenantId, getGlobalDBName } = require("../tenancy") -const fetch = require("node-fetch") -const { doWithDB, allDbs } = require("./index") -const { getCouchUrl } = require("./pouch") -const { getAppMetadata } = require("../cache/appMetadata") -const { checkSlashesInUrl } = require("../helpers") -const { - isDevApp, - isProdAppID, - isDevAppID, - getDevelopmentAppID, - getProdAppID, -} = require("./conversions") +import { newid } from "../hashing" +import { DEFAULT_TENANT_ID, Configs } from "../constants" +import * as env from "../environment" +import { SEPARATOR, DocumentTypes } from "./constants" +import { getTenantId, getGlobalDBName } from "../tenancy" +import fetch from "node-fetch" +import { doWithDB, allDbs } from "./index" +import { getCouchUrl } from "./pouch" +import { getAppMetadata } from "../cache/appMetadata" +import { checkSlashesInUrl } from "../helpers" +import { isDevApp, isDevAppID } from "./conversions" const UNICODE_MAX = "\ufff0" -exports.ViewNames = { +export const ViewNames = { USER_BY_EMAIL: "by_email", BY_API_KEY: "by_api_key", USER_BY_BUILDERS: "by_builders", } -exports.StaticDatabases = StaticDatabases - -exports.DocumentTypes = DocumentTypes -exports.APP_PREFIX = APP_PREFIX -exports.APP_DEV = exports.APP_DEV_PREFIX = APP_DEV -exports.SEPARATOR = SEPARATOR -exports.isDevApp = isDevApp -exports.isProdAppID = isProdAppID -exports.isDevAppID = isDevAppID -exports.getDevelopmentAppID = getDevelopmentAppID -exports.getProdAppID = getProdAppID +export * from "./constants" +export * from "./conversions" +export { default as Replication } from "./Replication" /** * If creating DB allDocs/query params with only a single top level ID this can be used, this @@ -55,7 +34,11 @@ exports.getProdAppID = getProdAppID * @param {object} otherProps Add any other properties onto the request, e.g. include_docs. * @returns {object} Parameters which can then be used with an allDocs request. */ -function getDocParams(docType, docId = null, otherProps = {}) { +export function getDocParams( + docType: any, + docId: any = null, + otherProps: any = {} +) { if (docId == null) { docId = "" } @@ -65,20 +48,19 @@ function getDocParams(docType, docId = null, otherProps = {}) { endkey: `${docType}${SEPARATOR}${docId}${UNICODE_MAX}`, } } -exports.getDocParams = getDocParams /** * Generates a new workspace ID. * @returns {string} The new workspace ID which the workspace doc can be stored under. */ -exports.generateWorkspaceID = () => { +export function generateWorkspaceID() { return `${DocumentTypes.WORKSPACE}${SEPARATOR}${newid()}` } /** * Gets parameters for retrieving workspaces. */ -exports.getWorkspaceParams = (id = "", otherProps = {}) => { +export function getWorkspaceParams(id = "", otherProps = {}) { return { ...otherProps, startkey: `${DocumentTypes.WORKSPACE}${SEPARATOR}${id}`, @@ -90,14 +72,14 @@ exports.getWorkspaceParams = (id = "", otherProps = {}) => { * Generates a new global user ID. * @returns {string} The new user ID which the user doc can be stored under. */ -exports.generateGlobalUserID = id => { +export function generateGlobalUserID(id: any) { return `${DocumentTypes.USER}${SEPARATOR}${id || newid()}` } /** * Gets parameters for retrieving users. */ -exports.getGlobalUserParams = (globalId, otherProps = {}) => { +export function getGlobalUserParams(globalId: any, otherProps = {}) { if (!globalId) { globalId = "" } @@ -112,14 +94,18 @@ exports.getGlobalUserParams = (globalId, otherProps = {}) => { * Generates a template ID. * @param ownerId The owner/user of the template, this could be global or a workspace level. */ -exports.generateTemplateID = ownerId => { +export function generateTemplateID(ownerId: any) { return `${DocumentTypes.TEMPLATE}${SEPARATOR}${ownerId}${SEPARATOR}${newid()}` } /** * Gets parameters for retrieving templates. Owner ID must be specified, either global or a workspace level. */ -exports.getTemplateParams = (ownerId, templateId, otherProps = {}) => { +export function getTemplateParams( + ownerId: any, + templateId: any, + otherProps = {} +) { if (!templateId) { templateId = "" } @@ -140,18 +126,18 @@ exports.getTemplateParams = (ownerId, templateId, otherProps = {}) => { * Generates a new role ID. * @returns {string} The new role ID which the role doc can be stored under. */ -exports.generateRoleID = id => { +export function generateRoleID(id: any) { return `${DocumentTypes.ROLE}${SEPARATOR}${id || newid()}` } /** * Gets parameters for retrieving a role, this is a utility function for the getDocParams function. */ -exports.getRoleParams = (roleId = null, otherProps = {}) => { +export function getRoleParams(roleId = null, otherProps = {}) { return getDocParams(DocumentTypes.ROLE, roleId, otherProps) } -exports.getStartEndKeyURL = (base, baseKey, tenantId = null) => { +export function getStartEndKeyURL(base: any, baseKey: any, tenantId = null) { const tenancy = tenantId ? `${SEPARATOR}${tenantId}` : "" return `${base}?startkey="${baseKey}${tenancy}"&endkey="${baseKey}${tenancy}${UNICODE_MAX}"` } @@ -162,14 +148,14 @@ exports.getStartEndKeyURL = (base, baseKey, tenantId = null) => { * opts.efficient can be provided to make sure this call is always quick in a multi-tenant environment, * but it may not be 100% accurate in full efficiency mode (some tenantless apps may be missed). */ -exports.getAllDbs = async (opts = { efficient: false }) => { +export async function getAllDbs(opts = { efficient: false }) { const efficient = opts && opts.efficient // specifically for testing we use the pouch package for this if (env.isTest()) { return allDbs() } - let dbs = [] - async function addDbs(url) { + let dbs: any = [] + async function addDbs(url: any) { const response = await fetch(checkSlashesInUrl(encodeURI(url))) if (response.status === 200) { let json = await response.json() @@ -189,13 +175,9 @@ exports.getAllDbs = async (opts = { efficient: false }) => { await addDbs(couchUrl) } else { // get prod apps - await addDbs( - exports.getStartEndKeyURL(couchUrl, DocumentTypes.APP, tenantId) - ) + await addDbs(getStartEndKeyURL(couchUrl, DocumentTypes.APP, tenantId)) // get dev apps - await addDbs( - exports.getStartEndKeyURL(couchUrl, DocumentTypes.APP_DEV, tenantId) - ) + await addDbs(getStartEndKeyURL(couchUrl, DocumentTypes.APP_DEV, tenantId)) // add global db name dbs.push(getGlobalDBName(tenantId)) } @@ -208,13 +190,13 @@ exports.getAllDbs = async (opts = { efficient: false }) => { * * @return {Promise} returns the app information document stored in each app database. */ -exports.getAllApps = async ({ dev, all, idsOnly, efficient } = {}) => { +export async function getAllApps({ dev, all, idsOnly, efficient }: any = {}) { let tenantId = getTenantId() if (!env.MULTI_TENANCY && !tenantId) { tenantId = DEFAULT_TENANT_ID } - let dbs = await exports.getAllDbs({ efficient }) - const appDbNames = dbs.filter(dbName => { + let dbs = await getAllDbs({ efficient }) + const appDbNames = dbs.filter((dbName: any) => { const split = dbName.split(SEPARATOR) // it is an app, check the tenantId if (split[0] === DocumentTypes.APP) { @@ -234,7 +216,7 @@ exports.getAllApps = async ({ dev, all, idsOnly, efficient } = {}) => { if (idsOnly) { return appDbNames } - const appPromises = appDbNames.map(app => + const appPromises = appDbNames.map((app: any) => // skip setup otherwise databases could be re-created getAppMetadata(app) ) @@ -243,17 +225,19 @@ exports.getAllApps = async ({ dev, all, idsOnly, efficient } = {}) => { } else { const response = await Promise.allSettled(appPromises) const apps = response - .filter(result => result.status === "fulfilled" && result.value != null) - .map(({ value }) => value) + .filter( + (result: any) => result.status === "fulfilled" && result.value != null + ) + .map(({ value }: any) => value) if (!all) { - return apps.filter(app => { + return apps.filter((app: any) => { if (dev) { return isDevApp(app) } return !isDevApp(app) }) } else { - return apps.map(app => ({ + return apps.map((app: any) => ({ ...app, status: isDevApp(app) ? "development" : "published", })) @@ -264,26 +248,26 @@ exports.getAllApps = async ({ dev, all, idsOnly, efficient } = {}) => { /** * Utility function for getAllApps but filters to production apps only. */ -exports.getProdAppIDs = async () => { - return (await exports.getAllApps({ idsOnly: true })).filter( - id => !exports.isDevAppID(id) +export async function getProdAppIDs() { + return (await getAllApps({ idsOnly: true })).filter( + (id: any) => !isDevAppID(id) ) } /** * Utility function for the inverse of above. */ -exports.getDevAppIDs = async () => { - return (await exports.getAllApps({ idsOnly: true })).filter(id => - exports.isDevAppID(id) +export async function getDevAppIDs() { + return (await getAllApps({ idsOnly: true })).filter((id: any) => + isDevAppID(id) ) } -exports.dbExists = async dbName => { +export async function dbExists(dbName: any) { let exists = false return doWithDB( dbName, - async db => { + async (db: any) => { try { // check if database exists const info = await db.info() @@ -303,7 +287,7 @@ exports.dbExists = async dbName => { * Generates a new configuration ID. * @returns {string} The new configuration ID which the config doc can be stored under. */ -const generateConfigID = ({ type, workspace, user }) => { +export const generateConfigID = ({ type, workspace, user }: any) => { const scope = [type, workspace, user].filter(Boolean).join(SEPARATOR) return `${DocumentTypes.CONFIG}${SEPARATOR}${scope}` @@ -312,7 +296,10 @@ const generateConfigID = ({ type, workspace, user }) => { /** * Gets parameters for retrieving configurations. */ -const getConfigParams = ({ type, workspace, user }, otherProps = {}) => { +export const getConfigParams = ( + { type, workspace, user }: any, + otherProps = {} +) => { const scope = [type, workspace, user].filter(Boolean).join(SEPARATOR) return { @@ -326,7 +313,7 @@ const getConfigParams = ({ type, workspace, user }, otherProps = {}) => { * Generates a new dev info document ID - this is scoped to a user. * @returns {string} The new dev info ID which info for dev (like api key) can be stored under. */ -const generateDevInfoID = userId => { +export const generateDevInfoID = (userId: any) => { return `${DocumentTypes.DEV_INFO}${SEPARATOR}${userId}` } @@ -336,7 +323,10 @@ const generateDevInfoID = userId => { * @param {Object} scopes - the type, workspace and userID scopes of the configuration. * @returns The most granular configuration document based on the scope. */ -const getScopedFullConfig = async function (db, { type, user, workspace }) { +export const getScopedFullConfig = async function ( + db: any, + { type, user, workspace }: any +) { const response = await db.allDocs( getConfigParams( { type, user, workspace }, @@ -346,7 +336,7 @@ const getScopedFullConfig = async function (db, { type, user, workspace }) { ) ) - function determineScore(row) { + function determineScore(row: any) { const config = row.doc // Config is specific to a user and a workspace @@ -367,7 +357,7 @@ const getScopedFullConfig = async function (db, { type, user, workspace }) { // Find the config with the most granular scope based on context let scopedConfig = response.rows.sort( - (a, b) => determineScore(a) - determineScore(b) + (a: any, b: any) => determineScore(a) - determineScore(b) )[0] // custom logic for settings doc @@ -391,7 +381,7 @@ const getScopedFullConfig = async function (db, { type, user, workspace }) { return scopedConfig && scopedConfig.doc } -const getPlatformUrl = async settings => { +export const getPlatformUrl = async (settings?: any) => { let platformUrl = env.PLATFORM_URL || "http://localhost:10000" if (!env.SELF_HOSTED && env.MULTI_TENANCY) { @@ -410,15 +400,7 @@ const getPlatformUrl = async settings => { return platformUrl } -async function getScopedConfig(db, params) { +export async function getScopedConfig(db: any, params: any) { const configDoc = await getScopedFullConfig(db, params) return configDoc && configDoc.config ? configDoc.config : configDoc } - -exports.Replication = Replication -exports.getScopedConfig = getScopedConfig -exports.generateConfigID = generateConfigID -exports.getConfigParams = getConfigParams -exports.getScopedFullConfig = getScopedFullConfig -exports.generateDevInfoID = generateDevInfoID -exports.getPlatformUrl = getPlatformUrl diff --git a/packages/backend-core/src/objectStore/index.js b/packages/backend-core/src/objectStore/index.js index 2385149f4d..e4addfcf0b 100644 --- a/packages/backend-core/src/objectStore/index.js +++ b/packages/backend-core/src/objectStore/index.js @@ -1,16 +1,16 @@ -const sanitize = require("sanitize-s3-objectkey") -const AWS = require("aws-sdk") -const stream = require("stream") -const fetch = require("node-fetch") -const tar = require("tar-fs") -const zlib = require("zlib") -const { promisify } = require("util") -const { join } = require("path") -const fs = require("fs") -const env = require("../environment") -const { budibaseTempDir, ObjectStoreBuckets } = require("./utils") -const { v4 } = require("uuid") -const { APP_PREFIX, APP_DEV_PREFIX } = require("../db/utils") +import sanitize from "sanitize-s3-objectkey" +import AWS from "aws-sdk" +import stream from "stream" +import fetch from "node-fetch" +import tar from "tar-fs" +import zlib from "zlib" +import { promisify } from "util" +import { join } from "path" +import fs from "fs" +import env from "../environment" +import { budibaseTempDir, ObjectStoreBuckets } from "./utils" +import { v4 } from "uuid" +import { APP_PREFIX, APP_DEV_PREFIX } from "../db/utils" const streamPipeline = promisify(stream.pipeline) // use this as a temporary store of buckets that are being created diff --git a/packages/backend-core/tsconfig.build.json b/packages/backend-core/tsconfig.build.json new file mode 100644 index 0000000000..9c67698e91 --- /dev/null +++ b/packages/backend-core/tsconfig.build.json @@ -0,0 +1,10 @@ +{ + // Used for building with tsc + "extends": "./tsconfig.json", + "exclude": [ + "node_modules", + "**/*.json", + "**/*.spec.js", + "**/*.spec.ts" + ] +} \ No newline at end of file diff --git a/packages/backend-core/tsconfig.json b/packages/backend-core/tsconfig.json new file mode 100644 index 0000000000..5f9000c18f --- /dev/null +++ b/packages/backend-core/tsconfig.json @@ -0,0 +1,35 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "lib": ["es2020"], + // Tells TypeScript to read JS files, as + // normally they are ignored as source files + "allowJs": true, + // Generate d.ts files + "declaration": true, + // go to js file when using IDE functions like + // "Go to Definition" in VSCode + "declarationMap": true, + "sourceMap": true, + // Types should go into this directory. + // Removing this would place the .d.ts files + // next to the .js files + "outDir": "dist", + "strict": true, + "noImplicitAny": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "incremental": true, + "types": [ "node", "jest"], + }, + "include": [ + "**/*.js", + "**/*.ts", + ], + "exclude": [ + "node_modules", + "**/*.spec.js", + // "**/*.spec.ts" // don't exclude spec.ts files for editor support + ] +} diff --git a/packages/backend-core/yarn.lock b/packages/backend-core/yarn.lock index 87db3761bc..6b93a44fe0 100644 --- a/packages/backend-core/yarn.lock +++ b/packages/backend-core/yarn.lock @@ -586,11 +586,32 @@ dependencies: "@types/istanbul-lib-report" "*" +"@types/jest@^27.4.1": + version "27.4.1" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.4.1.tgz#185cbe2926eaaf9662d340cc02e548ce9e11ab6d" + integrity sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw== + dependencies: + jest-matcher-utils "^27.0.0" + pretty-format "^27.0.0" + +"@types/node-fetch@^2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975" + integrity sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + "@types/node@*": version "16.11.7" resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.7.tgz#36820945061326978c42a01e56b61cd223dfdc42" integrity sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw== +"@types/node@^15.12.4": + version "15.14.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.9.tgz#bc43c990c3c9be7281868bbc7b8fdd6e2b57adfa" + integrity sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A== + "@types/normalize-package-data@^2.4.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" @@ -732,6 +753,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" @@ -1494,6 +1520,11 @@ diff-sequences@^26.6.2: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== +diff-sequences@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" + integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== + domexception@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" @@ -2555,6 +2586,16 @@ jest-diff@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" +jest-diff@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" + integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== + dependencies: + chalk "^4.0.0" + diff-sequences "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + jest-docblock@^26.0.0: version "26.0.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" @@ -2603,6 +2644,11 @@ jest-get-type@^26.3.0: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== +jest-get-type@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" + integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== + jest-haste-map@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" @@ -2666,6 +2712,16 @@ jest-matcher-utils@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" +jest-matcher-utils@^27.0.0: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" + integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== + dependencies: + chalk "^4.0.0" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + jest-message-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" @@ -4104,6 +4160,15 @@ pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" +pretty-format@^27.0.0, pretty-format@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + private@^0.1.6, private@~0.1.5: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" @@ -5041,6 +5106,11 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" +typescript@^4.5.5: + version "4.6.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9" + integrity sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg== + uid2@0.0.x: version "0.0.4" resolved "https://registry.yarnpkg.com/uid2/-/uid2-0.0.4.tgz#033f3b1d5d32505f5ce5f888b9f3b667123c0a44" diff --git a/packages/server/src/module.d.ts b/packages/server/src/module.d.ts index 0419a40a14..dd01358254 100644 --- a/packages/server/src/module.d.ts +++ b/packages/server/src/module.d.ts @@ -1,4 +1,4 @@ -declare module "@budibase/backend-core" +// declare module "@budibase/backend-core" declare module "@budibase/backend-core/tenancy" declare module "@budibase/backend-core/db" declare module "@budibase/backend-core/context" diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 64f7be12e9..f0272eb5b1 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -1014,10 +1014,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@1.0.124-alpha.0": - version "1.0.124-alpha.0" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.124-alpha.0.tgz#33a9408206088da49154710910dafc8088d864d2" - integrity sha512-0ZUkDeqaoXS9qyK91SjwokYEA1wUPhi48nFE0+UwBloF8i7zVDFp2kOX7VNUrUer4gLuND9BoihEdpqsdQDvAg== +"@budibase/backend-core@1.0.126-alpha.0": + version "1.0.126-alpha.0" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.126-alpha.0.tgz#1e0968c685420592e1a7b3c12362075bd96fba57" + integrity sha512-35X/+B2IPvl6WZR0ztl6u6yz049TwEZrs4+BSp/euqRCzntVKuhfsN4dR+dDV/WGvOywrcARPCl28ubE7dLI8g== dependencies: "@techpass/passport-openidconnect" "^0.3.0" aws-sdk "^2.901.0" @@ -1091,12 +1091,12 @@ svelte-flatpickr "^3.2.3" svelte-portal "^1.0.0" -"@budibase/pro@1.0.124-alpha.0": - version "1.0.124-alpha.0" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.124-alpha.0.tgz#6287a51fa7c19754e44374c209c4aa3480fc3ac9" - integrity sha512-EgMuh+XSd/9tb3Ej9EZa4Y8hgiS6fHG+tuUwUcTuP6zvHbTijQGPb9075yImUbSc10bS3o41AP2qa2/ZdZKV2w== +"@budibase/pro@1.0.126-alpha.0": + version "1.0.126-alpha.0" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.126-alpha.0.tgz#b9b0d73ecbb5e878efafef3289409448a4884f19" + integrity sha512-+2aSs0LicKyWu+3A+b7eZXNhaPEkVrGUVtqUmfyLiqhnYM6ICVCBQJOxYQX08fpXq++icUfHoye4Me03aKSnKw== dependencies: - "@budibase/backend-core" "1.0.124-alpha.0" + "@budibase/backend-core" "1.0.126-alpha.0" node-fetch "^2.6.1" "@budibase/standard-components@^0.9.139": diff --git a/packages/worker/yarn.lock b/packages/worker/yarn.lock index 405845b896..d955e405bc 100644 --- a/packages/worker/yarn.lock +++ b/packages/worker/yarn.lock @@ -293,10 +293,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@1.0.124-alpha.0": - version "1.0.124-alpha.0" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.124-alpha.0.tgz#33a9408206088da49154710910dafc8088d864d2" - integrity sha512-0ZUkDeqaoXS9qyK91SjwokYEA1wUPhi48nFE0+UwBloF8i7zVDFp2kOX7VNUrUer4gLuND9BoihEdpqsdQDvAg== +"@budibase/backend-core@1.0.126-alpha.0": + version "1.0.126-alpha.0" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.126-alpha.0.tgz#1e0968c685420592e1a7b3c12362075bd96fba57" + integrity sha512-35X/+B2IPvl6WZR0ztl6u6yz049TwEZrs4+BSp/euqRCzntVKuhfsN4dR+dDV/WGvOywrcARPCl28ubE7dLI8g== dependencies: "@techpass/passport-openidconnect" "^0.3.0" aws-sdk "^2.901.0" @@ -321,12 +321,12 @@ uuid "^8.3.2" zlib "^1.0.5" -"@budibase/pro@1.0.124-alpha.0": - version "1.0.124-alpha.0" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.124-alpha.0.tgz#6287a51fa7c19754e44374c209c4aa3480fc3ac9" - integrity sha512-EgMuh+XSd/9tb3Ej9EZa4Y8hgiS6fHG+tuUwUcTuP6zvHbTijQGPb9075yImUbSc10bS3o41AP2qa2/ZdZKV2w== +"@budibase/pro@1.0.126-alpha.0": + version "1.0.126-alpha.0" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.126-alpha.0.tgz#b9b0d73ecbb5e878efafef3289409448a4884f19" + integrity sha512-+2aSs0LicKyWu+3A+b7eZXNhaPEkVrGUVtqUmfyLiqhnYM6ICVCBQJOxYQX08fpXq++icUfHoye4Me03aKSnKw== dependencies: - "@budibase/backend-core" "1.0.124-alpha.0" + "@budibase/backend-core" "1.0.126-alpha.0" node-fetch "^2.6.1" "@cspotcode/source-map-consumer@0.8.0": From 8b4f4c813a68d94cd27a813515997c3e740de0b8 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 3 May 2022 17:13:13 +0100 Subject: [PATCH 31/75] Add @budibase/types package --- packages/backend-core/package.json | 1 + packages/backend-core/src/events/constants.js | 141 ----------------- packages/backend-core/src/events/events.js | 8 +- .../src/events/handlers/account.js | 17 -- .../src/events/handlers/account.ts | 17 ++ .../backend-core/src/events/handlers/app.js | 59 ------- .../backend-core/src/events/handlers/app.ts | 73 +++++++++ .../backend-core/src/events/handlers/auth.js | 42 ----- .../backend-core/src/events/handlers/auth.ts | 52 +++++++ .../src/events/handlers/automation.js | 38 ----- .../src/events/handlers/automation.ts | 52 +++++++ .../src/events/handlers/datasource.js | 17 -- .../src/events/handlers/datasource.ts | 23 +++ .../backend-core/src/events/handlers/email.js | 12 -- .../backend-core/src/events/handlers/email.ts | 17 ++ .../backend-core/src/events/handlers/index.js | 37 ----- .../backend-core/src/events/handlers/index.ts | 17 ++ .../src/events/handlers/layout.js | 12 -- .../src/events/handlers/layout.ts | 17 ++ .../src/events/handlers/license.js | 35 ----- .../src/events/handlers/license.ts | 43 ++++++ .../backend-core/src/events/handlers/org.js | 30 ---- .../backend-core/src/events/handlers/org.ts | 30 ++++ .../backend-core/src/events/handlers/query.js | 35 ----- .../backend-core/src/events/handlers/query.ts | 48 ++++++ .../backend-core/src/events/handlers/role.js | 29 ---- .../backend-core/src/events/handlers/role.ts | 38 +++++ .../backend-core/src/events/handlers/row.js | 14 -- .../backend-core/src/events/handlers/row.ts | 23 +++ .../src/events/handlers/screen.js | 12 -- .../src/events/handlers/screen.ts | 17 ++ .../backend-core/src/events/handlers/serve.js | 19 --- .../backend-core/src/events/handlers/serve.ts | 25 +++ .../backend-core/src/events/handlers/table.js | 35 ----- .../backend-core/src/events/handlers/table.ts | 45 ++++++ .../backend-core/src/events/handlers/user.js | 75 --------- .../backend-core/src/events/handlers/user.ts | 89 +++++++++++ .../backend-core/src/events/handlers/view.js | 54 ------- .../backend-core/src/events/handlers/view.ts | 66 ++++++++ packages/backend-core/src/events/index.js | 6 +- packages/backend-core/src/index.js | 3 +- packages/types/.gitignore | 3 + packages/types/package.json | 18 +++ packages/types/src/api/public/.keep | 0 packages/types/src/api/web/.keep | 0 .../types/src/documents/account/account.ts | 1 + packages/types/src/documents/account/index.ts | 1 + packages/types/src/documents/app/app.ts | 1 + .../types/src/documents/app/automation.ts | 5 + .../types/src/documents/app/datasource.ts | 1 + packages/types/src/documents/app/index.ts | 9 ++ packages/types/src/documents/app/layout.ts | 1 + packages/types/src/documents/app/query.ts | 1 + packages/types/src/documents/app/role.ts | 1 + packages/types/src/documents/app/screen.ts | 1 + packages/types/src/documents/app/table.ts | 1 + packages/types/src/documents/app/view.ts | 1 + packages/types/src/documents/global/config.ts | 1 + packages/types/src/documents/global/index.ts | 2 + packages/types/src/documents/global/user.ts | 1 + packages/types/src/documents/index.ts | 4 + .../types/src/documents/platform/index.ts | 1 + packages/types/src/documents/platform/info.ts | 1 + packages/types/src/events/app.ts | 21 +++ packages/types/src/events/auth.ts | 16 ++ packages/types/src/events/automation.ts | 11 ++ packages/types/src/events/config.ts | 3 + packages/types/src/events/datasource.ts | 5 + packages/types/src/events/events.ts | 145 ++++++++++++++++++ packages/types/src/events/index.ts | 17 ++ packages/types/src/events/layout.ts | 3 + packages/types/src/events/license.ts | 9 ++ packages/types/src/events/org.ts | 1 + packages/types/src/events/query.ts | 9 ++ packages/types/src/events/role.ts | 9 ++ packages/types/src/events/row.ts | 1 + packages/types/src/events/screen.ts | 3 + packages/types/src/events/serve.ts | 5 + packages/types/src/events/table.ts | 9 ++ packages/types/src/events/user.ts | 21 +++ packages/types/src/events/view.ts | 19 +++ packages/types/src/index.ts | 3 + packages/types/src/licensing/index.ts | 1 + packages/types/src/licensing/license.ts | 1 + packages/types/tsconfig.json | 17 ++ packages/types/yarn.lock | 13 ++ 86 files changed, 1097 insertions(+), 723 deletions(-) delete mode 100644 packages/backend-core/src/events/constants.js delete mode 100644 packages/backend-core/src/events/handlers/account.js create mode 100644 packages/backend-core/src/events/handlers/account.ts delete mode 100644 packages/backend-core/src/events/handlers/app.js create mode 100644 packages/backend-core/src/events/handlers/app.ts delete mode 100644 packages/backend-core/src/events/handlers/auth.js create mode 100644 packages/backend-core/src/events/handlers/auth.ts delete mode 100644 packages/backend-core/src/events/handlers/automation.js create mode 100644 packages/backend-core/src/events/handlers/automation.ts delete mode 100644 packages/backend-core/src/events/handlers/datasource.js create mode 100644 packages/backend-core/src/events/handlers/datasource.ts delete mode 100644 packages/backend-core/src/events/handlers/email.js create mode 100644 packages/backend-core/src/events/handlers/email.ts delete mode 100644 packages/backend-core/src/events/handlers/index.js create mode 100644 packages/backend-core/src/events/handlers/index.ts delete mode 100644 packages/backend-core/src/events/handlers/layout.js create mode 100644 packages/backend-core/src/events/handlers/layout.ts delete mode 100644 packages/backend-core/src/events/handlers/license.js create mode 100644 packages/backend-core/src/events/handlers/license.ts delete mode 100644 packages/backend-core/src/events/handlers/org.js create mode 100644 packages/backend-core/src/events/handlers/org.ts delete mode 100644 packages/backend-core/src/events/handlers/query.js create mode 100644 packages/backend-core/src/events/handlers/query.ts delete mode 100644 packages/backend-core/src/events/handlers/role.js create mode 100644 packages/backend-core/src/events/handlers/role.ts delete mode 100644 packages/backend-core/src/events/handlers/row.js create mode 100644 packages/backend-core/src/events/handlers/row.ts delete mode 100644 packages/backend-core/src/events/handlers/screen.js create mode 100644 packages/backend-core/src/events/handlers/screen.ts delete mode 100644 packages/backend-core/src/events/handlers/serve.js create mode 100644 packages/backend-core/src/events/handlers/serve.ts delete mode 100644 packages/backend-core/src/events/handlers/table.js create mode 100644 packages/backend-core/src/events/handlers/table.ts delete mode 100644 packages/backend-core/src/events/handlers/user.js create mode 100644 packages/backend-core/src/events/handlers/user.ts delete mode 100644 packages/backend-core/src/events/handlers/view.js create mode 100644 packages/backend-core/src/events/handlers/view.ts create mode 100644 packages/types/.gitignore create mode 100644 packages/types/package.json create mode 100644 packages/types/src/api/public/.keep create mode 100644 packages/types/src/api/web/.keep create mode 100644 packages/types/src/documents/account/account.ts create mode 100644 packages/types/src/documents/account/index.ts create mode 100644 packages/types/src/documents/app/app.ts create mode 100644 packages/types/src/documents/app/automation.ts create mode 100644 packages/types/src/documents/app/datasource.ts create mode 100644 packages/types/src/documents/app/index.ts create mode 100644 packages/types/src/documents/app/layout.ts create mode 100644 packages/types/src/documents/app/query.ts create mode 100644 packages/types/src/documents/app/role.ts create mode 100644 packages/types/src/documents/app/screen.ts create mode 100644 packages/types/src/documents/app/table.ts create mode 100644 packages/types/src/documents/app/view.ts create mode 100644 packages/types/src/documents/global/config.ts create mode 100644 packages/types/src/documents/global/index.ts create mode 100644 packages/types/src/documents/global/user.ts create mode 100644 packages/types/src/documents/index.ts create mode 100644 packages/types/src/documents/platform/index.ts create mode 100644 packages/types/src/documents/platform/info.ts create mode 100644 packages/types/src/events/app.ts create mode 100644 packages/types/src/events/auth.ts create mode 100644 packages/types/src/events/automation.ts create mode 100644 packages/types/src/events/config.ts create mode 100644 packages/types/src/events/datasource.ts create mode 100644 packages/types/src/events/events.ts create mode 100644 packages/types/src/events/index.ts create mode 100644 packages/types/src/events/layout.ts create mode 100644 packages/types/src/events/license.ts create mode 100644 packages/types/src/events/org.ts create mode 100644 packages/types/src/events/query.ts create mode 100644 packages/types/src/events/role.ts create mode 100644 packages/types/src/events/row.ts create mode 100644 packages/types/src/events/screen.ts create mode 100644 packages/types/src/events/serve.ts create mode 100644 packages/types/src/events/table.ts create mode 100644 packages/types/src/events/user.ts create mode 100644 packages/types/src/events/view.ts create mode 100644 packages/types/src/index.ts create mode 100644 packages/types/src/licensing/index.ts create mode 100644 packages/types/src/licensing/license.ts create mode 100644 packages/types/tsconfig.json create mode 100644 packages/types/yarn.lock diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 7074031b9b..3b22bd5b2e 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -44,6 +44,7 @@ "@types/jest": "^27.4.1", "@types/node": "^15.12.4", "@types/node-fetch": "^2.6.1", + "@budibase/types": "^1.0.126-alpha.0", "typescript": "^4.5.5", "@shopify/jest-koa-mocks": "^3.1.5", "ioredis-mock": "^5.5.5", diff --git a/packages/backend-core/src/events/constants.js b/packages/backend-core/src/events/constants.js deleted file mode 100644 index 99bd8a6ada..0000000000 --- a/packages/backend-core/src/events/constants.js +++ /dev/null @@ -1,141 +0,0 @@ -exports.Events = { - // USER - USER_CREATED: "user:created", - USER_UPDATED: "user:updated", - USER_DELETED: "user:deleted", - - // USER / PERMISSIONS - USER_PERMISSION_ADMIN_ASSIGNED: "user:admin:assigned", - USER_PERMISSION_ADMIN_REMOVED: "user:admin:removed", - USER_PERMISSION_BUILDER_ASSIGNED: "user:builder:assigned", - USER_PERMISSION_BUILDER_REMOVED: "userbuilder:removed", - - // USER / INVITE - USER_INVITED: "user:invited", - USER_INVITED_ACCEPTED: "user:invite:accepted", - - // USER / PASSWORD - USER_PASSWORD_FORCE_RESET: "user:password:force:reset", - USER_PASSWORD_UPDATED: "user:password:updated", - USER_PASSWORD_RESET_REQUESTED: "user:password:reset:requested", - USER_PASSWORD_RESET: "user:password:reset", - - // EMAIL - EMAIL_SMTP_CREATED: "email:smtp:created", - EMAIL_SMTP_UPDATED: "email:smtp:updated", - - // AUTH - AUTH_SSO_CREATED: "auth:sso:created", - AUTH_SSO_UPDATED: "auth:sso:updated", - AUTH_SSO_ACTIVATED: "auth:sso:activated", - AUTH_SSO_DEACTIVATED: "auth:sso:deactivated", - AUTH_LOGIN: "auth:login", - AUTH_LOGOUT: "auth:logout", - - // ORG - ORG_NAME_UPDATED: "org:info:name:updated", - ORG_LOGO_UPDATED: "org:info:logo:updated", - ORG_PLATFORM_URL_UPDATED: "org:platformurl:updated", - - // ORG / UPDATE - UPDATE_VERSION_CHECKED: "version:checked", - - // ORG / ANALYTICS - ANALYTICS_OPT_OUT: "analytics:opt:out", - - // APP - APP_CREATED: "app:created", - APP_UPDATED: "app:updated", - APP_DELETED: "app:deleted", - APP_PUBLISHED: "app:published", - APP_UNPUBLISHED: "app:unpublished", - APP_TEMPLATE_IMPORTED: "app:template:imported", - APP_FILE_IMPORTED: "app:file:imported", - APP_VERSION_UPDATED: "app:version:updated", - APP_VERSION_REVERTED: "app:version:reverted", - APP_REVERTED: "app:reverted", - APP_EXPORTED: "app:exported", - - // ROLE - ROLE_CREATED: "role:created", - ROLE_UPDATED: "role:updated", - ROLE_DELETED: "role:deleted", - ROLE_ASSIGNED: "role:assigned", - ROLE_UNASSIGNED: "role:unassigned", - - // SERVE - SERVED_BUILDER: "served:builder", - SERVED_APP: "served:app", - SERVED_APP_PREVIEW: "served:app:preview", - - // DATASOURCE - DATASOURCE_CREATED: "datasource:created", - DATASOURCE_UPDATED: "datasource:updated", - DATASOURCE_DELETED: "datasource:deleted", - - // QUERY - QUERY_CREATED: "query:created", - QUERY_UPDATED: "query:updated", - QUERY_DELETED: "query:deleted", - QUERY_IMPORT: "query:import", - // QUERY_RUN: "query:run", - QUERY_PREVIEWED: "query:previewed", - - // TABLE - TABLE_CREATED: "table:created", - TABLE_UPDATED: "table:updated", - TABLE_DELETED: "table:deleted", - TABLE_EXPORTED: "table:exported", - TABLE_IMPORTED: "table:imported", - TABLE_DATA_IMPORTED: "table:data:imported", - TABLE_PERMISSION_UPDATED: "table:permission:updated", - - // VIEW - VIEW_CREATED: "view:created", - VIEW_UPDATED: "view:updated", - VIEW_DELETED: "view:deleted", - VIEW_EXPORTED: "view:exported", - VIEW_FILTER_CREATED: "view:filter:created", - VIEW_FILTER_UPDATED: "view:filter:updated", - VIEW_FILTER_DELETED: "view:filter:deleted", - VIEW_CALCULATION_CREATED: "view:calculation:created", - VIEW_CALCULATION_UPDATED: "view:calculation:updated", - VIEW_CALCULATION_DELETED: "view:calculation:deleted", - - // ROW - // ROW_CREATED: "row:created", - ROW_IMPORT: "row:import", - - // COMPONENT - COMPONENT_CREATED: "component:created", - COMPONENT_DELETED: "component:deleted", - - // SCREEN - SCREEN_CREATED: "screen:created", - SCREEN_DELETED: "screen:deleted", - - // LAYOUT - LAYOUT_CREATED: "layout:created", - LAYOUT_DELETED: "layout:deleted", - - // AUTOMATION - AUTOMATION_CREATED: "automation:created", - AUTOMATION_DELETED: "automation:deleted", - AUTOMATION_TESTED: "automation:tested", - // AUTOMATION_RUN: "automation:run", - AUTOMATION_STEP_CREATED: "automation:step:created", - AUTOMATION_STEP_DELETED: "automation:step:deleted", - AUTOMATION_TRIGGER_UPDATED: "automation:trigger:updated", - - // LICENSE - LICENSE_UPGRADED: "license:upgraded", - LICENSE_DOWNGRADED: "license:downgraded", - LICENSE_UPDATED: "license:updated", - LICENSE_ACTIVATED: "license:activated", - LICENSE_QUOTA_EXCEEDED: "license:quota:exceeded", - - // ACCOUNT - ACCOUNT_CREATED: "account:created", - ACCOUNT_DELETED: "account:deleted", - ACCOUNT_VERIFIED: "account:verified", -} diff --git a/packages/backend-core/src/events/events.js b/packages/backend-core/src/events/events.js index 56fe069c7e..eb00fc5d47 100644 --- a/packages/backend-core/src/events/events.js +++ b/packages/backend-core/src/events/events.js @@ -1,5 +1,5 @@ -const { getTenantId } = require("../context") -const analytics = require("../analytics") +import { getTenantId } from "../context" +import { captureEvent } from "../analytics" const logEvent = messsage => { const tenantId = getTenantId() @@ -7,10 +7,10 @@ const logEvent = messsage => { console.log(`[audit] [tenant=${tenantId}] [user=${userId}] ${messsage}`) } -exports.processEvent = (event, properties) => { +export const processEvent = (event, properties) => { // logging logEvent(event) // analytics - analytics.captureEvent(event, properties) + captureEvent(event, properties) } diff --git a/packages/backend-core/src/events/handlers/account.js b/packages/backend-core/src/events/handlers/account.js deleted file mode 100644 index e1d1eb8d35..0000000000 --- a/packages/backend-core/src/events/handlers/account.js +++ /dev/null @@ -1,17 +0,0 @@ -const events = require("../events") -const { Events } = require("../constants") - -exports.created = () => { - const properties = {} - events.processEvent(Events.ACCOUNT_CREATED, properties) -} - -exports.deleted = () => { - const properties = {} - events.processEvent(Events.ACCOUNT_DELETED, properties) -} - -exports.verified = () => { - const properties = {} - events.processEvent(Events.ACCOUNT_VERIFIED, properties) -} diff --git a/packages/backend-core/src/events/handlers/account.ts b/packages/backend-core/src/events/handlers/account.ts new file mode 100644 index 0000000000..9577edc43b --- /dev/null +++ b/packages/backend-core/src/events/handlers/account.ts @@ -0,0 +1,17 @@ +import { processEvent } from "../events" +import { Events, Account } from "@budibase/types" + +export function created(account: Account) { + const properties = {} + processEvent(Events.ACCOUNT_CREATED, properties) +} + +export function deleted(account: Account) { + const properties = {} + processEvent(Events.ACCOUNT_DELETED, properties) +} + +export function verified(account: Account) { + const properties = {} + processEvent(Events.ACCOUNT_VERIFIED, properties) +} diff --git a/packages/backend-core/src/events/handlers/app.js b/packages/backend-core/src/events/handlers/app.js deleted file mode 100644 index 17c86d9a6d..0000000000 --- a/packages/backend-core/src/events/handlers/app.js +++ /dev/null @@ -1,59 +0,0 @@ -const events = require("../events") -const { Events } = require("../constants") - -exports.created = () => { - const properties = {} - events.processEvent(Events.APP_CREATED, properties) -} - -exports.updated = () => { - const properties = {} - events.processEvent(Events.APP_UPDATED, properties) -} - -exports.deleted = () => { - const properties = {} - events.processEvent(Events.APP_DELETED, properties) -} - -exports.published = () => { - const properties = {} - events.processEvent(Events.APP_PUBLISHED, properties) -} - -exports.unpublished = () => { - const properties = {} - events.processEvent(Events.APP_UNPUBLISHED, properties) -} - -exports.fileImported = () => { - const properties = {} - events.processEvent(Events.APP_FILE_IMPORTED, properties) -} - -exports.templateImported = templateKey => { - const properties = { - templateKey, - } - events.processEvent(Events.APP_TEMPLATE_IMPORTED, properties) -} - -exports.versionUpdated = () => { - const properties = {} - events.processEvent(Events.APP_VERSION_UPDATED, properties) -} - -exports.versionReverted = () => { - const properties = {} - events.processEvent(Events.APP_VERSION_REVERTED, properties) -} - -exports.reverted = () => { - const properties = {} - events.processEvent(Events.APP_REVERTED, properties) -} - -exports.exported = () => { - const properties = {} - events.processEvent(Events.APP_EXPORTED, properties) -} diff --git a/packages/backend-core/src/events/handlers/app.ts b/packages/backend-core/src/events/handlers/app.ts new file mode 100644 index 0000000000..904f8efeda --- /dev/null +++ b/packages/backend-core/src/events/handlers/app.ts @@ -0,0 +1,73 @@ +import { processEvent } from "../events" +import { + Events, + App, + AppCreatedEvent, + AppUpdatedEvent, + AppDeletedEvent, + AppPublishedEvent, + AppUnpublishedEvent, + AppFileImportedEvent, + AppTemplateImportedEvent, + AppVersionUpdatedEvent, + AppVersionRevertedEvent, + AppRevertedEvent, + AppExportedEvent, +} from "@budibase/types" + +export function created(app: App) { + const properties: AppCreatedEvent = {} + processEvent(Events.APP_CREATED, properties) +} + +export function updated(app: App) { + const properties: AppUpdatedEvent = {} + processEvent(Events.APP_UPDATED, properties) +} + +export function deleted(app: App) { + const properties: AppDeletedEvent = {} + processEvent(Events.APP_DELETED, properties) +} + +export function published(app: App) { + const properties: AppPublishedEvent = {} + processEvent(Events.APP_PUBLISHED, properties) +} + +export function unpublished(app: App) { + const properties: AppUnpublishedEvent = {} + processEvent(Events.APP_UNPUBLISHED, properties) +} + +export function fileImported(app: App) { + const properties: AppFileImportedEvent = {} + processEvent(Events.APP_FILE_IMPORTED, properties) +} + +export function templateImported(templateKey: string) { + const properties: AppTemplateImportedEvent = { + templateKey, + } + processEvent(Events.APP_TEMPLATE_IMPORTED, properties) +} + +export function versionUpdated(app: App) { + const properties: AppVersionUpdatedEvent = {} + processEvent(Events.APP_VERSION_UPDATED, properties) +} + +export function versionReverted(app: App) { + const properties: AppVersionRevertedEvent = {} + processEvent(Events.APP_VERSION_REVERTED, properties) +} + +export function reverted(app: App) { + const properties: AppRevertedEvent = {} + processEvent(Events.APP_REVERTED, properties) +} + +export function exported(app: App) { + const properties: AppExportedEvent = {} + processEvent(Events.APP_EXPORTED, properties) +} diff --git a/packages/backend-core/src/events/handlers/auth.js b/packages/backend-core/src/events/handlers/auth.js deleted file mode 100644 index 98bbbd2994..0000000000 --- a/packages/backend-core/src/events/handlers/auth.js +++ /dev/null @@ -1,42 +0,0 @@ -const events = require("../events") -const { Events } = require("../constants") - -exports.login = source => { - const properties = { - source, - } - events.processEvent(Events.AUTH_LOGIN, properties) -} - -exports.logout = () => { - const properties = {} - events.processEvent(Events.AUTH_LOGOUT, properties) -} - -exports.SSOCreated = type => { - const properties = { - type, - } - events.processEvent(Events.AUTH_SSO_CREATED, properties) -} - -exports.SSOUpdated = type => { - const properties = { - type, - } - events.processEvent(Events.AUTH_SSO_UPDATED, properties) -} - -exports.SSOActivated = type => { - const properties = { - type, - } - events.processEvent(Events.AUTH_SSO_ACTIVATED, properties) -} - -exports.SSODeactivated = type => { - const properties = { - type, - } - events.processEvent(Events.AUTH_SSO_DEACTIVATED, properties) -} diff --git a/packages/backend-core/src/events/handlers/auth.ts b/packages/backend-core/src/events/handlers/auth.ts new file mode 100644 index 0000000000..8d74a9a103 --- /dev/null +++ b/packages/backend-core/src/events/handlers/auth.ts @@ -0,0 +1,52 @@ +import { processEvent } from "../events" +import { + Events, + LoginEvent, + LoginSource, + LogoutEvent, + SSOActivatedEvent, + SSOCreatedEvent, + SSODeactivatedEvent, + SSOType, + SSOUpdatedEvent, +} from "@budibase/types" + +export function login(source: LoginSource) { + const properties: LoginEvent = { + source, + } + processEvent(Events.AUTH_LOGIN, properties) +} + +export function logout() { + const properties: LogoutEvent = {} + processEvent(Events.AUTH_LOGOUT, properties) +} + +export function SSOCreated(type: SSOType) { + const properties: SSOCreatedEvent = { + type, + } + processEvent(Events.AUTH_SSO_CREATED, properties) +} + +export function SSOUpdated(type: SSOType) { + const properties: SSOUpdatedEvent = { + type, + } + processEvent(Events.AUTH_SSO_UPDATED, properties) +} + +export function SSOActivated(type: SSOType) { + const properties: SSOActivatedEvent = { + type, + } + processEvent(Events.AUTH_SSO_ACTIVATED, properties) +} + +export function SSODeactivated(type: SSOType) { + const properties: SSODeactivatedEvent = { + type, + } + processEvent(Events.AUTH_SSO_DEACTIVATED, properties) +} diff --git a/packages/backend-core/src/events/handlers/automation.js b/packages/backend-core/src/events/handlers/automation.js deleted file mode 100644 index 2a978b4645..0000000000 --- a/packages/backend-core/src/events/handlers/automation.js +++ /dev/null @@ -1,38 +0,0 @@ -const events = require("../events") -const { Events } = require("../constants") - -exports.created = () => { - const properties = {} - events.processEvent(Events.AUTOMATION_CREATED, properties) -} - -exports.deleted = () => { - const properties = {} - events.processEvent(Events.AUTOMATION_DELETED, properties) -} - -exports.tested = () => { - const properties = {} - events.processEvent(Events.AUTOMATION_TESTED, properties) -} - -// TODO -// exports.run = () => { -// const properties = {} -// events.processEvent(Events.AUTOMATION_RUN, properties) -// } - -exports.stepCreated = () => { - const properties = {} - events.processEvent(Events.AUTOMATION_STEP_CREATED, properties) -} - -exports.stepDeleted = () => { - const properties = {} - events.processEvent(Events.AUTOMATION_STEP_DELETED, properties) -} - -exports.triggerUpdated = () => { - const properties = {} - events.processEvent(Events.AUTOMATION_TRIGGER_UPDATED, properties) -} diff --git a/packages/backend-core/src/events/handlers/automation.ts b/packages/backend-core/src/events/handlers/automation.ts new file mode 100644 index 0000000000..22986bf935 --- /dev/null +++ b/packages/backend-core/src/events/handlers/automation.ts @@ -0,0 +1,52 @@ +import { processEvent } from "../events" +import { + Automation, + Events, + AutomationStep, + AutomationTrigger, + AutomationCreatedEvent, + AutomationDeletedEvent, + AutomationTestedEvent, + AutomationStepCreatedEvent, + AutomationStepDeletedEvent, + AutomationTriggerUpdatedEvent, +} from "@budibase/types" + +export function created(automation: Automation) { + const properties: AutomationCreatedEvent = {} + processEvent(Events.AUTOMATION_CREATED, properties) +} + +export function deleted(automation: Automation) { + const properties: AutomationDeletedEvent = {} + processEvent(Events.AUTOMATION_DELETED, properties) +} + +export function tested(automation: Automation) { + const properties: AutomationTestedEvent = {} + processEvent(Events.AUTOMATION_TESTED, properties) +} + +// TODO +// exports.run = () => { +// const properties = {} +// events.processEvent(Events.AUTOMATION_RUN, properties) +// } + +export function stepCreated(automation: Automation, step: AutomationStep) { + const properties: AutomationStepCreatedEvent = {} + processEvent(Events.AUTOMATION_STEP_CREATED, properties) +} + +export function stepDeleted(automation: Automation, step: AutomationStep) { + const properties: AutomationStepDeletedEvent = {} + processEvent(Events.AUTOMATION_STEP_DELETED, properties) +} + +export function triggerUpdated( + automation: Automation, + trigger: AutomationTrigger +) { + const properties: AutomationTriggerUpdatedEvent = {} + processEvent(Events.AUTOMATION_TRIGGER_UPDATED, properties) +} diff --git a/packages/backend-core/src/events/handlers/datasource.js b/packages/backend-core/src/events/handlers/datasource.js deleted file mode 100644 index 512a6cc28e..0000000000 --- a/packages/backend-core/src/events/handlers/datasource.js +++ /dev/null @@ -1,17 +0,0 @@ -const events = require("../events") -const { Events } = require("../constants") - -exports.created = () => { - const properties = {} - events.processEvent(Events.DATASOURCE_CREATED, properties) -} - -exports.updated = () => { - const properties = {} - events.processEvent(Events.DATASOURCE_UPDATED, properties) -} - -exports.deleted = () => { - const properties = {} - events.processEvent(Events.DATASOURCE_DELETED, properties) -} diff --git a/packages/backend-core/src/events/handlers/datasource.ts b/packages/backend-core/src/events/handlers/datasource.ts new file mode 100644 index 0000000000..c32d4292ba --- /dev/null +++ b/packages/backend-core/src/events/handlers/datasource.ts @@ -0,0 +1,23 @@ +import { processEvent } from "../events" +import { + Events, + Datasource, + DatasourceCreatedEvent, + DatasourceUpdatedEvent, + DatasourceDeletedEvent, +} from "@budibase/types" + +export function created(datasource: Datasource) { + const properties: DatasourceCreatedEvent = {} + processEvent(Events.DATASOURCE_CREATED, properties) +} + +export function updated(datasource: Datasource) { + const properties: DatasourceUpdatedEvent = {} + processEvent(Events.DATASOURCE_UPDATED, properties) +} + +export function deleted(datasource: Datasource) { + const properties: DatasourceDeletedEvent = {} + processEvent(Events.DATASOURCE_DELETED, properties) +} diff --git a/packages/backend-core/src/events/handlers/email.js b/packages/backend-core/src/events/handlers/email.js deleted file mode 100644 index 189f82dec7..0000000000 --- a/packages/backend-core/src/events/handlers/email.js +++ /dev/null @@ -1,12 +0,0 @@ -const events = require("../events") -const { Events } = require("../constants") - -exports.SMTPCreated = () => { - const properties = {} - events.processEvent(Events.EMAIL_SMTP_CREATED, properties) -} - -exports.SMTPUpdated = () => { - const properties = {} - events.processEvent(Events.EMAIL_SMTP_UPDATED, properties) -} diff --git a/packages/backend-core/src/events/handlers/email.ts b/packages/backend-core/src/events/handlers/email.ts new file mode 100644 index 0000000000..d9f0320540 --- /dev/null +++ b/packages/backend-core/src/events/handlers/email.ts @@ -0,0 +1,17 @@ +import { processEvent } from "../events" +import { + Events, + SMTPConfig, + SMTPCreatedEvent, + SMTPUpdatedEvent, +} from "@budibase/types" + +export function SMTPCreated(config: SMTPConfig) { + const properties: SMTPCreatedEvent = {} + processEvent(Events.EMAIL_SMTP_CREATED, properties) +} + +export function SMTPUpdated(config: SMTPConfig) { + const properties: SMTPUpdatedEvent = {} + processEvent(Events.EMAIL_SMTP_UPDATED, properties) +} diff --git a/packages/backend-core/src/events/handlers/index.js b/packages/backend-core/src/events/handlers/index.js deleted file mode 100644 index a1b92b0332..0000000000 --- a/packages/backend-core/src/events/handlers/index.js +++ /dev/null @@ -1,37 +0,0 @@ -const account = require("./account") -const app = require("./app") -const auth = require("./auth") -const automation = require("./automation") -const datasource = require("./datasource") -const email = require("./email") -const license = require("./license") -const layout = require("./layout") -const org = require("./org") -const query = require("./query") -const role = require("./role") -const screen = require("./screen") -const row = require("./row") -const table = require("./table") -const serve = require("./serve") -const user = require("./user") -const view = require("./view") - -module.exports = { - account, - app, - auth, - automation, - datasource, - email, - license, - layout, - org, - query, - role, - screen, - row, - table, - serve, - user, - view, -} diff --git a/packages/backend-core/src/events/handlers/index.ts b/packages/backend-core/src/events/handlers/index.ts new file mode 100644 index 0000000000..b8381622b7 --- /dev/null +++ b/packages/backend-core/src/events/handlers/index.ts @@ -0,0 +1,17 @@ +export * as account from "./account" +export * as app from "./app" +export * as auth from "./auth" +export * as automation from "./automation" +export * as datasource from "./datasource" +export * as email from "./email" +export * as license from "./license" +export * as layout from "./layout" +export * as org from "./org" +export * as query from "./query" +export * as role from "./role" +export * as screen from "./screen" +export * as row from "./row" +export * as table from "./table" +export * as serve from "./serve" +export * as user from "./user" +export * as view from "./view" diff --git a/packages/backend-core/src/events/handlers/layout.js b/packages/backend-core/src/events/handlers/layout.js deleted file mode 100644 index fdeda28d60..0000000000 --- a/packages/backend-core/src/events/handlers/layout.js +++ /dev/null @@ -1,12 +0,0 @@ -const events = require("../events") -const { Events } = require("../constants") - -exports.created = () => { - const properties = {} - events.processEvent(Events.LAYOUT_CREATED, properties) -} - -exports.deleted = () => { - const properties = {} - events.processEvent(Events.LAYOUT_DELETED, properties) -} diff --git a/packages/backend-core/src/events/handlers/layout.ts b/packages/backend-core/src/events/handlers/layout.ts new file mode 100644 index 0000000000..325f4dda50 --- /dev/null +++ b/packages/backend-core/src/events/handlers/layout.ts @@ -0,0 +1,17 @@ +import { processEvent } from "../events" +import { + Events, + Layout, + LayoutCreatedEvent, + LayoutDeletedEvent, +} from "@budibase/types" + +export function created(layout: Layout) { + const properties: LayoutCreatedEvent = {} + processEvent(Events.LAYOUT_CREATED, properties) +} + +export function deleted(layout: Layout) { + const properties: LayoutDeletedEvent = {} + processEvent(Events.LAYOUT_DELETED, properties) +} diff --git a/packages/backend-core/src/events/handlers/license.js b/packages/backend-core/src/events/handlers/license.js deleted file mode 100644 index 68107e8fd9..0000000000 --- a/packages/backend-core/src/events/handlers/license.js +++ /dev/null @@ -1,35 +0,0 @@ -const events = require("../events") -const { Events } = require("../constants") - -// TODO -exports.updgraded = () => { - const properties = {} - events.processEvent(Events.LICENSE_UPGRADED, properties) -} - -// TODO -exports.downgraded = () => { - const properties = {} - events.processEvent(Events.LICENSE_DOWNGRADED, properties) -} - -// TODO -exports.updated = () => { - const properties = {} - events.processEvent(Events.LICENSE_UPDATED, properties) -} - -// TODO -exports.activated = () => { - const properties = {} - events.processEvent(Events.LICENSE_ACTIVATED, properties) -} - -// TODO -exports.quotaExceeded = (quotaName, value) => { - const properties = { - name: quotaName, - value, - } - events.processEvent(Events.LICENSE_QUOTA_EXCEEDED, properties) -} diff --git a/packages/backend-core/src/events/handlers/license.ts b/packages/backend-core/src/events/handlers/license.ts new file mode 100644 index 0000000000..f33deb2018 --- /dev/null +++ b/packages/backend-core/src/events/handlers/license.ts @@ -0,0 +1,43 @@ +import { processEvent } from "../events" +import { + Events, + License, + LicenseActivatedEvent, + LicenseDowngradedEvent, + LicenseQuotaExceededEvent, + LicenseUpdatedEvent, + LicenseUpgradedEvent, +} from "@budibase/types" + +// TODO +export function updgraded(license: License) { + const properties: LicenseUpgradedEvent = {} + processEvent(Events.LICENSE_UPGRADED, properties) +} + +// TODO +export function downgraded(license: License) { + const properties: LicenseDowngradedEvent = {} + processEvent(Events.LICENSE_DOWNGRADED, properties) +} + +// TODO +export function updated(license: License) { + const properties: LicenseUpdatedEvent = {} + processEvent(Events.LICENSE_UPDATED, properties) +} + +// TODO +export function activated(license: License) { + const properties: LicenseActivatedEvent = {} + processEvent(Events.LICENSE_ACTIVATED, properties) +} + +// TODO +export function quotaExceeded(quotaName: string, value: number) { + const properties: LicenseQuotaExceededEvent = { + name: quotaName, + value, + } + processEvent(Events.LICENSE_QUOTA_EXCEEDED, properties) +} diff --git a/packages/backend-core/src/events/handlers/org.js b/packages/backend-core/src/events/handlers/org.js deleted file mode 100644 index fc5ad13c64..0000000000 --- a/packages/backend-core/src/events/handlers/org.js +++ /dev/null @@ -1,30 +0,0 @@ -const events = require("../events") -const { Events } = require("../constants") - -exports.nameUpdated = () => { - const properties = {} - events.processEvent(Events.ORG_NAME_UPDATED, properties) -} - -exports.logoUpdated = () => { - const properties = {} - events.processEvent(Events.ORG_LOGO_UPDATED, properties) -} - -exports.platformURLUpdated = () => { - const properties = {} - events.processEvent(Events.ORG_PLATFORM_URL_UPDATED, properties) -} - -exports.versionChecked = version => { - const properties = { - version, - } - events.processEvent(Events.UPDATE_VERSION_CHECKED, properties) -} - -// TODO -exports.analyticsOptOut = () => { - const properties = {} - events.processEvent(Events.ANALYTICS_OPT_OUT, properties) -} diff --git a/packages/backend-core/src/events/handlers/org.ts b/packages/backend-core/src/events/handlers/org.ts new file mode 100644 index 0000000000..3f8658f2f6 --- /dev/null +++ b/packages/backend-core/src/events/handlers/org.ts @@ -0,0 +1,30 @@ +import { processEvent } from "../events" +import { Events, VersionCheckedEvent } from "@budibase/types" + +export function nameUpdated() { + const properties = {} + processEvent(Events.ORG_NAME_UPDATED, properties) +} + +export function logoUpdated() { + const properties = {} + processEvent(Events.ORG_LOGO_UPDATED, properties) +} + +export function platformURLUpdated() { + const properties = {} + processEvent(Events.ORG_PLATFORM_URL_UPDATED, properties) +} + +export function versionChecked(version: number) { + const properties: VersionCheckedEvent = { + version, + } + processEvent(Events.UPDATE_VERSION_CHECKED, properties) +} + +// TODO +export function analyticsOptOut() { + const properties = {} + processEvent(Events.ANALYTICS_OPT_OUT, properties) +} diff --git a/packages/backend-core/src/events/handlers/query.js b/packages/backend-core/src/events/handlers/query.js deleted file mode 100644 index d878c79610..0000000000 --- a/packages/backend-core/src/events/handlers/query.js +++ /dev/null @@ -1,35 +0,0 @@ -const events = require("../events") -const { Events } = require("../constants") - -/* eslint-disable */ - -exports.created = (datasource, query) => { - const properties = {} - events.processEvent(Events.QUERY_CREATED, properties) -} - -exports.updated = (datasource, query) => { - const properties = {} - events.processEvent(Events.QUERY_UPDATED, properties) -} - -exports.deleted = (datasource, query) => { - const properties = {} - events.processEvent(Events.QUERY_DELETED, properties) -} - -exports.import = (datasource, importSource, count) => { - const properties = {} - events.processEvent(Events.QUERY_IMPORT, properties) -} - -// TODO -// exports.run = () => { -// const properties = {} -// events.processEvent(Events.QUERY_RUN, properties) -// } - -exports.previewed = datasource => { - const properties = {} - events.processEvent(Events.QUERY_PREVIEWED, properties) -} diff --git a/packages/backend-core/src/events/handlers/query.ts b/packages/backend-core/src/events/handlers/query.ts new file mode 100644 index 0000000000..bc173008bd --- /dev/null +++ b/packages/backend-core/src/events/handlers/query.ts @@ -0,0 +1,48 @@ +import { processEvent } from "../events" +import { + Events, + Datasource, + Query, + QueryCreatedEvent, + QueryUpdatedEvent, + QueryDeletedEvent, + QueryImportedEvent, + QueryPreviewedEvent, +} from "@budibase/types" + +/* eslint-disable */ + +export const created = (datasource: Datasource, query: Query) => { + const properties: QueryCreatedEvent = {} + processEvent(Events.QUERY_CREATED, properties) +} + +export const updated = (datasource: Datasource, query: Query) => { + const properties: QueryUpdatedEvent = {} + processEvent(Events.QUERY_UPDATED, properties) +} + +export const deleted = (datasource: Datasource, query: Query) => { + const properties: QueryDeletedEvent = {} + processEvent(Events.QUERY_DELETED, properties) +} + +export const imported = ( + datasource: Datasource, + importSource: any, + count: any +) => { + const properties: QueryImportedEvent = {} + processEvent(Events.QUERY_IMPORT, properties) +} + +// TODO +// exports.run = () => { +// const properties = {} +// events.processEvent(Events.QUERY_RUN, properties) +// } + +export const previewed = (datasource: Datasource) => { + const properties: QueryPreviewedEvent = {} + processEvent(Events.QUERY_PREVIEWED, properties) +} diff --git a/packages/backend-core/src/events/handlers/role.js b/packages/backend-core/src/events/handlers/role.js deleted file mode 100644 index 1181af01ed..0000000000 --- a/packages/backend-core/src/events/handlers/role.js +++ /dev/null @@ -1,29 +0,0 @@ -const events = require("../events") -const { Events } = require("../constants") - -/* eslint-disable */ - -exports.created = role => { - const properties = {} - events.processEvent(Events.ROLE_CREATED, properties) -} - -exports.updated = role => { - const properties = {} - events.processEvent(Events.ROLE_UPDATED, properties) -} - -exports.deleted = role => { - const properties = {} - events.processEvent(Events.ROLE_DELETED, properties) -} - -exports.assigned = (user, role) => { - const properties = {} - events.processEvent(Events.ROLE_ASSIGNED, properties) -} - -exports.unassigned = (user, role) => { - const properties = {} - events.processEvent(Events.ROLE_UNASSIGNED, properties) -} diff --git a/packages/backend-core/src/events/handlers/role.ts b/packages/backend-core/src/events/handlers/role.ts new file mode 100644 index 0000000000..e41b50389b --- /dev/null +++ b/packages/backend-core/src/events/handlers/role.ts @@ -0,0 +1,38 @@ +import { processEvent } from "../events" +import { + Events, + Role, + RoleAssignedEvent, + RoleCreatedEvent, + RoleDeletedEvent, + RoleUnassignedEvent, + RoleUpdatedEvent, + User, +} from "@budibase/types" + +/* eslint-disable */ + +export function created(role: Role) { + const properties: RoleCreatedEvent = {} + processEvent(Events.ROLE_CREATED, properties) +} + +export function updated(role: Role) { + const properties: RoleUpdatedEvent = {} + processEvent(Events.ROLE_UPDATED, properties) +} + +export function deleted(role: Role) { + const properties: RoleDeletedEvent = {} + processEvent(Events.ROLE_DELETED, properties) +} + +export function assigned(user: User, role: Role) { + const properties: RoleAssignedEvent = {} + processEvent(Events.ROLE_ASSIGNED, properties) +} + +export function unassigned(user: User, role: Role) { + const properties: RoleUnassignedEvent = {} + processEvent(Events.ROLE_UNASSIGNED, properties) +} diff --git a/packages/backend-core/src/events/handlers/row.js b/packages/backend-core/src/events/handlers/row.js deleted file mode 100644 index 28cb45645a..0000000000 --- a/packages/backend-core/src/events/handlers/row.js +++ /dev/null @@ -1,14 +0,0 @@ -const events = require("../events") -const { Events } = require("../constants") - -/* eslint-disable */ - -// exports.created = () => { -// const properties = {} -// events.processEvent(Events.ROW_CREATED, properties) -// } - -exports.import = (table, format, count) => { - const properties = {} - events.processEvent(Events.ROW_IMPORT, properties) -} diff --git a/packages/backend-core/src/events/handlers/row.ts b/packages/backend-core/src/events/handlers/row.ts new file mode 100644 index 0000000000..c5e34f786d --- /dev/null +++ b/packages/backend-core/src/events/handlers/row.ts @@ -0,0 +1,23 @@ +import { processEvent } from "../events" +import { + Events, + RowImportedEvent, + RowImportFormat, + Table, +} from "@budibase/types" + +/* eslint-disable */ + +// exports.created = () => { +// const properties = {} +// events.processEvent(Events.ROW_CREATED, properties) +// } + +export const imported = ( + table: Table, + format: RowImportFormat, + count: number +) => { + const properties: RowImportedEvent = {} + processEvent(Events.ROW_IMPORT, properties) +} diff --git a/packages/backend-core/src/events/handlers/screen.js b/packages/backend-core/src/events/handlers/screen.js deleted file mode 100644 index 0e2b4d268d..0000000000 --- a/packages/backend-core/src/events/handlers/screen.js +++ /dev/null @@ -1,12 +0,0 @@ -const events = require("../events") -const { Events } = require("../constants") - -exports.created = () => { - const properties = {} - events.processEvent(Events.SCREEN_CREATED, properties) -} - -exports.deleted = () => { - const properties = {} - events.processEvent(Events.SCREEN_DELETED, properties) -} diff --git a/packages/backend-core/src/events/handlers/screen.ts b/packages/backend-core/src/events/handlers/screen.ts new file mode 100644 index 0000000000..eade48bf30 --- /dev/null +++ b/packages/backend-core/src/events/handlers/screen.ts @@ -0,0 +1,17 @@ +import { processEvent } from "../events" +import { + Events, + Screen, + ScreenCreatedEvent, + ScreenDeletedEvent, +} from "@budibase/types" + +export function created(screen: Screen) { + const properties: ScreenCreatedEvent = {} + processEvent(Events.SCREEN_CREATED, properties) +} + +export function deleted(screen: Screen) { + const properties: ScreenDeletedEvent = {} + processEvent(Events.SCREEN_DELETED, properties) +} diff --git a/packages/backend-core/src/events/handlers/serve.js b/packages/backend-core/src/events/handlers/serve.js deleted file mode 100644 index 169d7338c2..0000000000 --- a/packages/backend-core/src/events/handlers/serve.js +++ /dev/null @@ -1,19 +0,0 @@ -const events = require("../events") -const { Events } = require("../constants") - -/* eslint-disable */ - -exports.servedBuilder = version => { - const properties = {} - events.processEvent(Events.SERVED_BUILDER, properties) -} - -exports.servedApp = appMetadata => { - const properties = {} - events.processEvent(Events.SERVED_APP, properties) -} - -exports.servedAppPreview = appMetadata => { - const properties = {} - events.processEvent(Events.SERVED_APP_PREVIEW, properties) -} diff --git a/packages/backend-core/src/events/handlers/serve.ts b/packages/backend-core/src/events/handlers/serve.ts new file mode 100644 index 0000000000..e7563432d7 --- /dev/null +++ b/packages/backend-core/src/events/handlers/serve.ts @@ -0,0 +1,25 @@ +import { AppServedEvent } from "./../../../../types/src/events/serve" +import { processEvent } from "../events" +import { + App, + BuilderServedEvent, + Events, + AppPreviewServedEvent, +} from "@budibase/types" + +/* eslint-disable */ + +export function servedBuilder(version: number) { + const properties: BuilderServedEvent = {} + processEvent(Events.SERVED_BUILDER, properties) +} + +export function servedApp(app: App) { + const properties: AppServedEvent = {} + processEvent(Events.SERVED_APP, properties) +} + +export function servedAppPreview(app: App) { + const properties: AppPreviewServedEvent = {} + processEvent(Events.SERVED_APP_PREVIEW, properties) +} diff --git a/packages/backend-core/src/events/handlers/table.js b/packages/backend-core/src/events/handlers/table.js deleted file mode 100644 index e212faad15..0000000000 --- a/packages/backend-core/src/events/handlers/table.js +++ /dev/null @@ -1,35 +0,0 @@ -const events = require("../events") -const { Events } = require("../constants") - -/* eslint-disable */ - -exports.created = table => { - const properties = {} - events.processEvent(Events.TABLE_CREATED, properties) -} - -exports.updated = table => { - const properties = {} - events.processEvent(Events.TABLE_UPDATED, properties) -} - -exports.deleted = table => { - const properties = {} - events.processEvent(Events.TABLE_DELETED, properties) -} - -exports.exported = (table, format) => { - const properties = {} - events.processEvent(Events.TABLE_EXPORTED, properties) -} - -exports.imported = (table, format) => { - const properties = {} - events.processEvent(Events.TABLE_IMPORTED, properties) -} - -// TODO -exports.permissionUpdated = () => { - const properties = {} - events.processEvent(Events.TABLE_PERMISSION_UPDATED, properties) -} diff --git a/packages/backend-core/src/events/handlers/table.ts b/packages/backend-core/src/events/handlers/table.ts new file mode 100644 index 0000000000..5ebadd14fd --- /dev/null +++ b/packages/backend-core/src/events/handlers/table.ts @@ -0,0 +1,45 @@ +import { processEvent } from "../events" +import { + Events, + TableExportFormat, + TableImportFormat, + Table, + TableCreatedEvent, + TableUpdatedEvent, + TableDeletedEvent, + TableExportedEvent, + TableImportedEvent, +} from "@budibase/types" + +/* eslint-disable */ + +export function created(table: Table) { + const properties: TableCreatedEvent = {} + processEvent(Events.TABLE_CREATED, properties) +} + +export function updated(table: Table) { + const properties: TableUpdatedEvent = {} + processEvent(Events.TABLE_UPDATED, properties) +} + +export function deleted(table: Table) { + const properties: TableDeletedEvent = {} + processEvent(Events.TABLE_DELETED, properties) +} + +export function exported(table: Table, format: TableExportFormat) { + const properties: TableExportedEvent = {} + processEvent(Events.TABLE_EXPORTED, properties) +} + +export function imported(table: Table, format: TableImportFormat) { + const properties: TableImportedEvent = {} + processEvent(Events.TABLE_IMPORTED, properties) +} + +// TODO +export function permissionUpdated() { + const properties = {} + processEvent(Events.TABLE_PERMISSION_UPDATED, properties) +} diff --git a/packages/backend-core/src/events/handlers/user.js b/packages/backend-core/src/events/handlers/user.js deleted file mode 100644 index ca6d541a7d..0000000000 --- a/packages/backend-core/src/events/handlers/user.js +++ /dev/null @@ -1,75 +0,0 @@ -const events = require("../events") -const { Events } = require("../constants") - -/* eslint-disable */ - -exports.created = user => { - const properties = {} - events.processEvent(Events.USER_CREATED, properties) -} - -exports.updated = user => { - const properties = {} - events.processEvent(Events.USER_UPDATED, properties) -} - -exports.deleted = user => { - const properties = {} - events.processEvent(Events.USER_DELETED, properties) -} - -// PERMISSIONS - -exports.permissionAdminAssigned = user => { - const properties = {} - events.processEvent(Events.USER_PERMISSION_ADMIN_ASSIGNED, properties) -} - -exports.permissionAdminRemoved = user => { - const properties = {} - events.processEvent(Events.USER_PERMISSION_ADMIN_REMOVED, properties) -} - -exports.permissionBuilderAssigned = user => { - const properties = {} - events.processEvent(Events.USER_PERMISSION_BUILDER_ASSIGNED, properties) -} - -exports.permissionBuilderRemoved = user => { - const properties = {} - events.processEvent(Events.USER_PERMISSION_BUILDER_REMOVED, properties) -} - -// INVITE - -exports.invited = userInfo => { - const properties = {} - events.processEvent(Events.USER_INVITED, properties) -} - -exports.inviteAccepted = user => { - const properties = {} - events.processEvent(Events.USER_INVITED_ACCEPTED, properties) -} - -// PASSWORD - -exports.passwordForceReset = user => { - const properties = {} - events.processEvent(Events.USER_PASSWORD_FORCE_RESET, properties) -} - -exports.passwordUpdated = user => { - const properties = {} - events.processEvent(Events.USER_PASSWORD_UPDATED, properties) -} - -exports.passwordResetRequested = user => { - const properties = {} - events.processEvent(Events.USER_PASSWORD_RESET_REQUESTED, properties) -} - -exports.passwordReset = user => { - const properties = {} - events.processEvent(Events.USER_PASSWORD_RESET, properties) -} diff --git a/packages/backend-core/src/events/handlers/user.ts b/packages/backend-core/src/events/handlers/user.ts new file mode 100644 index 0000000000..6328059cb1 --- /dev/null +++ b/packages/backend-core/src/events/handlers/user.ts @@ -0,0 +1,89 @@ +import { processEvent } from "../events" +import { + Events, + User, + UserCreatedEvent, + UserDeletedEvent, + UserInviteAcceptedEvent, + UserInvitedEvent, + UserPasswordForceResetEvent, + UserPasswordResetEvent, + UserPasswordResetRequestedEvent, + UserPasswordUpdatedEvent, + UserPermissionAssignedEvent, + UserPermissionRemovedEvent, + UserUpdatedEvent, +} from "@budibase/types" + +/* eslint-disable */ + +export function created(user: User) { + const properties: UserCreatedEvent = {} + processEvent(Events.USER_CREATED, properties) +} + +export function updated(user: User) { + const properties: UserUpdatedEvent = {} + processEvent(Events.USER_UPDATED, properties) +} + +export function deleted(user: User) { + const properties: UserDeletedEvent = {} + processEvent(Events.USER_DELETED, properties) +} + +// PERMISSIONS + +export function permissionAdminAssigned(user: User) { + const properties: UserPermissionAssignedEvent = {} + processEvent(Events.USER_PERMISSION_ADMIN_ASSIGNED, properties) +} + +export function permissionAdminRemoved(user: User) { + const properties: UserPermissionRemovedEvent = {} + processEvent(Events.USER_PERMISSION_ADMIN_REMOVED, properties) +} + +export function permissionBuilderAssigned(user: User) { + const properties: UserPermissionAssignedEvent = {} + processEvent(Events.USER_PERMISSION_BUILDER_ASSIGNED, properties) +} + +export function permissionBuilderRemoved(user: User) { + const properties: UserPermissionRemovedEvent = {} + processEvent(Events.USER_PERMISSION_BUILDER_REMOVED, properties) +} + +// INVITE + +export function invited(userInfo: any) { + const properties: UserInvitedEvent = {} + processEvent(Events.USER_INVITED, properties) +} + +export function inviteAccepted(user: User) { + const properties: UserInviteAcceptedEvent = {} + processEvent(Events.USER_INVITED_ACCEPTED, properties) +} + +// PASSWORD + +export function passwordForceReset(user: User) { + const properties: UserPasswordForceResetEvent = {} + processEvent(Events.USER_PASSWORD_FORCE_RESET, properties) +} + +export function passwordUpdated(user: User) { + const properties: UserPasswordUpdatedEvent = {} + processEvent(Events.USER_PASSWORD_UPDATED, properties) +} + +export function passwordResetRequested(user: User) { + const properties: UserPasswordResetRequestedEvent = {} + processEvent(Events.USER_PASSWORD_RESET_REQUESTED, properties) +} + +export function passwordReset(user: User) { + const properties: UserPasswordResetEvent = {} + processEvent(Events.USER_PASSWORD_RESET, properties) +} diff --git a/packages/backend-core/src/events/handlers/view.js b/packages/backend-core/src/events/handlers/view.js deleted file mode 100644 index 9c1c77ad63..0000000000 --- a/packages/backend-core/src/events/handlers/view.js +++ /dev/null @@ -1,54 +0,0 @@ -const events = require("../events") -const { Events } = require("../constants") - -/* eslint-disable */ - -exports.created = () => { - const properties = {} - events.processEvent(Events.VIEW_CREATED, properties) -} - -exports.updated = () => { - const properties = {} - events.processEvent(Events.VIEW_UPDATED, properties) -} - -exports.deleted = () => { - const properties = {} - events.processEvent(Events.VIEW_DELETED, properties) -} - -exports.exported = (table, format) => { - const properties = {} - events.processEvent(Events.VIEW_EXPORTED, properties) -} - -exports.filterCreated = () => { - const properties = {} - events.processEvent(Events.VIEW_FILTER_CREATED, properties) -} - -exports.filterUpdated = () => { - const properties = {} - events.processEvent(Events.VIEW_FILTER_UPDATED, properties) -} - -exports.filterDeleted = () => { - const properties = {} - events.processEvent(Events.VIEW_FILTER_DELETED, properties) -} - -exports.calculationCreated = () => { - const properties = {} - events.processEvent(Events.VIEW_CALCULATION_CREATED, properties) -} - -exports.calculationUpdated = () => { - const properties = {} - events.processEvent(Events.VIEW_CALCULATION_UPDATED, properties) -} - -exports.calculationDeleted = () => { - const properties = {} - events.processEvent(Events.VIEW_CALCULATION_DELETED, properties) -} diff --git a/packages/backend-core/src/events/handlers/view.ts b/packages/backend-core/src/events/handlers/view.ts new file mode 100644 index 0000000000..33975e64f8 --- /dev/null +++ b/packages/backend-core/src/events/handlers/view.ts @@ -0,0 +1,66 @@ +import { processEvent } from "../events" +import { + Events, + ViewCalculationCreatedEvent, + ViewCalculationDeletedEvent, + ViewCalculationUpdatedEvent, + ViewCreatedEvent, + ViewDeletedEvent, + ViewExportedEvent, + ViewFilterCreatedEvent, + ViewFilterDeletedEvent, + ViewFilterUpdatedEvent, + ViewUpdatedEvent, +} from "@budibase/types" + +/* eslint-disable */ + +export function created(view: View) { + const properties: ViewCreatedEvent = {} + processEvent(Events.VIEW_CREATED, properties) +} + +export function updated(view: View) { + const properties: ViewUpdatedEvent = {} + processEvent(Events.VIEW_UPDATED, properties) +} + +export function deleted() { + const properties: ViewDeletedEvent = {} + processEvent(Events.VIEW_DELETED, properties) +} + +export function exported(table, format) { + const properties: ViewExportedEvent = {} + processEvent(Events.VIEW_EXPORTED, properties) +} + +export function filterCreated() { + const properties: ViewFilterCreatedEvent = {} + processEvent(Events.VIEW_FILTER_CREATED, properties) +} + +export function filterUpdated() { + const properties: ViewFilterUpdatedEvent = {} + processEvent(Events.VIEW_FILTER_UPDATED, properties) +} + +export function filterDeleted() { + const properties: ViewFilterDeletedEvent = {} + processEvent(Events.VIEW_FILTER_DELETED, properties) +} + +export function calculationCreated() { + const properties: ViewCalculationCreatedEvent = {} + processEvent(Events.VIEW_CALCULATION_CREATED, properties) +} + +export function calculationUpdated() { + const properties: ViewCalculationUpdatedEvent = {} + processEvent(Events.VIEW_CALCULATION_UPDATED, properties) +} + +export function calculationDeleted() { + const properties: ViewCalculationDeletedEvent = {} + processEvent(Events.VIEW_CALCULATION_DELETED, properties) +} diff --git a/packages/backend-core/src/events/index.js b/packages/backend-core/src/events/index.js index 024194081b..92fa300a33 100644 --- a/packages/backend-core/src/events/index.js +++ b/packages/backend-core/src/events/index.js @@ -1,5 +1 @@ -const handlers = require("./handlers") - -module.exports = { - ...handlers, -} +export * from "./handlers" diff --git a/packages/backend-core/src/index.js b/packages/backend-core/src/index.js index b255567639..2752312c45 100644 --- a/packages/backend-core/src/index.js +++ b/packages/backend-core/src/index.js @@ -1,5 +1,6 @@ const db = require("./db") const errors = require("./errors") +import * as events from "./events" module.exports = { init(opts = {}) { @@ -23,7 +24,7 @@ module.exports = { accounts: require("./cloud/accounts"), tenancy: require("./tenancy"), featureFlags: require("./featureFlags"), - events: require("./events"), + events, analytics: require("./analytics"), sessions: require("./security/sessions"), deprovisioning: require("./context/deprovision"), diff --git a/packages/types/.gitignore b/packages/types/.gitignore new file mode 100644 index 0000000000..b4773960f9 --- /dev/null +++ b/packages/types/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +dist +coverage/ diff --git a/packages/types/package.json b/packages/types/package.json new file mode 100644 index 0000000000..248f18708a --- /dev/null +++ b/packages/types/package.json @@ -0,0 +1,18 @@ +{ + "name": "@budibase/types", + "version": "1.0.126-alpha.0", + "description": "Budibase types", + "main": "src/index.ts", + "types": "src/index.ts", + "author": "Budibase", + "license": "GPL-3.0", + "scripts": { + "build": "rimraf dist/ && tsc -p tsconfig.json" + }, + "jest": { + }, + "devDependencies": { + "@types/node": "^15.12.4", + "typescript": "^4.5.5" + } +} diff --git a/packages/types/src/api/public/.keep b/packages/types/src/api/public/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/types/src/api/web/.keep b/packages/types/src/api/web/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/types/src/documents/account/account.ts b/packages/types/src/documents/account/account.ts new file mode 100644 index 0000000000..bfafd9fd85 --- /dev/null +++ b/packages/types/src/documents/account/account.ts @@ -0,0 +1 @@ +export interface Account {} diff --git a/packages/types/src/documents/account/index.ts b/packages/types/src/documents/account/index.ts new file mode 100644 index 0000000000..20b4e77def --- /dev/null +++ b/packages/types/src/documents/account/index.ts @@ -0,0 +1 @@ +export * from "./account" diff --git a/packages/types/src/documents/app/app.ts b/packages/types/src/documents/app/app.ts new file mode 100644 index 0000000000..a27344a799 --- /dev/null +++ b/packages/types/src/documents/app/app.ts @@ -0,0 +1 @@ +export interface App {} diff --git a/packages/types/src/documents/app/automation.ts b/packages/types/src/documents/app/automation.ts new file mode 100644 index 0000000000..80a2afa543 --- /dev/null +++ b/packages/types/src/documents/app/automation.ts @@ -0,0 +1,5 @@ +export interface Automation {} + +export interface AutomationStep {} + +export interface AutomationTrigger {} diff --git a/packages/types/src/documents/app/datasource.ts b/packages/types/src/documents/app/datasource.ts new file mode 100644 index 0000000000..03e8ce7fe8 --- /dev/null +++ b/packages/types/src/documents/app/datasource.ts @@ -0,0 +1 @@ +export interface Datasource {} diff --git a/packages/types/src/documents/app/index.ts b/packages/types/src/documents/app/index.ts new file mode 100644 index 0000000000..9db35940bd --- /dev/null +++ b/packages/types/src/documents/app/index.ts @@ -0,0 +1,9 @@ +export * from "./app" +export * from "./automation" +export * from "./datasource" +export * from "./layout" +export * from "./query" +export * from "./role" +export * from "./table" +export * from "./screen" +export * from "./view" diff --git a/packages/types/src/documents/app/layout.ts b/packages/types/src/documents/app/layout.ts new file mode 100644 index 0000000000..4f0f85bd87 --- /dev/null +++ b/packages/types/src/documents/app/layout.ts @@ -0,0 +1 @@ +export interface Layout {} diff --git a/packages/types/src/documents/app/query.ts b/packages/types/src/documents/app/query.ts new file mode 100644 index 0000000000..ba038af326 --- /dev/null +++ b/packages/types/src/documents/app/query.ts @@ -0,0 +1 @@ +export interface Query {} diff --git a/packages/types/src/documents/app/role.ts b/packages/types/src/documents/app/role.ts new file mode 100644 index 0000000000..304e11b799 --- /dev/null +++ b/packages/types/src/documents/app/role.ts @@ -0,0 +1 @@ +export interface Role {} diff --git a/packages/types/src/documents/app/screen.ts b/packages/types/src/documents/app/screen.ts new file mode 100644 index 0000000000..1a88fe8db3 --- /dev/null +++ b/packages/types/src/documents/app/screen.ts @@ -0,0 +1 @@ +export interface Screen {} diff --git a/packages/types/src/documents/app/table.ts b/packages/types/src/documents/app/table.ts new file mode 100644 index 0000000000..74923f6554 --- /dev/null +++ b/packages/types/src/documents/app/table.ts @@ -0,0 +1 @@ +export interface Table {} diff --git a/packages/types/src/documents/app/view.ts b/packages/types/src/documents/app/view.ts new file mode 100644 index 0000000000..038e718355 --- /dev/null +++ b/packages/types/src/documents/app/view.ts @@ -0,0 +1 @@ +export interface View {} diff --git a/packages/types/src/documents/global/config.ts b/packages/types/src/documents/global/config.ts new file mode 100644 index 0000000000..6828cabc30 --- /dev/null +++ b/packages/types/src/documents/global/config.ts @@ -0,0 +1 @@ +export interface SMTPConfig {} diff --git a/packages/types/src/documents/global/index.ts b/packages/types/src/documents/global/index.ts new file mode 100644 index 0000000000..da01c73304 --- /dev/null +++ b/packages/types/src/documents/global/index.ts @@ -0,0 +1,2 @@ +export * from "./config" +export * from "./user" diff --git a/packages/types/src/documents/global/user.ts b/packages/types/src/documents/global/user.ts new file mode 100644 index 0000000000..e918b910d0 --- /dev/null +++ b/packages/types/src/documents/global/user.ts @@ -0,0 +1 @@ +export interface User {} diff --git a/packages/types/src/documents/index.ts b/packages/types/src/documents/index.ts new file mode 100644 index 0000000000..7938052425 --- /dev/null +++ b/packages/types/src/documents/index.ts @@ -0,0 +1,4 @@ +export * from "./account" +export * from "./app" +export * from "./global" +export * from "./platform" diff --git a/packages/types/src/documents/platform/index.ts b/packages/types/src/documents/platform/index.ts new file mode 100644 index 0000000000..ba329f1bd0 --- /dev/null +++ b/packages/types/src/documents/platform/index.ts @@ -0,0 +1 @@ +export * from "./info" diff --git a/packages/types/src/documents/platform/info.ts b/packages/types/src/documents/platform/info.ts new file mode 100644 index 0000000000..782ce5dd09 --- /dev/null +++ b/packages/types/src/documents/platform/info.ts @@ -0,0 +1 @@ +export interface GlobalInfo {} diff --git a/packages/types/src/events/app.ts b/packages/types/src/events/app.ts new file mode 100644 index 0000000000..429fb50114 --- /dev/null +++ b/packages/types/src/events/app.ts @@ -0,0 +1,21 @@ +export interface AppCreatedEvent {} + +export interface AppUpdatedEvent {} + +export interface AppDeletedEvent {} + +export interface AppPublishedEvent {} + +export interface AppUnpublishedEvent {} + +export interface AppFileImportedEvent {} + +export interface AppTemplateImportedEvent {} + +export interface AppVersionUpdatedEvent {} + +export interface AppVersionRevertedEvent {} + +export interface AppRevertedEvent {} + +export interface AppExportedEvent {} diff --git a/packages/types/src/events/auth.ts b/packages/types/src/events/auth.ts new file mode 100644 index 0000000000..aaa2cdd834 --- /dev/null +++ b/packages/types/src/events/auth.ts @@ -0,0 +1,16 @@ +export type LoginSource = "local" | "sso" +export type SSOType = "oidc" | "google" + +export interface LoginEvent { + source: LoginSource +} + +export interface LogoutEvent {} + +export interface SSOCreatedEvent {} + +export interface SSOUpdatedEvent {} + +export interface SSOActivatedEvent {} + +export interface SSODeactivatedEvent {} diff --git a/packages/types/src/events/automation.ts b/packages/types/src/events/automation.ts new file mode 100644 index 0000000000..699d130618 --- /dev/null +++ b/packages/types/src/events/automation.ts @@ -0,0 +1,11 @@ +export interface AutomationCreatedEvent {} + +export interface AutomationDeletedEvent {} + +export interface AutomationTestedEvent {} + +export interface AutomationStepCreatedEvent {} + +export interface AutomationStepDeletedEvent {} + +export interface AutomationTriggerUpdatedEvent {} diff --git a/packages/types/src/events/config.ts b/packages/types/src/events/config.ts new file mode 100644 index 0000000000..de3c5dc2dd --- /dev/null +++ b/packages/types/src/events/config.ts @@ -0,0 +1,3 @@ +export interface SMTPCreatedEvent {} + +export interface SMTPUpdatedEvent {} diff --git a/packages/types/src/events/datasource.ts b/packages/types/src/events/datasource.ts new file mode 100644 index 0000000000..2eca9e30f5 --- /dev/null +++ b/packages/types/src/events/datasource.ts @@ -0,0 +1,5 @@ +export interface DatasourceCreatedEvent {} + +export interface DatasourceUpdatedEvent {} + +export interface DatasourceDeletedEvent {} diff --git a/packages/types/src/events/events.ts b/packages/types/src/events/events.ts new file mode 100644 index 0000000000..9c6a6031e2 --- /dev/null +++ b/packages/types/src/events/events.ts @@ -0,0 +1,145 @@ +export enum Events { + // USER + USER_CREATED = "user:created", + USER_UPDATED = "user:updated", + USER_DELETED = "user:deleted", + + // USER / PERMISSIONS + USER_PERMISSION_ADMIN_ASSIGNED = "user:admin:assigned", + USER_PERMISSION_ADMIN_REMOVED = "user:admin:removed", + USER_PERMISSION_BUILDER_ASSIGNED = "user:builder:assigned", + USER_PERMISSION_BUILDER_REMOVED = "userbuilder:removed", + + // USER / INVITE + USER_INVITED = "user:invited", + USER_INVITED_ACCEPTED = "user:invite:accepted", + + // USER / PASSWORD + USER_PASSWORD_FORCE_RESET = "user:password:force:reset", + USER_PASSWORD_UPDATED = "user:password:updated", + USER_PASSWORD_RESET_REQUESTED = "user:password:reset:requested", + USER_PASSWORD_RESET = "user:password:reset", + + // EMAIL + EMAIL_SMTP_CREATED = "email:smtp:created", + EMAIL_SMTP_UPDATED = "email:smtp:updated", + + // AUTH + AUTH_SSO_CREATED = "auth:sso:created", + AUTH_SSO_UPDATED = "auth:sso:updated", + AUTH_SSO_ACTIVATED = "auth:sso:activated", + AUTH_SSO_DEACTIVATED = "auth:sso:deactivated", + AUTH_LOGIN = "auth:login", + AUTH_LOGOUT = "auth:logout", + + // ORG + ORG_NAME_UPDATED = "org:info:name:updated", + ORG_LOGO_UPDATED = "org:info:logo:updated", + ORG_PLATFORM_URL_UPDATED = "org:platformurl:updated", + + // ORG / UPDATE + UPDATE_VERSION_CHECKED = "version:checked", + + // ORG / ANALYTICS + ANALYTICS_OPT_OUT = "analytics:opt:out", + + // APP + APP_CREATED = "app:created", + APP_UPDATED = "app:updated", + APP_DELETED = "app:deleted", + APP_PUBLISHED = "app:published", + APP_UNPUBLISHED = "app:unpublished", + APP_TEMPLATE_IMPORTED = "app:template:imported", + APP_FILE_IMPORTED = "app:file:imported", + APP_VERSION_UPDATED = "app:version:updated", + APP_VERSION_REVERTED = "app:version:reverted", + APP_REVERTED = "app:reverted", + APP_EXPORTED = "app:exported", + + // ROLE + ROLE_CREATED = "role:created", + ROLE_UPDATED = "role:updated", + ROLE_DELETED = "role:deleted", + ROLE_ASSIGNED = "role:assigned", + ROLE_UNASSIGNED = "role:unassigned", + + // SERVE + SERVED_BUILDER = "served:builder", + SERVED_APP = "served:app", + SERVED_APP_PREVIEW = "served:app:preview", + + // DATASOURCE + DATASOURCE_CREATED = "datasource:created", + DATASOURCE_UPDATED = "datasource:updated", + DATASOURCE_DELETED = "datasource:deleted", + + // QUERY + QUERY_CREATED = "query:created", + QUERY_UPDATED = "query:updated", + QUERY_DELETED = "query:deleted", + QUERY_IMPORT = "query:import", + // QUERY_RUN = "query:run", + QUERY_PREVIEWED = "query:previewed", + + // TABLE + TABLE_CREATED = "table:created", + TABLE_UPDATED = "table:updated", + TABLE_DELETED = "table:deleted", + TABLE_EXPORTED = "table:exported", + TABLE_IMPORTED = "table:imported", + TABLE_DATA_IMPORTED = "table:data:imported", + TABLE_PERMISSION_UPDATED = "table:permission:updated", + + // VIEW + VIEW_CREATED = "view:created", + VIEW_UPDATED = "view:updated", + VIEW_DELETED = "view:deleted", + VIEW_EXPORTED = "view:exported", + VIEW_FILTER_CREATED = "view:filter:created", + VIEW_FILTER_UPDATED = "view:filter:updated", + VIEW_FILTER_DELETED = "view:filter:deleted", + VIEW_CALCULATION_CREATED = "view:calculation:created", + VIEW_CALCULATION_UPDATED = "view:calculation:updated", + VIEW_CALCULATION_DELETED = "view:calculation:deleted", + + // ROW + // ROW_CREATED = "row:created", + ROW_IMPORT = "row:import", + + // COMPONENT + COMPONENT_CREATED = "component:created", + COMPONENT_DELETED = "component:deleted", + + // SCREEN + SCREEN_CREATED = "screen:created", + SCREEN_DELETED = "screen:deleted", + + // LAYOUT + LAYOUT_CREATED = "layout:created", + LAYOUT_DELETED = "layout:deleted", + + // AUTOMATION + AUTOMATION_CREATED = "automation:created", + AUTOMATION_DELETED = "automation:deleted", + AUTOMATION_TESTED = "automation:tested", + // AUTOMATION_RUN = "automation:run", + AUTOMATION_STEP_CREATED = "automation:step:created", + AUTOMATION_STEP_DELETED = "automation:step:deleted", + AUTOMATION_TRIGGER_UPDATED = "automation:trigger:updated", + + // LICENSE + LICENSE_UPGRADED = "license:upgraded", + LICENSE_DOWNGRADED = "license:downgraded", + LICENSE_UPDATED = "license:updated", + LICENSE_ACTIVATED = "license:activated", + LICENSE_QUOTA_EXCEEDED = "license:quota:exceeded", + + // ACCOUNT + ACCOUNT_CREATED = "account:created", + ACCOUNT_DELETED = "account:deleted", + ACCOUNT_VERIFIED = "account:verified", +} + +export type RowImportFormat = "csv" +export type TableExportFormat = "json" | "csv" +export type TableImportFormat = "csv" diff --git a/packages/types/src/events/index.ts b/packages/types/src/events/index.ts new file mode 100644 index 0000000000..22a3f9af8d --- /dev/null +++ b/packages/types/src/events/index.ts @@ -0,0 +1,17 @@ +export * from "./app" +export * from "./auth" +export * from "./automation" +export * from "./config" +export * from "./datasource" +export * from "./events" +export * from "./layout" +export * from "./license" +export * from "./org" +export * from "./query" +export * from "./role" +export * from "./row" +export * from "./screen" +export * from "./serve" +export * from "./table" +export * from "./user" +export * from "./view" diff --git a/packages/types/src/events/layout.ts b/packages/types/src/events/layout.ts new file mode 100644 index 0000000000..fbfac2c411 --- /dev/null +++ b/packages/types/src/events/layout.ts @@ -0,0 +1,3 @@ +export interface LayoutCreatedEvent {} + +export interface LayoutDeletedEvent {} diff --git a/packages/types/src/events/license.ts b/packages/types/src/events/license.ts new file mode 100644 index 0000000000..2fa1f0b6cf --- /dev/null +++ b/packages/types/src/events/license.ts @@ -0,0 +1,9 @@ +export interface LicenseUpgradedEvent {} + +export interface LicenseDowngradedEvent {} + +export interface LicenseUpdatedEvent {} + +export interface LicenseActivatedEvent {} + +export interface LicenseQuotaExceededEvent {} diff --git a/packages/types/src/events/org.ts b/packages/types/src/events/org.ts new file mode 100644 index 0000000000..8d0243366e --- /dev/null +++ b/packages/types/src/events/org.ts @@ -0,0 +1 @@ +export interface VersionCheckedEvent {} diff --git a/packages/types/src/events/query.ts b/packages/types/src/events/query.ts new file mode 100644 index 0000000000..4a89c38f79 --- /dev/null +++ b/packages/types/src/events/query.ts @@ -0,0 +1,9 @@ +export interface QueryCreatedEvent {} + +export interface QueryUpdatedEvent {} + +export interface QueryDeletedEvent {} + +export interface QueryImportedEvent {} + +export interface QueryPreviewedEvent {} diff --git a/packages/types/src/events/role.ts b/packages/types/src/events/role.ts new file mode 100644 index 0000000000..45e6c84938 --- /dev/null +++ b/packages/types/src/events/role.ts @@ -0,0 +1,9 @@ +export interface RoleCreatedEvent {} + +export interface RoleUpdatedEvent {} + +export interface RoleDeletedEvent {} + +export interface RoleAssignedEvent {} + +export interface RoleUnassignedEvent {} diff --git a/packages/types/src/events/row.ts b/packages/types/src/events/row.ts new file mode 100644 index 0000000000..3d4d093e80 --- /dev/null +++ b/packages/types/src/events/row.ts @@ -0,0 +1 @@ +export interface RowImportedEvent {} diff --git a/packages/types/src/events/screen.ts b/packages/types/src/events/screen.ts new file mode 100644 index 0000000000..5c7e2952e7 --- /dev/null +++ b/packages/types/src/events/screen.ts @@ -0,0 +1,3 @@ +export interface ScreenCreatedEvent {} + +export interface ScreenDeletedEvent {} diff --git a/packages/types/src/events/serve.ts b/packages/types/src/events/serve.ts new file mode 100644 index 0000000000..f3fd21dd15 --- /dev/null +++ b/packages/types/src/events/serve.ts @@ -0,0 +1,5 @@ +export interface BuilderServedEvent {} + +export interface AppServedEvent {} + +export interface AppPreviewServedEvent {} diff --git a/packages/types/src/events/table.ts b/packages/types/src/events/table.ts new file mode 100644 index 0000000000..2a3c5ef2aa --- /dev/null +++ b/packages/types/src/events/table.ts @@ -0,0 +1,9 @@ +export interface TableCreatedEvent {} + +export interface TableUpdatedEvent {} + +export interface TableDeletedEvent {} + +export interface TableExportedEvent {} + +export interface TableImportedEvent {} diff --git a/packages/types/src/events/user.ts b/packages/types/src/events/user.ts new file mode 100644 index 0000000000..df75dd1158 --- /dev/null +++ b/packages/types/src/events/user.ts @@ -0,0 +1,21 @@ +export interface UserCreatedEvent {} + +export interface UserUpdatedEvent {} + +export interface UserDeletedEvent {} + +export interface UserPermissionAssignedEvent {} + +export interface UserPermissionRemovedEvent {} + +export interface UserInvitedEvent {} + +export interface UserInviteAcceptedEvent {} + +export interface UserPasswordForceResetEvent {} + +export interface UserPasswordUpdatedEvent {} + +export interface UserPasswordResetRequestedEvent {} + +export interface UserPasswordResetEvent {} diff --git a/packages/types/src/events/view.ts b/packages/types/src/events/view.ts new file mode 100644 index 0000000000..eec8992002 --- /dev/null +++ b/packages/types/src/events/view.ts @@ -0,0 +1,19 @@ +export interface ViewCreatedEvent {} + +export interface ViewUpdatedEvent {} + +export interface ViewDeletedEvent {} + +export interface ViewExportedEvent {} + +export interface ViewFilterCreatedEvent {} + +export interface ViewFilterUpdatedEvent {} + +export interface ViewFilterDeletedEvent {} + +export interface ViewCalculationCreatedEvent {} + +export interface ViewCalculationUpdatedEvent {} + +export interface ViewCalculationDeletedEvent {} diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts new file mode 100644 index 0000000000..b6c01a2fe5 --- /dev/null +++ b/packages/types/src/index.ts @@ -0,0 +1,3 @@ +export * from "./documents" +export * from "./events" +export * from "./licensing" diff --git a/packages/types/src/licensing/index.ts b/packages/types/src/licensing/index.ts new file mode 100644 index 0000000000..e6080073b7 --- /dev/null +++ b/packages/types/src/licensing/index.ts @@ -0,0 +1 @@ +export * from "./license" diff --git a/packages/types/src/licensing/license.ts b/packages/types/src/licensing/license.ts new file mode 100644 index 0000000000..ed2e0a0245 --- /dev/null +++ b/packages/types/src/licensing/license.ts @@ -0,0 +1 @@ +export interface License {} diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json new file mode 100644 index 0000000000..86a066fec8 --- /dev/null +++ b/packages/types/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "lib": ["es2020"], + "outDir": "dist", + "strict": true, + "noImplicitAny": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "incremental": true, + "types": [ "node"], + }, + "include": [ + "**/*.ts", + ] +} diff --git a/packages/types/yarn.lock b/packages/types/yarn.lock new file mode 100644 index 0000000000..536b5c836c --- /dev/null +++ b/packages/types/yarn.lock @@ -0,0 +1,13 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/node@^15.12.4": + version "15.14.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.9.tgz#bc43c990c3c9be7281868bbc7b8fdd6e2b57adfa" + integrity sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A== + +typescript@^4.5.5: + version "4.6.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9" + integrity sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg== From 3b9142f103b9ec2e5ea041ed14a0ad15b3cb0e9e Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 3 May 2022 22:58:19 +0100 Subject: [PATCH 32/75] Build and test fixes --- packages/backend-core/package.json | 21 +- .../scripts/{jestSetup.js => jestSetup.ts} | 6 +- packages/backend-core/src/db/utils.ts | 2 +- .../src/{environment.js => environment.ts} | 6 +- .../backend-core/src/events/handlers/serve.ts | 2 +- .../backend-core/src/events/handlers/view.ts | 5 +- .../backend-core/src/{index.js => index.ts} | 10 +- .../src/objectStore/{index.js => index.ts} | 102 +- .../backend-core/src/tests/utilities/index.js | 7 - .../backend-core/src/tests/utilities/index.ts | 2 + .../src/tests/utilities/mocks/events.js | 4 +- .../src/tests/utilities/structures/index.js | 8 - .../src/tests/utilities/structures/index.ts | 3 + .../src/tests/utilities/structures/koa.js | 5 - .../src/tests/utilities/structures/koa.ts | 5 + packages/backend-core/testUtils.js | 1 - packages/backend-core/testUtils.ts | 1 + packages/backend-core/yarn.lock | 2571 +++++++---------- packages/server/package.json | 1 + .../server/src/api/controllers/application.ts | 46 +- .../src/api/controllers/deploy/index.ts | 8 +- .../src/api/controllers/query/import/index.ts | 4 +- .../query/import/tests/index.spec.js | 4 +- .../server/src/api/controllers/table/utils.ts | 2 +- .../src/api/routes/tests/application.spec.js | 4 +- .../server/src/api/routes/tests/table.spec.js | 8 +- packages/server/tsconfig.json | 2 +- packages/types/package.json | 2 +- packages/types/src/documents/app/app.ts | 4 +- packages/worker/tsconfig.json | 5 +- packages/worker/yarn.lock | 60 +- 31 files changed, 1314 insertions(+), 1597 deletions(-) rename packages/backend-core/scripts/{jestSetup.js => jestSetup.ts} (69%) rename packages/backend-core/src/{environment.js => environment.ts} (94%) rename packages/backend-core/src/{index.js => index.ts} (87%) rename packages/backend-core/src/objectStore/{index.js => index.ts} (73%) delete mode 100644 packages/backend-core/src/tests/utilities/index.js create mode 100644 packages/backend-core/src/tests/utilities/index.ts delete mode 100644 packages/backend-core/src/tests/utilities/structures/index.js create mode 100644 packages/backend-core/src/tests/utilities/structures/index.ts delete mode 100644 packages/backend-core/src/tests/utilities/structures/koa.js create mode 100644 packages/backend-core/src/tests/utilities/structures/koa.ts delete mode 100644 packages/backend-core/testUtils.js create mode 100644 packages/backend-core/testUtils.ts diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 3b22bd5b2e..5d95315006 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -2,7 +2,7 @@ "name": "@budibase/backend-core", "version": "1.0.126-alpha.0", "description": "Budibase backend core libraries used in server and worker", - "main": "src/index.js", + "main": "src/index.ts", "types": "dist/src/index.d.ts", "author": "Budibase", "license": "GPL-3.0", @@ -36,22 +36,29 @@ "zlib": "^1.0.5" }, "jest": { + "preset": "ts-jest", + "testEnvironment": "node", "setupFiles": [ - "./scripts/jestSetup.js" + "./scripts/jestSetup.ts" ] }, "devDependencies": { + "@shopify/jest-koa-mocks": "^3.1.5", + "@budibase/types": "^1.0.126-alpha.0", "@types/jest": "^27.4.1", + "@types/koa": "^2.13.3", "@types/node": "^15.12.4", "@types/node-fetch": "^2.6.1", - "@budibase/types": "^1.0.126-alpha.0", - "typescript": "^4.5.5", - "@shopify/jest-koa-mocks": "^3.1.5", + "@types/tar-fs": "^2.0.1", + "@types/uuid": "^8.3.4", "ioredis-mock": "^5.5.5", - "jest": "^26.6.3", + "jest": "^27.0.3", + "koa": "2.7.0", "pouchdb-adapter-memory": "^7.2.2", "pouchdb-all-dbs": "^1.0.2", - "timekeeper": "^2.2.0" + "timekeeper": "^2.2.0", + "ts-jest": "^27.0.3", + "typescript": "^4.5.5" }, "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc" } diff --git a/packages/backend-core/scripts/jestSetup.js b/packages/backend-core/scripts/jestSetup.ts similarity index 69% rename from packages/backend-core/scripts/jestSetup.js rename to packages/backend-core/scripts/jestSetup.ts index fcb00e64e3..690103abce 100644 --- a/packages/backend-core/scripts/jestSetup.js +++ b/packages/backend-core/scripts/jestSetup.ts @@ -1,9 +1,9 @@ -const env = require("../src/environment") -const { mocks } = require("../testUtils") +import env from "../src/environment" +import { mocks } from "../testUtils" // mock all dates to 2020-01-01T00:00:00.000Z // use tk.reset() to use real dates in individual tests -const tk = require("timekeeper") +import tk from "timekeeper" tk.freeze(mocks.date.MOCK_DATE) env._set("SELF_HOSTED", "1") diff --git a/packages/backend-core/src/db/utils.ts b/packages/backend-core/src/db/utils.ts index fe5bfe52e3..e0cb675e99 100644 --- a/packages/backend-core/src/db/utils.ts +++ b/packages/backend-core/src/db/utils.ts @@ -1,6 +1,6 @@ import { newid } from "../hashing" import { DEFAULT_TENANT_ID, Configs } from "../constants" -import * as env from "../environment" +import env from "../environment" import { SEPARATOR, DocumentTypes } from "./constants" import { getTenantId, getGlobalDBName } from "../tenancy" import fetch from "node-fetch" diff --git a/packages/backend-core/src/environment.js b/packages/backend-core/src/environment.ts similarity index 94% rename from packages/backend-core/src/environment.js rename to packages/backend-core/src/environment.ts index fc7f83eea3..6779429d90 100644 --- a/packages/backend-core/src/environment.js +++ b/packages/backend-core/src/environment.ts @@ -6,7 +6,7 @@ function isTest() { ) } -module.exports = { +export = { JWT_SECRET: process.env.JWT_SECRET, COUCH_DB_URL: process.env.COUCH_DB_URL, COUCH_DB_USERNAME: process.env.COUCH_DB_USER, @@ -26,7 +26,7 @@ module.exports = { process.env.ACCOUNT_PORTAL_URL || "https://account.budibase.app", ACCOUNT_PORTAL_API_KEY: process.env.ACCOUNT_PORTAL_API_KEY, DISABLE_ACCOUNT_PORTAL: process.env.DISABLE_ACCOUNT_PORTAL, - SELF_HOSTED: !!parseInt(process.env.SELF_HOSTED), + SELF_HOSTED: !!parseInt(process.env.SELF_HOSTED || ""), COOKIE_DOMAIN: process.env.COOKIE_DOMAIN, PLATFORM_URL: process.env.PLATFORM_URL, POSTHOG_TOKEN: process.env.POSTHOG_TOKEN, @@ -34,7 +34,7 @@ module.exports = { TENANT_FEATURE_FLAGS: process.env.TENANT_FEATURE_FLAGS, USE_COUCH: process.env.USE_COUCH || true, isTest, - _set(key, value) { + _set(key: any, value: any) { process.env[key] = value module.exports[key] = value }, diff --git a/packages/backend-core/src/events/handlers/serve.ts b/packages/backend-core/src/events/handlers/serve.ts index e7563432d7..fd7694008c 100644 --- a/packages/backend-core/src/events/handlers/serve.ts +++ b/packages/backend-core/src/events/handlers/serve.ts @@ -1,10 +1,10 @@ -import { AppServedEvent } from "./../../../../types/src/events/serve" import { processEvent } from "../events" import { App, BuilderServedEvent, Events, AppPreviewServedEvent, + AppServedEvent, } from "@budibase/types" /* eslint-disable */ diff --git a/packages/backend-core/src/events/handlers/view.ts b/packages/backend-core/src/events/handlers/view.ts index 33975e64f8..77b6c573ad 100644 --- a/packages/backend-core/src/events/handlers/view.ts +++ b/packages/backend-core/src/events/handlers/view.ts @@ -11,6 +11,9 @@ import { ViewFilterDeletedEvent, ViewFilterUpdatedEvent, ViewUpdatedEvent, + View, + Table, + TableExportFormat, } from "@budibase/types" /* eslint-disable */ @@ -30,7 +33,7 @@ export function deleted() { processEvent(Events.VIEW_DELETED, properties) } -export function exported(table, format) { +export function exported(table: Table, format: TableExportFormat) { const properties: ViewExportedEvent = {} processEvent(Events.VIEW_EXPORTED, properties) } diff --git a/packages/backend-core/src/index.js b/packages/backend-core/src/index.ts similarity index 87% rename from packages/backend-core/src/index.js rename to packages/backend-core/src/index.ts index 2752312c45..0003af280c 100644 --- a/packages/backend-core/src/index.js +++ b/packages/backend-core/src/index.ts @@ -1,9 +1,9 @@ -const db = require("./db") -const errors = require("./errors") +import db from "./db" +import errors from "./errors" import * as events from "./events" -module.exports = { - init(opts = {}) { +export = { + init(opts: any = {}) { db.init(opts.db) }, // some default exports from the library, however these ideally shouldn't @@ -18,7 +18,7 @@ module.exports = { auth: require("../auth"), constants: require("../constants"), migrations: require("../migrations"), - errors: require("./errors"), + errors, ...errors.errors, env: require("./environment"), accounts: require("./cloud/accounts"), diff --git a/packages/backend-core/src/objectStore/index.js b/packages/backend-core/src/objectStore/index.ts similarity index 73% rename from packages/backend-core/src/objectStore/index.js rename to packages/backend-core/src/objectStore/index.ts index e4addfcf0b..9bb0760f5b 100644 --- a/packages/backend-core/src/objectStore/index.js +++ b/packages/backend-core/src/objectStore/index.ts @@ -1,9 +1,9 @@ -import sanitize from "sanitize-s3-objectkey" +const sanitize = require("sanitize-s3-objectkey") import AWS from "aws-sdk" import stream from "stream" import fetch from "node-fetch" import tar from "tar-fs" -import zlib from "zlib" +const zlib = require("zlib") import { promisify } from "util" import { join } from "path" import fs from "fs" @@ -18,7 +18,7 @@ const STATE = { bucketCreationPromises: {}, } -const CONTENT_TYPE_MAP = { +const CONTENT_TYPE_MAP: any = { html: "text/html", css: "text/css", js: "application/javascript", @@ -32,20 +32,16 @@ const STRING_CONTENT_TYPES = [ ] // does normal sanitization and then swaps dev apps to apps -function sanitizeKey(input) { +export function sanitizeKey(input: any) { return sanitize(sanitizeBucket(input)).replace(/\\/g, "/") } -exports.sanitizeKey = sanitizeKey - // simply handles the dev app to app conversion -function sanitizeBucket(input) { +export function sanitizeBucket(input: any) { return input.replace(new RegExp(APP_DEV_PREFIX, "g"), APP_PREFIX) } -exports.sanitizeBucket = sanitizeBucket - -function publicPolicy(bucketName) { +function publicPolicy(bucketName: any) { return { Version: "2012-10-17", Statement: [ @@ -69,13 +65,13 @@ const PUBLIC_BUCKETS = [ObjectStoreBuckets.APPS, ObjectStoreBuckets.GLOBAL] * @return {Object} an S3 object store object, check S3 Nodejs SDK for usage. * @constructor */ -exports.ObjectStore = bucket => { +export const ObjectStore = (bucket: any) => { AWS.config.update({ accessKeyId: env.MINIO_ACCESS_KEY, secretAccessKey: env.MINIO_SECRET_KEY, region: env.AWS_REGION, }) - const config = { + const config: any = { s3ForcePathStyle: true, signatureVersion: "v4", apiVersion: "2006-03-01", @@ -93,7 +89,7 @@ exports.ObjectStore = bucket => { * Given an object store and a bucket name this will make sure the bucket exists, * if it does not exist then it will create it. */ -exports.makeSureBucketExists = async (client, bucketName) => { +export const makeSureBucketExists = async (client: any, bucketName: any) => { bucketName = sanitizeBucket(bucketName) try { await client @@ -101,8 +97,8 @@ exports.makeSureBucketExists = async (client, bucketName) => { Bucket: bucketName, }) .promise() - } catch (err) { - const promises = STATE.bucketCreationPromises + } catch (err: any) { + const promises: any = STATE.bucketCreationPromises const doesntExist = err.statusCode === 404, noAccess = err.statusCode === 403 if (promises[bucketName]) { @@ -138,20 +134,20 @@ exports.makeSureBucketExists = async (client, bucketName) => { * Uploads the contents of a file given the required parameters, useful when * temp files in use (for example file uploaded as an attachment). */ -exports.upload = async ({ +export const upload = async ({ bucket: bucketName, filename, path, type, metadata, -}) => { +}: any) => { const extension = [...filename.split(".")].pop() const fileBytes = fs.readFileSync(path) - const objectStore = exports.ObjectStore(bucketName) - await exports.makeSureBucketExists(objectStore, bucketName) + const objectStore = ObjectStore(bucketName) + await makeSureBucketExists(objectStore, bucketName) - const config = { + const config: any = { // windows file paths need to be converted to forward slashes for s3 Key: sanitizeKey(filename), Body: fileBytes, @@ -167,9 +163,14 @@ exports.upload = async ({ * Similar to the upload function but can be used to send a file stream * through to the object store. */ -exports.streamUpload = async (bucketName, filename, stream, extra = {}) => { - const objectStore = exports.ObjectStore(bucketName) - await exports.makeSureBucketExists(objectStore, bucketName) +export const streamUpload = async ( + bucketName: any, + filename: any, + stream: any, + extra = {} +) => { + const objectStore = ObjectStore(bucketName) + await makeSureBucketExists(objectStore, bucketName) const params = { Bucket: sanitizeBucket(bucketName), @@ -184,13 +185,13 @@ exports.streamUpload = async (bucketName, filename, stream, extra = {}) => { * retrieves the contents of a file from the object store, if it is a known content type it * will be converted, otherwise it will be returned as a buffer stream. */ -exports.retrieve = async (bucketName, filepath) => { - const objectStore = exports.ObjectStore(bucketName) +export const retrieve = async (bucketName: any, filepath: any) => { + const objectStore = ObjectStore(bucketName) const params = { Bucket: sanitizeBucket(bucketName), Key: sanitizeKey(filepath), } - const response = await objectStore.getObject(params).promise() + const response: any = await objectStore.getObject(params).promise() // currently these are all strings if (STRING_CONTENT_TYPES.includes(response.ContentType)) { return response.Body.toString("utf8") @@ -202,10 +203,10 @@ exports.retrieve = async (bucketName, filepath) => { /** * Same as retrieval function but puts to a temporary file. */ -exports.retrieveToTmp = async (bucketName, filepath) => { +export const retrieveToTmp = async (bucketName: any, filepath: any) => { bucketName = sanitizeBucket(bucketName) filepath = sanitizeKey(filepath) - const data = await exports.retrieve(bucketName, filepath) + const data = await retrieve(bucketName, filepath) const outputPath = join(budibaseTempDir(), v4()) fs.writeFileSync(outputPath, data) return outputPath @@ -214,9 +215,9 @@ exports.retrieveToTmp = async (bucketName, filepath) => { /** * Delete a single file. */ -exports.deleteFile = async (bucketName, filepath) => { - const objectStore = exports.ObjectStore(bucketName) - await exports.makeSureBucketExists(objectStore, bucketName) +export const deleteFile = async (bucketName: any, filepath: any) => { + const objectStore = ObjectStore(bucketName) + await makeSureBucketExists(objectStore, bucketName) const params = { Bucket: bucketName, Key: filepath, @@ -224,13 +225,13 @@ exports.deleteFile = async (bucketName, filepath) => { return objectStore.deleteObject(params) } -exports.deleteFiles = async (bucketName, filepaths) => { - const objectStore = exports.ObjectStore(bucketName) - await exports.makeSureBucketExists(objectStore, bucketName) +export const deleteFiles = async (bucketName: any, filepaths: any) => { + const objectStore = ObjectStore(bucketName) + await makeSureBucketExists(objectStore, bucketName) const params = { Bucket: bucketName, Delete: { - Objects: filepaths.map(path => ({ Key: path })), + Objects: filepaths.map((path: any) => ({ Key: path })), }, } return objectStore.deleteObjects(params).promise() @@ -239,38 +240,45 @@ exports.deleteFiles = async (bucketName, filepaths) => { /** * Delete a path, including everything within. */ -exports.deleteFolder = async (bucketName, folder) => { +export const deleteFolder = async ( + bucketName: any, + folder: any +): Promise => { bucketName = sanitizeBucket(bucketName) folder = sanitizeKey(folder) - const client = exports.ObjectStore(bucketName) + const client = ObjectStore(bucketName) const listParams = { Bucket: bucketName, Prefix: folder, } - let response = await client.listObjects(listParams).promise() + let response: any = await client.listObjects(listParams).promise() if (response.Contents.length === 0) { return } - const deleteParams = { + const deleteParams: any = { Bucket: bucketName, Delete: { Objects: [], }, } - response.Contents.forEach(content => { + response.Contents.forEach((content: any) => { deleteParams.Delete.Objects.push({ Key: content.Key }) }) response = await client.deleteObjects(deleteParams).promise() // can only empty 1000 items at once if (response.Deleted.length === 1000) { - return exports.deleteFolder(bucketName, folder) + return deleteFolder(bucketName, folder) } } -exports.uploadDirectory = async (bucketName, localPath, bucketPath) => { +export const uploadDirectory = async ( + bucketName: any, + localPath: any, + bucketPath: any +) => { bucketName = sanitizeBucket(bucketName) let uploads = [] const files = fs.readdirSync(localPath, { withFileTypes: true }) @@ -278,17 +286,15 @@ exports.uploadDirectory = async (bucketName, localPath, bucketPath) => { const path = sanitizeKey(join(bucketPath, file.name)) const local = join(localPath, file.name) if (file.isDirectory()) { - uploads.push(exports.uploadDirectory(bucketName, local, path)) + uploads.push(uploadDirectory(bucketName, local, path)) } else { - uploads.push( - exports.streamUpload(bucketName, path, fs.createReadStream(local)) - ) + uploads.push(streamUpload(bucketName, path, fs.createReadStream(local))) } } await Promise.all(uploads) } -exports.downloadTarball = async (url, bucketName, path) => { +export const downloadTarball = async (url: any, bucketName: any, path: any) => { bucketName = sanitizeBucket(bucketName) path = sanitizeKey(path) const response = await fetch(url) @@ -299,7 +305,7 @@ exports.downloadTarball = async (url, bucketName, path) => { const tmpPath = join(budibaseTempDir(), path) await streamPipeline(response.body, zlib.Unzip(), tar.extract(tmpPath)) if (!env.isTest() && env.SELF_HOSTED) { - await exports.uploadDirectory(bucketName, tmpPath, path) + await uploadDirectory(bucketName, tmpPath, path) } // return the temporary path incase there is a use for it return tmpPath diff --git a/packages/backend-core/src/tests/utilities/index.js b/packages/backend-core/src/tests/utilities/index.js deleted file mode 100644 index db3d027511..0000000000 --- a/packages/backend-core/src/tests/utilities/index.js +++ /dev/null @@ -1,7 +0,0 @@ -const mocks = require("./mocks") -const structures = require("./structures") - -module.exports = { - mocks, - structures, -} diff --git a/packages/backend-core/src/tests/utilities/index.ts b/packages/backend-core/src/tests/utilities/index.ts new file mode 100644 index 0000000000..1e73be4c17 --- /dev/null +++ b/packages/backend-core/src/tests/utilities/index.ts @@ -0,0 +1,2 @@ +export * as mocks from "./mocks" +export * as structures from "./structures" diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index 4ec2da5835..b752d946c2 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -59,7 +59,7 @@ jest.mock("../../../events", () => { created: jest.fn(), updated: jest.fn(), deleted: jest.fn(), - import: jest.fn(), + imported: jest.fn(), previewed: jest.fn(), }, role: { @@ -70,7 +70,7 @@ jest.mock("../../../events", () => { unassigned: jest.fn(), }, row: { - import: jest.fn(), + imported: jest.fn(), }, screen: { created: jest.fn(), diff --git a/packages/backend-core/src/tests/utilities/structures/index.js b/packages/backend-core/src/tests/utilities/structures/index.js deleted file mode 100644 index 6ac3b1f5f4..0000000000 --- a/packages/backend-core/src/tests/utilities/structures/index.js +++ /dev/null @@ -1,8 +0,0 @@ -require("../mocks") -const koa = require("./koa") - -const structures = { - koa, -} - -module.exports = structures diff --git a/packages/backend-core/src/tests/utilities/structures/index.ts b/packages/backend-core/src/tests/utilities/structures/index.ts new file mode 100644 index 0000000000..bb826276d1 --- /dev/null +++ b/packages/backend-core/src/tests/utilities/structures/index.ts @@ -0,0 +1,3 @@ +import "../mocks" + +export * as koa from "./koa" diff --git a/packages/backend-core/src/tests/utilities/structures/koa.js b/packages/backend-core/src/tests/utilities/structures/koa.js deleted file mode 100644 index c1e9955713..0000000000 --- a/packages/backend-core/src/tests/utilities/structures/koa.js +++ /dev/null @@ -1,5 +0,0 @@ -const { createMockContext } = require("@shopify/jest-koa-mocks") - -exports.newContext = () => { - return createMockContext() -} diff --git a/packages/backend-core/src/tests/utilities/structures/koa.ts b/packages/backend-core/src/tests/utilities/structures/koa.ts new file mode 100644 index 0000000000..6f0f7866e6 --- /dev/null +++ b/packages/backend-core/src/tests/utilities/structures/koa.ts @@ -0,0 +1,5 @@ +import { createMockContext } from "@shopify/jest-koa-mocks" + +export const newContext = () => { + return createMockContext() +} diff --git a/packages/backend-core/testUtils.js b/packages/backend-core/testUtils.js deleted file mode 100644 index b2d84e3d05..0000000000 --- a/packages/backend-core/testUtils.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require("./src/tests/utilities") diff --git a/packages/backend-core/testUtils.ts b/packages/backend-core/testUtils.ts new file mode 100644 index 0000000000..eb05230400 --- /dev/null +++ b/packages/backend-core/testUtils.ts @@ -0,0 +1 @@ +export * from "./src/tests/utilities" diff --git a/packages/backend-core/yarn.lock b/packages/backend-core/yarn.lock index 0971ca25f0..c28a591a0c 100644 --- a/packages/backend-core/yarn.lock +++ b/packages/backend-core/yarn.lock @@ -2,6 +2,14 @@ # yarn lockfile v1 +"@ampproject/remapping@^2.1.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" + integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== + dependencies: + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.0.tgz#0dfc80309beec8411e65e706461c408b0bb9b431" @@ -9,12 +17,24 @@ dependencies: "@babel/highlight" "^7.16.0" +"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== + dependencies: + "@babel/highlight" "^7.16.7" + "@babel/compat-data@^7.16.0": version "7.16.4" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.4.tgz#081d6bbc336ec5c2435c6346b2ae1fb98b5ac68e" integrity sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q== -"@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.7.5": +"@babel/compat-data@^7.17.10": + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.10.tgz#711dc726a492dfc8be8220028b1b92482362baab" + integrity sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw== + +"@babel/core@^7.1.0", "@babel/core@^7.12.3": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.16.0.tgz#c4ff44046f5fe310525cc9eb4ef5147f0c5374d4" integrity sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ== @@ -35,6 +55,27 @@ semver "^6.3.0" source-map "^0.5.0" +"@babel/core@^7.7.2", "@babel/core@^7.8.0": + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.10.tgz#74ef0fbf56b7dfc3f198fc2d927f4f03e12f4b05" + integrity sha512-liKoppandF3ZcBnIYFjfSDHZLKdLHGJRkoWtG8zQyGJBQfIYobpnVGI5+pLBNtS6psFLDzyq8+h5HiVljW9PNA== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.17.10" + "@babel/helper-compilation-targets" "^7.17.10" + "@babel/helper-module-transforms" "^7.17.7" + "@babel/helpers" "^7.17.9" + "@babel/parser" "^7.17.10" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.10" + "@babel/types" "^7.17.10" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.1" + semver "^6.3.0" + "@babel/generator@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.0.tgz#d40f3d1d5075e62d3500bccb67f3daa8a95265b2" @@ -44,6 +85,15 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.17.10", "@babel/generator@^7.7.2": + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.10.tgz#c281fa35b0c349bbe9d02916f4ae08fc85ed7189" + integrity sha512-46MJZZo9y3o4kmhBVc7zW7i8dtR1oIK/sdO5NcfcZRhTGYi+KKJRtHNgsU6c4VUcJmUNV/LQdebD/9Dlv4K+Tg== + dependencies: + "@babel/types" "^7.17.10" + "@jridgewell/gen-mapping" "^0.1.0" + jsesc "^2.5.1" + "@babel/helper-compilation-targets@^7.16.0": version "7.16.3" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.3.tgz#5b480cd13f68363df6ec4dc8ac8e2da11363cbf0" @@ -54,6 +104,23 @@ browserslist "^4.17.5" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.17.10": + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.10.tgz#09c63106d47af93cf31803db6bc49fef354e2ebe" + integrity sha512-gh3RxjWbauw/dFiU/7whjd0qN9K6nPJMqe6+Er7rOavFh0CQUSwhAE3IcTho2rywPJFxej6TUUHDkWcYI6gGqQ== + dependencies: + "@babel/compat-data" "^7.17.10" + "@babel/helper-validator-option" "^7.16.7" + browserslist "^4.20.2" + semver "^6.3.0" + +"@babel/helper-environment-visitor@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" + integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-function-name@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz#b7dd0797d00bbfee4f07e9c4ea5b0e30c8bb1481" @@ -63,6 +130,14 @@ "@babel/template" "^7.16.0" "@babel/types" "^7.16.0" +"@babel/helper-function-name@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz#136fcd54bc1da82fcb47565cf16fd8e444b1ff12" + integrity sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg== + dependencies: + "@babel/template" "^7.16.7" + "@babel/types" "^7.17.0" + "@babel/helper-get-function-arity@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz#0088c7486b29a9cb5d948b1a1de46db66e089cfa" @@ -77,6 +152,13 @@ dependencies: "@babel/types" "^7.16.0" +"@babel/helper-hoist-variables@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" + integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-member-expression-to-functions@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz#29287040efd197c77636ef75188e81da8bccd5a4" @@ -91,6 +173,13 @@ dependencies: "@babel/types" "^7.16.0" +"@babel/helper-module-imports@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" + integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-module-transforms@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz#1c82a8dd4cb34577502ebd2909699b194c3e9bb5" @@ -105,6 +194,20 @@ "@babel/traverse" "^7.16.0" "@babel/types" "^7.16.0" +"@babel/helper-module-transforms@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz#3943c7f777139e7954a5355c815263741a9c1cbd" + integrity sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw== + dependencies: + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-simple-access" "^7.17.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/helper-validator-identifier" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.3" + "@babel/types" "^7.17.0" + "@babel/helper-optimise-call-expression@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz#cecdb145d70c54096b1564f8e9f10cd7d193b338" @@ -117,6 +220,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== +"@babel/helper-plugin-utils@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" + integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== + "@babel/helper-replace-supers@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.0.tgz#73055e8d3cf9bcba8ddb55cad93fedc860f68f17" @@ -134,6 +242,13 @@ dependencies: "@babel/types" "^7.16.0" +"@babel/helper-simple-access@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz#aaa473de92b7987c6dfa7ce9a7d9674724823367" + integrity sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA== + dependencies: + "@babel/types" "^7.17.0" + "@babel/helper-split-export-declaration@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz#29672f43663e936df370aaeb22beddb3baec7438" @@ -141,16 +256,33 @@ dependencies: "@babel/types" "^7.16.0" +"@babel/helper-split-export-declaration@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" + integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== + dependencies: + "@babel/types" "^7.16.7" + "@babel/helper-validator-identifier@^7.15.7": version "7.15.7" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== + "@babel/helper-validator-option@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== +"@babel/helper-validator-option@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" + integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== + "@babel/helpers@^7.16.0": version "7.16.3" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.16.3.tgz#27fc64f40b996e7074dc73128c3e5c3e7f55c43c" @@ -160,6 +292,15 @@ "@babel/traverse" "^7.16.3" "@babel/types" "^7.16.0" +"@babel/helpers@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.9.tgz#b2af120821bfbe44f9907b1826e168e819375a1a" + integrity sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q== + dependencies: + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.9" + "@babel/types" "^7.17.0" + "@babel/highlight@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.0.tgz#6ceb32b2ca4b8f5f361fb7fd821e3fddf4a1725a" @@ -169,11 +310,25 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.16.7": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.9.tgz#61b2ee7f32ea0454612def4fccdae0de232b73e3" + integrity sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.0", "@babel/parser@^7.16.3": version "7.16.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.4.tgz#d5f92f57cf2c74ffe9b37981c0e72fee7311372e" integrity sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng== +"@babel/parser@^7.16.7", "@babel/parser@^7.17.10": + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.10.tgz#873b16db82a8909e0fbd7f115772f4b739f6ce78" + integrity sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ== + "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" @@ -258,6 +413,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" +"@babel/plugin-syntax-typescript@^7.7.2": + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.17.10.tgz#80031e6042cad6a95ed753f672ebd23c30933195" + integrity sha512-xJefea1DWXW09pW4Tm9bjwVlPDyYA2it3fWlmEjpYz6alPvTUjL0EOzNzI/FEOyI3r4/J7uVH5UqKgl1TQ5hqQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/runtime@^7.15.4": version "7.17.9" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72" @@ -274,7 +436,16 @@ "@babel/parser" "^7.16.0" "@babel/types" "^7.16.0" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.16.0", "@babel/traverse@^7.16.3": +"@babel/template@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" + integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/parser" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/traverse@^7.16.0", "@babel/traverse@^7.16.3": version "7.16.3" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.3.tgz#f63e8a938cc1b780f66d9ed3c54f532ca2d14787" integrity sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag== @@ -289,6 +460,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.17.10", "@babel/traverse@^7.17.3", "@babel/traverse@^7.17.9", "@babel/traverse@^7.7.2": + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.10.tgz#1ee1a5ac39f4eac844e6cf855b35520e5eb6f8b5" + integrity sha512-VmbrTHQteIdUUQNTb+zE12SHS/xQVIShmBPhlNP12hD5poF2pbITW1Z4172d03HegaQWhLffdkRJYtAzp0AGcw== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.17.10" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.17.9" + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/parser" "^7.17.10" + "@babel/types" "^7.17.10" + debug "^4.1.0" + globals "^11.1.0" + "@babel/types@^7.0.0", "@babel/types@^7.16.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.0.tgz#db3b313804f96aadd0b776c4823e127ad67289ba" @@ -297,19 +484,19 @@ "@babel/helper-validator-identifier" "^7.15.7" to-fast-properties "^2.0.0" +"@babel/types@^7.16.7", "@babel/types@^7.17.0", "@babel/types@^7.17.10": + version "7.17.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.10.tgz#d35d7b4467e439fcf06d195f8100e0fea7fc82c4" + integrity sha512-9O26jG0mBYfGkUYCYZRnBwbVLd1UZOICEr2Em6InB6jVfsAv1GKgwXHmrSg+WFWDmeKTA6vyTZiN8tCSM5Oo3A== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@cnakazawa/watch@^1.0.3": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" - integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== - dependencies: - exec-sh "^0.3.2" - minimist "^1.2.0" - "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -326,177 +513,206 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2" - integrity sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g== +"@jest/console@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba" + integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg== dependencies: - "@jest/types" "^26.6.2" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^26.6.2" - jest-util "^26.6.2" + jest-message-util "^27.5.1" + jest-util "^27.5.1" slash "^3.0.0" -"@jest/core@^26.6.3": - version "26.6.3" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.6.3.tgz#7639fcb3833d748a4656ada54bde193051e45fad" - integrity sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw== +"@jest/core@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" + integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ== dependencies: - "@jest/console" "^26.6.2" - "@jest/reporters" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/transform" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/console" "^27.5.1" + "@jest/reporters" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" + emittery "^0.8.1" exit "^0.1.2" - graceful-fs "^4.2.4" - jest-changed-files "^26.6.2" - jest-config "^26.6.3" - jest-haste-map "^26.6.2" - jest-message-util "^26.6.2" - jest-regex-util "^26.0.0" - jest-resolve "^26.6.2" - jest-resolve-dependencies "^26.6.3" - jest-runner "^26.6.3" - jest-runtime "^26.6.3" - jest-snapshot "^26.6.2" - jest-util "^26.6.2" - jest-validate "^26.6.2" - jest-watcher "^26.6.2" - micromatch "^4.0.2" - p-each-series "^2.1.0" + graceful-fs "^4.2.9" + jest-changed-files "^27.5.1" + jest-config "^27.5.1" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-resolve-dependencies "^27.5.1" + jest-runner "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + jest-watcher "^27.5.1" + micromatch "^4.0.4" rimraf "^3.0.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.6.2.tgz#ba364cc72e221e79cc8f0a99555bf5d7577cf92c" - integrity sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA== +"@jest/environment@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" + integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA== dependencies: - "@jest/fake-timers" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" - jest-mock "^26.6.2" + jest-mock "^27.5.1" -"@jest/fake-timers@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" - integrity sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA== +"@jest/fake-timers@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" + integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ== dependencies: - "@jest/types" "^26.6.2" - "@sinonjs/fake-timers" "^6.0.1" + "@jest/types" "^27.5.1" + "@sinonjs/fake-timers" "^8.0.1" "@types/node" "*" - jest-message-util "^26.6.2" - jest-mock "^26.6.2" - jest-util "^26.6.2" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-util "^27.5.1" -"@jest/globals@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.6.2.tgz#5b613b78a1aa2655ae908eba638cc96a20df720a" - integrity sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA== +"@jest/globals@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" + integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q== dependencies: - "@jest/environment" "^26.6.2" - "@jest/types" "^26.6.2" - expect "^26.6.2" + "@jest/environment" "^27.5.1" + "@jest/types" "^27.5.1" + expect "^27.5.1" -"@jest/reporters@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.6.2.tgz#1f518b99637a5f18307bd3ecf9275f6882a667f6" - integrity sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw== +"@jest/reporters@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" + integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/transform" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/console" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.2" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^4.0.3" + istanbul-lib-instrument "^5.1.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.0.2" - jest-haste-map "^26.6.2" - jest-resolve "^26.6.2" - jest-util "^26.6.2" - jest-worker "^26.6.2" + istanbul-reports "^3.1.3" + jest-haste-map "^27.5.1" + jest-resolve "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" slash "^3.0.0" source-map "^0.6.0" string-length "^4.0.1" terminal-link "^2.0.0" - v8-to-istanbul "^7.0.0" - optionalDependencies: - node-notifier "^8.0.0" + v8-to-istanbul "^8.1.0" -"@jest/source-map@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" - integrity sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA== +"@jest/source-map@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" + integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg== dependencies: callsites "^3.0.0" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" source-map "^0.6.0" -"@jest/test-result@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" - integrity sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ== +"@jest/test-result@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" + integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag== dependencies: - "@jest/console" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/console" "^27.5.1" + "@jest/types" "^27.5.1" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^26.6.3": - version "26.6.3" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz#98e8a45100863886d074205e8ffdc5a7eb582b17" - integrity sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw== +"@jest/test-sequencer@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" + integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ== dependencies: - "@jest/test-result" "^26.6.2" - graceful-fs "^4.2.4" - jest-haste-map "^26.6.2" - jest-runner "^26.6.3" - jest-runtime "^26.6.3" + "@jest/test-result" "^27.5.1" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-runtime "^27.5.1" -"@jest/transform@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" - integrity sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA== +"@jest/transform@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" + integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== dependencies: "@babel/core" "^7.1.0" - "@jest/types" "^26.6.2" - babel-plugin-istanbul "^6.0.0" + "@jest/types" "^27.5.1" + babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" convert-source-map "^1.4.0" fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.2.4" - jest-haste-map "^26.6.2" - jest-regex-util "^26.0.0" - jest-util "^26.6.2" - micromatch "^4.0.2" - pirates "^4.0.1" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-regex-util "^27.5.1" + jest-util "^27.5.1" + micromatch "^4.0.4" + pirates "^4.0.4" slash "^3.0.0" source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/types@^26.6.2": - version "26.6.2" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" - integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== +"@jest/types@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" + integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== dependencies: "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" - "@types/yargs" "^15.0.0" + "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@jridgewell/gen-mapping@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" + integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/resolve-uri@^3.0.3": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.6.tgz#4ac237f4dabc8dd93330386907b97591801f7352" + integrity sha512-R7xHtBSNm+9SyvpJkdQl+qrM3Hm2fea3Ef197M3mUug+v+yR+Rhfbs7PBtcBUVnIWJ4JcAdjvij+c8hXS9p5aw== + +"@jridgewell/set-array@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.0.tgz#1179863356ac8fbea64a5a4bcde93a4871012c01" + integrity sha512-SfJxIxNVYLTsKwzB3MoOQ1yxf4w/E6MdkvTgrgAt1bfxjSrLUoHMKrDOykwN14q65waezZIdqDneUIPh4/sKxg== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.12" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.12.tgz#7ed98f6fa525ffb7c56a2cbecb5f7bb91abd2baf" + integrity sha512-az/NhpIwP3K33ILr0T2bso+k2E/SLf8Yidd8mHl0n6sCQ4YdyC8qDhZA6kOPDNDBA56ZnIjngVl0U3jREA0BUA== + +"@jridgewell/trace-mapping@^0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@shopify/jest-koa-mocks@^3.1.5": version "3.1.5" resolved "https://registry.yarnpkg.com/@shopify/jest-koa-mocks/-/jest-koa-mocks-3.1.5.tgz#11f77ccfbcaf35cf5ee2c6108a286e61e6bea084" @@ -512,10 +728,10 @@ dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" - integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== +"@sinonjs/fake-timers@^8.0.1": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" + integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== dependencies: "@sinonjs/commons" "^1.7.0" @@ -535,7 +751,14 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": +"@types/accepts@*": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.5.tgz#c34bec115cfc746e04fe5a059df4ce7e7b391575" + integrity sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ== + dependencies: + "@types/node" "*" + +"@types/babel__core@^7.0.0": version "7.1.16" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.16.tgz#bc12c74b7d65e82d29876b5d0baf5c625ac58702" integrity sha512-EAEHtisTMM+KaKwfWdC3oyllIqswlznXCIVCt7/oRNrh+DhgT4UEBNC/jlADNjvw7UnfbcdkGQcPVZ1xYiLcrQ== @@ -546,6 +769,17 @@ "@types/babel__template" "*" "@types/babel__traverse" "*" +"@types/babel__core@^7.1.14": + version "7.1.19" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.19.tgz#7b497495b7d1b4812bdb9d02804d0576f43ee460" + integrity sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + "@types/babel__generator@*": version "7.6.3" resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.3.tgz#f456b4b2ce79137f768aa130d2423d2f0ccfaba5" @@ -568,6 +802,55 @@ dependencies: "@babel/types" "^7.3.0" +"@types/body-parser@*": + version "1.19.2" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" + integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.35" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + dependencies: + "@types/node" "*" + +"@types/content-disposition@*": + version "0.5.4" + resolved "https://registry.yarnpkg.com/@types/content-disposition/-/content-disposition-0.5.4.tgz#de48cf01c79c9f1560bcfd8ae43217ab028657f8" + integrity sha512-0mPF08jn9zYI0n0Q/Pnz7C4kThdSt+6LD4amsrYDDpgBfrVWa3TcCOxKX1zkGgYniGagRv8heN2cbh+CAn+uuQ== + +"@types/cookies@*": + version "0.7.7" + resolved "https://registry.yarnpkg.com/@types/cookies/-/cookies-0.7.7.tgz#7a92453d1d16389c05a5301eef566f34946cfd81" + integrity sha512-h7BcvPUogWbKCzBR2lY4oqaZbO3jXZksexYJVFvkrFeLgbZjQkU4x8pRq6eg2MHXQhY0McQdqmmsxRWlVAHooA== + dependencies: + "@types/connect" "*" + "@types/express" "*" + "@types/keygrip" "*" + "@types/node" "*" + +"@types/express-serve-static-core@^4.17.18": + version "4.17.28" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8" + integrity sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@*": + version "4.17.13" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" + integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.18" + "@types/qs" "*" + "@types/serve-static" "*" + "@types/graceful-fs@^4.1.2": version "4.1.5" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" @@ -575,6 +858,16 @@ dependencies: "@types/node" "*" +"@types/http-assert@*": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.3.tgz#ef8e3d1a8d46c387f04ab0f2e8ab8cb0c5078661" + integrity sha512-FyAOrDuQmBi8/or3ns4rwPno7/9tJTijVW6aQQjK02+kOQ8zmoNg2XJtAuQhvQcy1ASJq38wirX5//9J1EqoUA== + +"@types/http-errors@*": + version "1.8.2" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-1.8.2.tgz#7315b4c4c54f82d13fa61c228ec5c2ea5cc9e0e1" + integrity sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" @@ -602,6 +895,37 @@ jest-matcher-utils "^27.0.0" pretty-format "^27.0.0" +"@types/keygrip@*": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.2.tgz#513abfd256d7ad0bf1ee1873606317b33b1b2a72" + integrity sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw== + +"@types/koa-compose@*": + version "3.2.5" + resolved "https://registry.yarnpkg.com/@types/koa-compose/-/koa-compose-3.2.5.tgz#85eb2e80ac50be95f37ccf8c407c09bbe3468e9d" + integrity sha512-B8nG/OoE1ORZqCkBVsup/AKcvjdgoHnfi4pZMn5UwAPCbhk/96xyv284eBYW8JlQbQ7zDmnpFr68I/40mFoIBQ== + dependencies: + "@types/koa" "*" + +"@types/koa@*", "@types/koa@^2.13.3": + version "2.13.4" + resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.13.4.tgz#10620b3f24a8027ef5cbae88b393d1b31205726b" + integrity sha512-dfHYMfU+z/vKtQB7NUrthdAEiSvnLebvBjwHtfFmpZmB7em2N3WVQdHgnFq+xvyVgxW5jKDmjWfLD3lw4g4uTw== + dependencies: + "@types/accepts" "*" + "@types/content-disposition" "*" + "@types/cookies" "*" + "@types/http-assert" "*" + "@types/http-errors" "*" + "@types/keygrip" "*" + "@types/koa-compose" "*" + "@types/node" "*" + +"@types/mime@^1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== + "@types/node-fetch@^2.6.1": version "2.6.1" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975" @@ -620,30 +944,63 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.9.tgz#bc43c990c3c9be7281868bbc7b8fdd6e2b57adfa" integrity sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A== -"@types/normalize-package-data@^2.4.0": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" - integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== +"@types/prettier@^2.1.5": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.0.tgz#efcbd41937f9ae7434c714ab698604822d890759" + integrity sha512-G/AdOadiZhnJp0jXCaBQU449W2h716OW/EoXeYkCytxKL06X1WCXB4DZpp8TpZ8eyIJVS1cw4lrlkkSYU21cDw== -"@types/prettier@^2.0.0": - version "2.4.2" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.2.tgz#4c62fae93eb479660c3bd93f9d24d561597a8281" - integrity sha512-ekoj4qOQYp7CvjX8ZDBgN86w3MqQhLE1hczEJbEIjgFEumDy+na/4AJAbLXfgEWFNB2pKadM5rPFtuSGMWK7xA== +"@types/qs@*": + version "6.9.7" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + +"@types/range-parser@*": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + +"@types/serve-static@*": + version "1.13.10" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" + integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== + dependencies: + "@types/mime" "^1" + "@types/node" "*" "@types/stack-utils@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== +"@types/tar-fs@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/tar-fs/-/tar-fs-2.0.1.tgz#6391dcad1b03dea2d79fac07371585ab54472bb1" + integrity sha512-qlsQyIY9sN7p221xHuXKNoMfUenOcvEBN4zI8dGsYbYCqHtTarXOEXSIgUnK+GcR0fZDse6pAIc5pIrCh9NefQ== + dependencies: + "@types/node" "*" + "@types/tar-stream" "*" + +"@types/tar-stream@*": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@types/tar-stream/-/tar-stream-2.2.2.tgz#be9d0be9404166e4b114151f93e8442e6ab6fb1d" + integrity sha512-1AX+Yt3icFuU6kxwmPakaiGrJUwG44MpuiqPg4dSolRFk6jmvs4b3IbUol9wKDLIgU76gevn3EwE8y/DkSJCZQ== + dependencies: + "@types/node" "*" + +"@types/uuid@^8.3.4": + version "8.3.4" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" + integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== + "@types/yargs-parser@*": version "20.2.1" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw== -"@types/yargs@^15.0.0": - version "15.0.14" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.14.tgz#26d821ddb89e70492160b66d10a0eb6df8f6fb06" - integrity sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ== +"@types/yargs@^16.0.0": + version "16.0.4" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" + integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== dependencies: "@types/yargs-parser" "*" @@ -750,7 +1107,7 @@ ansi-escapes@^4.2.1: dependencies: type-fest "^0.21.3" -ansi-regex@^5.0.0, ansi-regex@^5.0.1: +ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== @@ -774,13 +1131,10 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" +any-promise@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= anymatch@^3.0.3: version "3.1.2" @@ -802,26 +1156,6 @@ argsarray@0.0.1: resolved "https://registry.yarnpkg.com/argsarray/-/argsarray-0.0.1.tgz#6e7207b4ecdb39b0af88303fa5ae22bda8df61cb" integrity sha1-bnIHtOzbObCviDA/pa4ivajfYcs= -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= - -arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= - -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= - asn1@~0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" @@ -837,11 +1171,6 @@ assert-plus@^0.2.0: resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" integrity sha1-104bh+ev/A24qttwIfP+SBAasjQ= -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= - ast-types@0.9.6: version "0.9.6" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9" @@ -866,11 +1195,6 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= -atob@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - aws-sdk@^2.901.0: version "2.1030.0" resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1030.0.tgz#24a856af3d2b8b37c14a8f59974993661c66fd82" @@ -921,21 +1245,21 @@ axios@0.24.0: dependencies: follow-redirects "^1.14.4" -babel-jest@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" - integrity sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA== +babel-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" + integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== dependencies: - "@jest/transform" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/babel__core" "^7.1.7" - babel-plugin-istanbul "^6.0.0" - babel-preset-jest "^26.6.2" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^27.5.1" chalk "^4.0.0" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" slash "^3.0.0" -babel-plugin-istanbul@^6.0.0: +babel-plugin-istanbul@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== @@ -946,10 +1270,10 @@ babel-plugin-istanbul@^6.0.0: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz#8185bd030348d254c6d7dd974355e6a28b21e62d" - integrity sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw== +babel-plugin-jest-hoist@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e" + integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" @@ -974,12 +1298,12 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-top-level-await" "^7.8.3" -babel-preset-jest@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz#747872b1171df032252426586881d62d31798fee" - integrity sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ== +babel-preset-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" + integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== dependencies: - babel-plugin-jest-hoist "^26.6.2" + babel-plugin-jest-hoist "^27.5.1" babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: @@ -1007,19 +1331,6 @@ base64url@3.x.x, base64url@^3.0.1: resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== -base@^0.11.1: - version "0.11.2" - resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" - bcrypt-pbkdf@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" @@ -1056,23 +1367,7 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== - dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" - -braces@^3.0.1: +braces@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -1095,6 +1390,24 @@ browserslist@^4.17.5: node-releases "^2.0.1" picocolors "^1.0.0" +browserslist@^4.20.2: + version "4.20.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.3.tgz#eb7572f49ec430e054f56d52ff0ebe9be915f8bf" + integrity sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg== + dependencies: + caniuse-lite "^1.0.30001332" + electron-to-chromium "^1.4.118" + escalade "^3.1.1" + node-releases "^2.0.3" + picocolors "^1.0.0" + +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -1134,21 +1447,6 @@ buffer@^5.5.0, buffer@^5.6.0: base64-js "^1.3.1" ieee754 "^1.1.13" -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== - dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" - cache-content-type@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-content-type/-/cache-content-type-1.0.1.tgz#035cde2b08ee2129f4a8315ea8f00a00dba1453c" @@ -1162,27 +1460,25 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camelcase@^5.0.0, camelcase@^5.3.1: +camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.0.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.1.tgz#250fd350cfd555d0d2160b1d51510eaf8326e86e" - integrity sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA== +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001280: version "1.0.30001282" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001282.tgz#38c781ee0a90ccfe1fe7fefd00e43f5ffdcb96fd" integrity sha512-YhF/hG6nqBEllymSIjLtR2iWDDnChvhnVJqp+vloyt2tEHFG1yBR+ac2B/rOw0qOK0m0lEXU2dv4E/sMk5P9Kg== -capture-exit@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" - integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== - dependencies: - rsvp "^4.8.4" +caniuse-lite@^1.0.30001332: + version "1.0.30001335" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001335.tgz#899254a0b70579e5a957c32dced79f0727c61f2a" + integrity sha512-ddP1Tgm7z2iIxu6QTtbZUv6HJxSaV/PZeSrWFZtbY4JZ69tOeNhBCl3HyRQgeNZKE5AOn1kpV7fhljigy0Ty3w== caseless@~0.12.0: version "0.12.0" @@ -1221,34 +1517,24 @@ chownr@^1.1.1: resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== -ci-info@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" - integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== +ci-info@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" + integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw== -cjs-module-lexer@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f" - integrity sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw== +cjs-module-lexer@^1.0.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" + integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== - dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" - -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== dependencies: string-width "^4.2.0" strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" + wrap-ansi "^7.0.0" clone-buffer@1.0.0: version "1.0.0" @@ -1279,14 +1565,6 @@ collect-v8-coverage@^1.0.0: resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -1345,11 +1623,6 @@ commoner@^0.10.1: q "^1.1.2" recast "^0.11.17" -component-emitter@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" - integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== - component-type@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-type/-/component-type-1.2.1.tgz#8a47901700238e4fc32269771230226f24b415a9" @@ -1379,6 +1652,14 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: dependencies: safe-buffer "~5.1.1" +cookies@~0.7.1: + version "0.7.3" + resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.7.3.tgz#7912ce21fbf2e8c2da70cf1c3f351aecf59dadfa" + integrity sha512-+gixgxYSgQLTaTIilDHAdlNPZDENDQernEMiIcZpYYP14zgHsCt4Ce1FEjFtcp6GefhozebB6orvhAAWx/IS0A== + dependencies: + depd "~1.1.2" + keygrip "~1.0.3" + cookies@~0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.8.0.tgz#1293ce4b391740a8406e3c9870e828c4b54f3f90" @@ -1387,28 +1668,12 @@ cookies@~0.8.0: depd "~2.0.0" keygrip "~1.1.0" -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= - core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -cross-spawn@^6.0.0: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - -cross-spawn@^7.0.0: +cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -1469,13 +1734,6 @@ debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: dependencies: ms "2.1.2" -debug@^2.2.0, debug@^2.3.3: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - debug@^4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" @@ -1483,20 +1741,22 @@ debug@^4.3.2: dependencies: ms "2.1.2" -decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +debug@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" decimal.js@^10.2.1: version "10.3.1" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== -decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= deep-equal@~1.0.1: version "1.0.1" @@ -1521,28 +1781,6 @@ deferred-leveldown@~5.3.0: abstract-leveldown "~6.2.1" inherits "^2.0.3" -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" - integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== - dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" - defined@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" @@ -1563,7 +1801,7 @@ denque@^1.1.0: resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.1.tgz#07f670e29c9a78f8faecb2566a1e2c11929c5cbf" integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw== -depd@^1.1.0, depd@~1.1.2: +depd@^1.1.0, depd@^1.1.2, depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= @@ -1591,11 +1829,6 @@ detective@^4.3.1: acorn "^5.2.1" defined "^1.0.0" -diff-sequences@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" - integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== - diff-sequences@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" @@ -1645,6 +1878,11 @@ electron-to-chromium@^1.3.896: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.900.tgz#5be2c5818a2a012c511b4b43e87b6ab7a296d4f5" integrity sha512-SuXbQD8D4EjsaBaJJxySHbC+zq8JrFfxtb4GIr4E9n1BcROyMcRrJCYQNpJ9N+Wjf5mFp7Wp0OHykd14JNEzzQ== +electron-to-chromium@^1.4.118: + version "1.4.131" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.131.tgz#ca42d22eac0fe545860fbc636a6f4a7190ba70a9" + integrity sha512-oi3YPmaP87hiHn0c4ePB67tXaF+ldGhxvZnT19tW9zX6/Ej+pLN0Afja5rQ6S+TND7I9EuwQTT8JYn1k7R7rrw== + emitter-listener@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/emitter-listener/-/emitter-listener-1.1.2.tgz#56b140e8f6992375b3d7cb2cab1cc7432d9632e8" @@ -1652,10 +1890,10 @@ emitter-listener@^1.0.1: dependencies: shimmer "^1.2.0" -emittery@^0.7.1: - version "0.7.2" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" - integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== +emittery@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" + integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== emoji-regex@^8.0.0: version "8.0.0" @@ -1705,6 +1943,11 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +error-inject@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/error-inject/-/error-inject-1.0.0.tgz#e2b3d91b54aed672f309d950d154850fa11d4f37" + integrity sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc= + es3ify@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/es3ify/-/es3ify-0.2.2.tgz#5dae3e650e5be3684b88066513d528d092629862" @@ -1786,37 +2029,19 @@ events@1.1.1: resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= -exec-sh@^0.3.2: - version "0.3.6" - resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" - integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== - -execa@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -execa@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" - integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== - dependencies: - cross-spawn "^7.0.0" - get-stream "^5.0.0" - human-signals "^1.1.1" + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" is-stream "^2.0.0" merge-stream "^2.0.0" - npm-run-path "^4.0.0" - onetime "^5.1.0" - signal-exit "^3.0.2" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" strip-final-newline "^2.0.0" exit@^0.1.2: @@ -1824,45 +2049,15 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" - integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= +expect@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" + integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -expect@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/expect/-/expect-26.6.2.tgz#c6b996bf26bf3fe18b67b2d0f51fc981ba934417" - integrity sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA== - dependencies: - "@jest/types" "^26.6.2" - ansi-styles "^4.0.0" - jest-get-type "^26.3.0" - jest-matcher-utils "^26.6.2" - jest-message-util "^26.6.2" - jest-regex-util "^26.0.0" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" + "@jest/types" "^27.5.1" + jest-get-type "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" extend@~3.0.0: version "3.0.1" @@ -1874,20 +2069,6 @@ extend@~3.0.2: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - extsprintf@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" @@ -1898,7 +2079,7 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -1943,16 +2124,6 @@ fetch-cookie@0.11.0: dependencies: tough-cookie "^2.3.3 || ^3.0.1 || ^4.0.0" -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -1973,11 +2144,6 @@ follow-redirects@^1.14.4: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== -for-in@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= - forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -2010,13 +2176,6 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= - dependencies: - map-cache "^0.2.2" - fresh@^0.5.2, fresh@~0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" @@ -2032,7 +2191,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^2.1.2: +fsevents@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -2052,7 +2211,7 @@ gensync@^1.0.0-beta.2: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -get-caller-file@^2.0.1: +get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -2062,24 +2221,10 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== -get-stream@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - -get-stream@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" - integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== getpass@^0.1.1: version "0.1.7" @@ -2142,15 +2287,15 @@ googleapis@^16.0.0: google-auth-library "~0.10.0" string-template "~1.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.2.4: +graceful-fs@^4.1.2: version "4.2.8" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== -growly@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" - integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= +graceful-fs@^4.2.9: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== gtoken@^1.2.1: version "1.2.2" @@ -2210,37 +2355,6 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" - integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" - integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" - integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -2263,11 +2377,6 @@ hoek@2.x.x: resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" integrity sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0= -hosted-git-info@^2.1.4: - version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" - integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== - html-encoding-sniffer@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" @@ -2334,10 +2443,10 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" -human-signals@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" - integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== iconv-lite@0.4.24, iconv-lite@^0.4.5: version "0.4.24" @@ -2414,93 +2523,23 @@ ioredis@^4.27.1: redis-parser "^3.0.0" standard-as-callback "^2.1.0" -is-accessor-descriptor@^0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" - integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= - dependencies: - kind-of "^3.0.2" - -is-accessor-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" - integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== - dependencies: - kind-of "^6.0.0" - is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= -is-buffer@^1.1.5, is-buffer@~1.1.6: +is-buffer@~1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-ci@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" - integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== - dependencies: - ci-info "^2.0.0" - -is-core-module@^2.2.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.0.tgz#0321336c3d0925e497fd97f5d95cb114a5ccd548" - integrity sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw== +is-core-module@^2.8.1: + version "2.9.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" + integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== dependencies: has "^1.0.3" -is-data-descriptor@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" - integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= - dependencies: - kind-of "^3.0.2" - -is-data-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" - integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== - dependencies: - kind-of "^6.0.0" - -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" - integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== - dependencies: - is-accessor-descriptor "^0.1.6" - is-data-descriptor "^0.1.4" - kind-of "^5.0.0" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" - integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== - dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" - -is-docker@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== - dependencies: - is-plain-object "^2.0.4" - is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -2518,25 +2557,11 @@ is-generator-function@^1.0.7: dependencies: has-tostringtag "^1.0.0" -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= - dependencies: - kind-of "^3.0.2" - is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-plain-object@^2.0.3, is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - is-potential-custom-element-name@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" @@ -2547,11 +2572,6 @@ is-retry-allowed@^2.2.0: resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz#88f34cbd236e043e71b6932d09b0c65fb7b4d71d" integrity sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg== -is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= - is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" @@ -2562,24 +2582,12 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= -is-windows@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" - integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== - -is-wsl@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== - dependencies: - is-docker "^2.0.0" - isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: +isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= @@ -2589,18 +2597,6 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= - dependencies: - isarray "1.0.0" - -isobject@^3.0.0, isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= - isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -2611,16 +2607,6 @@ istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== -istanbul-lib-instrument@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" - integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== - dependencies: - "@babel/core" "^7.7.5" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.0.0" - semver "^6.3.0" - istanbul-lib-instrument@^5.0.4: version "5.1.0" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz#7b49198b657b27a730b8e9cb601f1e1bff24c59a" @@ -2632,6 +2618,17 @@ istanbul-lib-instrument@^5.0.4: istanbul-lib-coverage "^3.2.0" semver "^6.3.0" +istanbul-lib-instrument@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz#31d18bdd127f825dd02ea7bfdfd906f8ab840e9f" + integrity sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^6.3.0" + istanbul-lib-report@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" @@ -2650,75 +2647,95 @@ istanbul-lib-source-maps@^4.0.0: istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" -istanbul-reports@^3.0.2: - version "3.0.5" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.5.tgz#a2580107e71279ea6d661ddede929ffc6d693384" - integrity sha512-5+19PlhnGabNWB7kOFnuxT8H3T/iIyQzIbQMxXsURmmvKg86P2sbkrGOT77VnHw0Qr0gc2XzRaRfMZYYbSQCJQ== +istanbul-reports@^3.1.3: + version "3.1.4" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.4.tgz#1b6f068ecbc6c331040aab5741991273e609e40c" + integrity sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -jest-changed-files@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.6.2.tgz#f6198479e1cc66f22f9ae1e22acaa0b429c042d0" - integrity sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ== +jest-changed-files@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5" + integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw== dependencies: - "@jest/types" "^26.6.2" - execa "^4.0.0" - throat "^5.0.0" + "@jest/types" "^27.5.1" + execa "^5.0.0" + throat "^6.0.1" -jest-cli@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.6.3.tgz#43117cfef24bc4cd691a174a8796a532e135e92a" - integrity sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg== +jest-circus@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" + integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw== dependencies: - "@jest/core" "^26.6.3" - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + expect "^27.5.1" + is-generator-fn "^2.0.0" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + slash "^3.0.0" + stack-utils "^2.0.3" + throat "^6.0.1" + +jest-cli@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" + integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw== + dependencies: + "@jest/core" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" chalk "^4.0.0" exit "^0.1.2" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" import-local "^3.0.2" - is-ci "^2.0.0" - jest-config "^26.6.3" - jest-util "^26.6.2" - jest-validate "^26.6.2" + jest-config "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" prompts "^2.0.1" - yargs "^15.4.1" + yargs "^16.2.0" -jest-config@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.6.3.tgz#64f41444eef9eb03dc51d5c53b75c8c71f645349" - integrity sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg== +jest-config@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" + integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== dependencies: - "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^26.6.3" - "@jest/types" "^26.6.2" - babel-jest "^26.6.3" + "@babel/core" "^7.8.0" + "@jest/test-sequencer" "^27.5.1" + "@jest/types" "^27.5.1" + babel-jest "^27.5.1" chalk "^4.0.0" + ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.1" - graceful-fs "^4.2.4" - jest-environment-jsdom "^26.6.2" - jest-environment-node "^26.6.2" - jest-get-type "^26.3.0" - jest-jasmine2 "^26.6.3" - jest-regex-util "^26.0.0" - jest-resolve "^26.6.2" - jest-util "^26.6.2" - jest-validate "^26.6.2" - micromatch "^4.0.2" - pretty-format "^26.6.2" - -jest-diff@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" - integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== - dependencies: - chalk "^4.0.0" - diff-sequences "^26.6.2" - jest-get-type "^26.3.0" - pretty-format "^26.6.2" + graceful-fs "^4.2.9" + jest-circus "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-get-type "^27.5.1" + jest-jasmine2 "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runner "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^27.5.1" + slash "^3.0.0" + strip-json-comments "^3.1.1" jest-diff@^27.5.1: version "27.5.1" @@ -2730,123 +2747,106 @@ jest-diff@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" -jest-docblock@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" - integrity sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w== +jest-docblock@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" + integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== dependencies: detect-newline "^3.0.0" -jest-each@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.6.2.tgz#02526438a77a67401c8a6382dfe5999952c167cb" - integrity sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A== +jest-each@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" + integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ== dependencies: - "@jest/types" "^26.6.2" + "@jest/types" "^27.5.1" chalk "^4.0.0" - jest-get-type "^26.3.0" - jest-util "^26.6.2" - pretty-format "^26.6.2" + jest-get-type "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" -jest-environment-jsdom@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz#78d09fe9cf019a357009b9b7e1f101d23bd1da3e" - integrity sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q== +jest-environment-jsdom@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" + integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw== dependencies: - "@jest/environment" "^26.6.2" - "@jest/fake-timers" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" - jest-mock "^26.6.2" - jest-util "^26.6.2" - jsdom "^16.4.0" + jest-mock "^27.5.1" + jest-util "^27.5.1" + jsdom "^16.6.0" -jest-environment-node@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.6.2.tgz#824e4c7fb4944646356f11ac75b229b0035f2b0c" - integrity sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag== +jest-environment-node@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e" + integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw== dependencies: - "@jest/environment" "^26.6.2" - "@jest/fake-timers" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" - jest-mock "^26.6.2" - jest-util "^26.6.2" - -jest-get-type@^26.3.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" - integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== + jest-mock "^27.5.1" + jest-util "^27.5.1" jest-get-type@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== -jest-haste-map@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" - integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== +jest-haste-map@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" + integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng== dependencies: - "@jest/types" "^26.6.2" + "@jest/types" "^27.5.1" "@types/graceful-fs" "^4.1.2" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" - graceful-fs "^4.2.4" - jest-regex-util "^26.0.0" - jest-serializer "^26.6.2" - jest-util "^26.6.2" - jest-worker "^26.6.2" - micromatch "^4.0.2" - sane "^4.0.3" + graceful-fs "^4.2.9" + jest-regex-util "^27.5.1" + jest-serializer "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" + micromatch "^4.0.4" walker "^1.0.7" optionalDependencies: - fsevents "^2.1.2" + fsevents "^2.3.2" -jest-jasmine2@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz#adc3cf915deacb5212c93b9f3547cd12958f2edd" - integrity sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg== +jest-jasmine2@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" + integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== dependencies: - "@babel/traverse" "^7.1.0" - "@jest/environment" "^26.6.2" - "@jest/source-map" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/environment" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - expect "^26.6.2" + expect "^27.5.1" is-generator-fn "^2.0.0" - jest-each "^26.6.2" - jest-matcher-utils "^26.6.2" - jest-message-util "^26.6.2" - jest-runtime "^26.6.3" - jest-snapshot "^26.6.2" - jest-util "^26.6.2" - pretty-format "^26.6.2" - throat "^5.0.0" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + throat "^6.0.1" -jest-leak-detector@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz#7717cf118b92238f2eba65054c8a0c9c653a91af" - integrity sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg== +jest-leak-detector@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8" + integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ== dependencies: - jest-get-type "^26.3.0" - pretty-format "^26.6.2" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" -jest-matcher-utils@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz#8e6fd6e863c8b2d31ac6472eeb237bc595e53e7a" - integrity sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw== - dependencies: - chalk "^4.0.0" - jest-diff "^26.6.2" - jest-get-type "^26.3.0" - pretty-format "^26.6.2" - -jest-matcher-utils@^27.0.0: +jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== @@ -2856,27 +2856,27 @@ jest-matcher-utils@^27.0.0: jest-get-type "^27.5.1" pretty-format "^27.5.1" -jest-message-util@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" - integrity sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA== +jest-message-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" + integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== dependencies: - "@babel/code-frame" "^7.0.0" - "@jest/types" "^26.6.2" + "@babel/code-frame" "^7.12.13" + "@jest/types" "^27.5.1" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" - graceful-fs "^4.2.4" - micromatch "^4.0.2" - pretty-format "^26.6.2" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^27.5.1" slash "^3.0.0" - stack-utils "^2.0.2" + stack-utils "^2.0.3" -jest-mock@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" - integrity sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew== +jest-mock@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" + integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== dependencies: - "@jest/types" "^26.6.2" + "@jest/types" "^27.5.1" "@types/node" "*" jest-pnp-resolver@^1.2.2: @@ -2884,177 +2884,181 @@ jest-pnp-resolver@^1.2.2: resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== -jest-regex-util@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" - integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== +jest-regex-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" + integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== -jest-resolve-dependencies@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz#6680859ee5d22ee5dcd961fe4871f59f4c784fb6" - integrity sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg== +jest-resolve-dependencies@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" + integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg== dependencies: - "@jest/types" "^26.6.2" - jest-regex-util "^26.0.0" - jest-snapshot "^26.6.2" + "@jest/types" "^27.5.1" + jest-regex-util "^27.5.1" + jest-snapshot "^27.5.1" -jest-resolve@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.6.2.tgz#a3ab1517217f469b504f1b56603c5bb541fbb507" - integrity sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ== +jest-resolve@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" + integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== dependencies: - "@jest/types" "^26.6.2" + "@jest/types" "^27.5.1" chalk "^4.0.0" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" jest-pnp-resolver "^1.2.2" - jest-util "^26.6.2" - read-pkg-up "^7.0.1" - resolve "^1.18.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + resolve "^1.20.0" + resolve.exports "^1.1.0" slash "^3.0.0" -jest-runner@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.6.3.tgz#2d1fed3d46e10f233fd1dbd3bfaa3fe8924be159" - integrity sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ== +jest-runner@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" + integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ== dependencies: - "@jest/console" "^26.6.2" - "@jest/environment" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/console" "^27.5.1" + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" - emittery "^0.7.1" - exit "^0.1.2" - graceful-fs "^4.2.4" - jest-config "^26.6.3" - jest-docblock "^26.0.0" - jest-haste-map "^26.6.2" - jest-leak-detector "^26.6.2" - jest-message-util "^26.6.2" - jest-resolve "^26.6.2" - jest-runtime "^26.6.3" - jest-util "^26.6.2" - jest-worker "^26.6.2" + emittery "^0.8.1" + graceful-fs "^4.2.9" + jest-docblock "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-haste-map "^27.5.1" + jest-leak-detector "^27.5.1" + jest-message-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runtime "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" source-map-support "^0.5.6" - throat "^5.0.0" + throat "^6.0.1" -jest-runtime@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.6.3.tgz#4f64efbcfac398331b74b4b3c82d27d401b8fa2b" - integrity sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw== +jest-runtime@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" + integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A== dependencies: - "@jest/console" "^26.6.2" - "@jest/environment" "^26.6.2" - "@jest/fake-timers" "^26.6.2" - "@jest/globals" "^26.6.2" - "@jest/source-map" "^26.6.2" - "@jest/test-result" "^26.6.2" - "@jest/transform" "^26.6.2" - "@jest/types" "^26.6.2" - "@types/yargs" "^15.0.0" + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/globals" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" chalk "^4.0.0" - cjs-module-lexer "^0.6.0" + cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" - exit "^0.1.2" + execa "^5.0.0" glob "^7.1.3" - graceful-fs "^4.2.4" - jest-config "^26.6.3" - jest-haste-map "^26.6.2" - jest-message-util "^26.6.2" - jest-mock "^26.6.2" - jest-regex-util "^26.0.0" - jest-resolve "^26.6.2" - jest-snapshot "^26.6.2" - jest-util "^26.6.2" - jest-validate "^26.6.2" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" slash "^3.0.0" strip-bom "^4.0.0" - yargs "^15.4.1" -jest-serializer@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" - integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== +jest-serializer@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" + integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== dependencies: "@types/node" "*" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" -jest-snapshot@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.6.2.tgz#f3b0af1acb223316850bd14e1beea9837fb39c84" - integrity sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og== +jest-snapshot@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1" + integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA== dependencies: + "@babel/core" "^7.7.2" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" "@babel/types" "^7.0.0" - "@jest/types" "^26.6.2" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" "@types/babel__traverse" "^7.0.4" - "@types/prettier" "^2.0.0" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^26.6.2" - graceful-fs "^4.2.4" - jest-diff "^26.6.2" - jest-get-type "^26.3.0" - jest-haste-map "^26.6.2" - jest-matcher-utils "^26.6.2" - jest-message-util "^26.6.2" - jest-resolve "^26.6.2" + expect "^27.5.1" + graceful-fs "^4.2.9" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + jest-haste-map "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-util "^27.5.1" natural-compare "^1.4.0" - pretty-format "^26.6.2" + pretty-format "^27.5.1" semver "^7.3.2" -jest-util@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" - integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== +jest-util@^27.0.0, jest-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" + integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== dependencies: - "@jest/types" "^26.6.2" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" - graceful-fs "^4.2.4" - is-ci "^2.0.0" - micromatch "^4.0.2" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" -jest-validate@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" - integrity sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ== +jest-validate@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" + integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ== dependencies: - "@jest/types" "^26.6.2" - camelcase "^6.0.0" + "@jest/types" "^27.5.1" + camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^26.3.0" + jest-get-type "^27.5.1" leven "^3.1.0" - pretty-format "^26.6.2" + pretty-format "^27.5.1" -jest-watcher@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.6.2.tgz#a5b683b8f9d68dbcb1d7dae32172d2cca0592975" - integrity sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ== +jest-watcher@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2" + integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw== dependencies: - "@jest/test-result" "^26.6.2" - "@jest/types" "^26.6.2" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - jest-util "^26.6.2" + jest-util "^27.5.1" string-length "^4.0.1" -jest-worker@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" - integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== +jest-worker@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== dependencies: "@types/node" "*" merge-stream "^2.0.0" - supports-color "^7.0.0" + supports-color "^8.0.0" -jest@^26.6.3: - version "26.6.3" - resolved "https://registry.yarnpkg.com/jest/-/jest-26.6.3.tgz#40e8fdbe48f00dfa1f0ce8121ca74b88ac9148ef" - integrity sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q== +jest@^27.0.3: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" + integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== dependencies: - "@jest/core" "^26.6.3" + "@jest/core" "^27.5.1" import-local "^3.0.2" - jest-cli "^26.6.3" + jest-cli "^27.5.1" jmespath@0.15.0: version "0.15.0" @@ -3091,7 +3095,7 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -jsdom@^16.4.0: +jsdom@^16.6.0: version "16.7.0" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== @@ -3156,6 +3160,11 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= +json5@2.x, json5@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" + integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + json5@^2.1.2: version "2.2.0" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" @@ -3241,6 +3250,11 @@ jws@^3.2.2: jwa "^1.4.1" safe-buffer "^5.0.1" +keygrip@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.3.tgz#399d709f0aed2bab0a059e0cdd3a5023a053e1dc" + integrity sha512-/PpesirAIfaklxUzp4Yb7xBper9MwP6hNRA6BGGUFCgbJ+BM5CKBtsoxinNXkLHAr+GXS1/lSlF2rP7cv5Fl+g== + keygrip@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226" @@ -3248,40 +3262,31 @@ keygrip@~1.1.0: dependencies: tsscmp "1.0.6" -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" - integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== - -kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +koa-compose@^3.0.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-3.2.1.tgz#a85ccb40b7d986d8e5a345b3a1ace8eabcf54de7" + integrity sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec= + dependencies: + any-promise "^1.1.0" + koa-compose@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877" integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw== +koa-convert@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-1.2.0.tgz#da40875df49de0539098d1700b50820cebcd21d0" + integrity sha1-2kCHXfSd4FOQmNFwC1CCDOvNIdA= + dependencies: + co "^4.6.0" + koa-compose "^3.0.0" + koa-convert@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-2.0.0.tgz#86a0c44d81d40551bae22fee6709904573eea4f5" @@ -3290,6 +3295,11 @@ koa-convert@^2.0.0: co "^4.6.0" koa-compose "^4.1.0" +koa-is-json@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/koa-is-json/-/koa-is-json-1.0.0.tgz#273c07edcdcb8df6a2c1ab7d59ee76491451ec14" + integrity sha1-JzwH7c3Ljfaiwat9We52SRRR7BQ= + koa-passport@^4.1.4: version "4.1.4" resolved "https://registry.yarnpkg.com/koa-passport/-/koa-passport-4.1.4.tgz#5f1665c1c2a37ace79af9f970b770885ca30ccfa" @@ -3297,6 +3307,36 @@ koa-passport@^4.1.4: dependencies: passport "^0.4.0" +koa@2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/koa/-/koa-2.7.0.tgz#7e00843506942b9d82c6cc33749f657c6e5e7adf" + integrity sha512-7ojD05s2Q+hFudF8tDLZ1CpCdVZw8JQELWSkcfG9bdtoTDzMmkRF6BQBU7JzIzCCOY3xd3tftiy/loHBUYaY2Q== + dependencies: + accepts "^1.3.5" + cache-content-type "^1.0.0" + content-disposition "~0.5.2" + content-type "^1.0.4" + cookies "~0.7.1" + debug "~3.1.0" + delegates "^1.0.0" + depd "^1.1.2" + destroy "^1.0.4" + error-inject "^1.0.0" + escape-html "^1.0.3" + fresh "~0.5.2" + http-assert "^1.3.0" + http-errors "^1.6.3" + is-generator-function "^1.0.7" + koa-compose "^4.1.0" + koa-convert "^1.2.0" + koa-is-json "^1.0.0" + on-finished "^2.3.0" + only "~0.0.2" + parseurl "^1.3.2" + statuses "^1.5.0" + type-is "^1.6.16" + vary "^1.1.2" + koa@^2.13.4: version "2.13.4" resolved "https://registry.yarnpkg.com/koa/-/koa-2.13.4.tgz#ee5b0cb39e0b8069c38d115139c774833d32462e" @@ -3492,6 +3532,11 @@ lodash.isstring@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= +lodash.memoize@4.x: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= + lodash.noop@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash.noop/-/lodash.noop-3.0.1.tgz#38188f4d650a3a474258439b96ec45b32617133c" @@ -3536,6 +3581,11 @@ make-dir@^3.0.0: dependencies: semver "^6.0.0" +make-error@1.x: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + makeerror@1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" @@ -3543,18 +3593,6 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" -map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= - -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= - dependencies: - object-visit "^1.0.0" - md5@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" @@ -3596,32 +3634,13 @@ methods@^1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= -micromatch@^3.1.4: - version "3.1.10" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== +micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" - -micromatch@^4.0.2: - version "4.0.4" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" - integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== - dependencies: - braces "^3.0.1" - picomatch "^2.2.3" + braces "^3.0.2" + picomatch "^2.3.1" mime-db@1.51.0: version "1.51.0" @@ -3681,19 +3700,11 @@ mimic-fn@^2.1.0: dependencies: brace-expansion "^1.1.7" -minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: +minimist@^1.2.0, minimist@^1.2.5: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== -mixin-deep@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - mkdirp-classic@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" @@ -3721,23 +3732,6 @@ ms@^2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - define-property "^2.0.2" - extend-shallow "^3.0.2" - fragment-cache "^0.2.1" - is-windows "^1.0.2" - kind-of "^6.0.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - napi-macros@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b" @@ -3763,11 +3757,6 @@ negotiator@0.6.3: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - node-fetch@2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" @@ -3811,58 +3800,22 @@ node-mocks-http@^1.5.8: range-parser "^1.2.0" type-is "^1.6.18" -node-modules-regexp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" - integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= - -node-notifier@^8.0.0: - version "8.0.2" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.2.tgz#f3167a38ef0d2c8a866a83e318c1ba0efeb702c5" - integrity sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg== - dependencies: - growly "^1.3.0" - is-wsl "^2.2.0" - semver "^7.3.2" - shellwords "^0.1.1" - uuid "^8.3.0" - which "^2.0.2" - node-releases@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== -normalize-package-data@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= - dependencies: - remove-trailing-separator "^1.0.1" +node-releases@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.4.tgz#f38252370c43854dc48aa431c766c6c398f40476" + integrity sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ== normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= - dependencies: - path-key "^2.0.0" - -npm-run-path@^4.0.0: +npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== @@ -3894,29 +3847,6 @@ object-assign@^2.0.0: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-2.1.1.tgz#43c36e5d569ff8e4816c4efa8be02d26967c18aa" integrity sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo= -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= - dependencies: - isobject "^3.0.0" - -object.pick@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" - integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= - dependencies: - isobject "^3.0.1" - on-finished@^2.3.0: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" @@ -3931,7 +3861,7 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -onetime@^5.1.0: +onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -3960,16 +3890,6 @@ os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= -p-each-series@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" - integrity sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA== - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= - p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -3994,7 +3914,7 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -parse-json@^5.0.0: +parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -4014,11 +3934,6 @@ parseurl@^1.3.2, parseurl@^1.3.3: resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= - passport-google-auth@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/passport-google-auth/-/passport-google-auth-1.0.2.tgz#8b300b5aa442ef433de1d832ed3112877d0b2938" @@ -4107,17 +4022,12 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-key@^2.0.0, path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= - path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.6: +path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -4147,12 +4057,15 @@ picomatch@^2.0.4, picomatch@^2.2.3: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== -pirates@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" - integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== - dependencies: - node-modules-regexp "^1.0.0" +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pirates@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" + integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== pkg-dir@^4.2.0: version "4.2.0" @@ -4161,11 +4074,6 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -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" - integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= - posthog-node@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/posthog-node/-/posthog-node-1.3.0.tgz#804ed2f213a2f05253f798bf9569d55a9cad94f7" @@ -4403,16 +4311,6 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -pretty-format@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" - integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== - dependencies: - "@jest/types" "^26.6.2" - ansi-regex "^5.0.0" - ansi-styles "^4.0.0" - react-is "^17.0.1" - pretty-format@^27.0.0, pretty-format@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" @@ -4503,25 +4401,6 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -read-pkg-up@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" - integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== - dependencies: - find-up "^4.1.0" - read-pkg "^5.2.0" - type-fest "^0.8.1" - -read-pkg@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" - integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== - dependencies: - "@types/normalize-package-data" "^2.4.0" - normalize-package-data "^2.5.0" - parse-json "^5.0.0" - type-fest "^0.6.0" - readable-stream@1.1.14, readable-stream@^1.0.27-1: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" @@ -4596,34 +4475,11 @@ regenerator-runtime@^0.13.4: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" - -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - remove-trailing-slash@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz#be2285a59f39c74d1bce4f825950061915e3780d" integrity sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA== -repeat-element@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" - integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== - -repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= - request@^2.72.0, request@^2.74.0: version "2.81.0" resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" @@ -4683,11 +4539,6 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -4700,23 +4551,19 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= +resolve.exports@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" + integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== -resolve@^1.10.0, resolve@^1.18.1: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== +resolve@^1.20.0: + version "1.22.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + is-core-module "^2.8.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" rimraf@^3.0.0: version "3.0.2" @@ -4725,11 +4572,6 @@ rimraf@^3.0.0: dependencies: glob "^7.1.3" -rsvp@^4.8.4: - version "4.8.5" - resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" - integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== - safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -4740,33 +4582,11 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= - dependencies: - ret "~0.1.10" - "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sane@^4.0.3: - version "4.1.0" - resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" - integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== - dependencies: - "@cnakazawa/watch" "^1.0.3" - anymatch "^2.0.0" - capture-exit "^2.0.0" - exec-sh "^0.3.2" - execa "^1.0.0" - fb-watchman "^2.0.0" - micromatch "^3.1.4" - minimist "^1.1.1" - walker "~1.0.5" - sanitize-s3-objectkey@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/sanitize-s3-objectkey/-/sanitize-s3-objectkey-0.0.1.tgz#efa9887cd45275b40234fb4bb12fc5754fe64e7e" @@ -4789,7 +4609,14 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: +semver@7.x: + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + dependencies: + lru-cache "^6.0.0" + +semver@^5.4.1, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -4806,33 +4633,11 @@ semver@^7.3.2: dependencies: lru-cache "^6.0.0" -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -4840,31 +4645,26 @@ shebang-command@^2.0.0: dependencies: shebang-regex "^3.0.0" -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - shebang-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shellwords@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" - integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== - shimmer@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== -signal-exit@^3.0.0, signal-exit@^3.0.2: +signal-exit@^3.0.2: version "3.0.5" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f" integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ== +signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -4875,36 +4675,6 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== - dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^3.1.0" - sntp@1.x.x: version "1.0.9" resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" @@ -4912,17 +4682,6 @@ sntp@1.x.x: dependencies: hoek "2.x.x" -source-map-resolve@^0.5.0: - version "0.5.3" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" - integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== - dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" - source-map-support@^0.5.6: version "0.5.20" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9" @@ -4931,11 +4690,6 @@ source-map-support@^0.5.6: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-url@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" - integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== - source-map@^0.4.2: version "0.4.4" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" @@ -4943,7 +4697,7 @@ source-map@^0.4.2: dependencies: amdefine ">=0.0.4" -source-map@^0.5.0, source-map@^0.5.6, source-map@~0.5.0: +source-map@^0.5.0, source-map@~0.5.0: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= @@ -4968,39 +4722,6 @@ spark-md5@3.0.2: resolved "https://registry.yarnpkg.com/spark-md5/-/spark-md5-3.0.2.tgz#7952c4a30784347abcee73268e473b9c0167e3fc" integrity sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw== -spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== - -spdx-expression-parse@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.11" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95" - integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g== - -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - dependencies: - extend-shallow "^3.0.0" - split2@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/split2/-/split2-2.2.0.tgz#186b2575bcf83e85b7d18465756238ee4ee42493" @@ -5039,7 +4760,7 @@ stack-chain@^1.3.7: resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285" integrity sha1-0ZLJ/06moiyUxN1FkXHj8AzqEoU= -stack-utils@^2.0.2: +stack-utils@^2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== @@ -5051,14 +4772,6 @@ standard-as-callback@^2.1.0: resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45" integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A== -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" - integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= - dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - "statuses@>= 1.5.0 < 2", statuses@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" @@ -5127,16 +4840,16 @@ strip-bom@^4.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= - strip-final-newline@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + sublevel-pouchdb@7.2.2: version "7.2.2" resolved "https://registry.yarnpkg.com/sublevel-pouchdb/-/sublevel-pouchdb-7.2.2.tgz#49e46cd37883bf7ff5006d7c5b9bcc7bcc1f422f" @@ -5161,6 +4874,13 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + supports-hyperlinks@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" @@ -5169,6 +4889,11 @@ supports-hyperlinks@^2.0.0: has-flag "^4.0.0" supports-color "^7.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" @@ -5212,10 +4937,10 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" -throat@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" - integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== +throat@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" + integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== through2@3.0.2: version "3.0.2" @@ -5265,21 +4990,6 @@ to-fast-properties@^2.0.0: resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" - integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= - dependencies: - kind-of "^3.0.2" - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -5287,16 +4997,6 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - toidentifier@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" @@ -5338,6 +5038,20 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= +ts-jest@^27.0.3: + version "27.1.4" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.4.tgz#84d42cf0f4e7157a52e7c64b1492c46330943e00" + integrity sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ== + dependencies: + bs-logger "0.x" + fast-json-stable-stringify "2.x" + jest-util "^27.0.0" + json5 "2.x" + lodash.memoize "4.x" + make-error "1.x" + semver "7.x" + yargs-parser "20.x" + tsscmp@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb" @@ -5372,16 +5086,6 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -type-fest@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" - integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== - -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - type-is@^1.6.16, type-is@^1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -5407,29 +5111,11 @@ uid2@0.0.x: resolved "https://registry.yarnpkg.com/uid2/-/uid2-0.0.4.tgz#033f3b1d5d32505f5ce5f888b9f3b667123c0a44" integrity sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA== -union-value@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" - universalify@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" - integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" - uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -5437,11 +5123,6 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= - url@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" @@ -5450,11 +5131,6 @@ url@0.10.3: punycode "1.3.2" querystring "0.2.0" -use@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== - util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -5475,7 +5151,7 @@ uuid@8.1.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.1.0.tgz#6f1536eb43249f473abc6bd58ff983da1ca30d8d" integrity sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg== -uuid@8.3.2, uuid@^8.3.0, uuid@^8.3.2: +uuid@8.3.2, uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== @@ -5490,23 +5166,15 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -v8-to-istanbul@^7.0.0: - version "7.1.2" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz#30898d1a7fa0c84d225a2c1434fb958f290883c1" - integrity sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow== +v8-to-istanbul@^8.1.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" + integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== dependencies: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" source-map "^0.7.3" -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - vary@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -5538,7 +5206,7 @@ w3c-xmlserializer@^2.0.0: dependencies: xml-name-validator "^3.0.0" -walker@^1.0.7, walker@~1.0.5: +walker@^1.0.7: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== @@ -5597,19 +5265,7 @@ whatwg-url@^8.0.0, whatwg-url@^8.5.0: tr46 "^2.1.0" webidl-conversions "^6.1.0" -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - -which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -which@^2.0.1, which@^2.0.2: +which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== @@ -5621,10 +5277,10 @@ word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" @@ -5692,40 +5348,33 @@ xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -y18n@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" - integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yargs-parser@^18.1.2: - version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" +yargs-parser@20.x, yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== -yargs@^15.4.1: - version "15.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" + y18n "^5.0.5" + yargs-parser "^20.2.2" ylru@^1.2.0: version "1.3.2" diff --git a/packages/server/package.json b/packages/server/package.json index 51c3ff75c9..f3b63c2de9 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -143,6 +143,7 @@ "@babel/core": "^7.14.3", "@babel/preset-env": "^7.14.4", "@budibase/standard-components": "^0.9.139", + "@budibase/types": "^1.0.126-alpha.0", "@jest/test-sequencer": "^24.8.0", "@types/apidoc": "^0.50.0", "@types/bull": "^3.15.1", diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index 0845b7da28..b7da735ed6 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -52,6 +52,7 @@ const { import { getUniqueRows } from "../../utilities/usageQuota/rows" import { quotas } from "@budibase/pro" import { errors, events } from "@budibase/backend-core" +import { App } from "@budibase/types" const URL_REGEX_SLASH = /\/|\\/g @@ -233,7 +234,7 @@ const performAppCreate = async (ctx: any) => { const apps = await getAllApps({ dev: true }) const name = ctx.request.body.name checkAppName(ctx, apps, name) - const url = exports.getAppUrl(ctx) + const url = getAppUrl(ctx) checkAppUrl(ctx, apps, url) const { useTemplate, templateKey, templateString } = ctx.request.body @@ -291,7 +292,7 @@ const performAppCreate = async (ctx: any) => { return newApplication } -const creationEvents = (request: any) => { +const creationEvents = (request: any, app: App) => { let creationFns = [] const body = request.body @@ -312,15 +313,15 @@ const creationEvents = (request: any) => { creationFns.push(events.app.created) for (let fn of creationFns) { - fn() + fn(app) } } -const appPostCreate = async (ctx: any, appId: string) => { - creationEvents(ctx.request) +const appPostCreate = async (ctx: any, app: App) => { + creationEvents(ctx.request, app) // app import & template creation if (ctx.request.body.useTemplate === "true") { - const rows = await getUniqueRows([appId]) + const rows = await getUniqueRows([app.appId]) const rowCount = rows ? rows.length : 0 if (rowCount) { try { @@ -330,7 +331,7 @@ const appPostCreate = async (ctx: any, appId: string) => { // this import resulted in row usage exceeding the quota // delete the app // skip pre and post steps as no rows have been added to quotas yet - ctx.params.appId = appId + ctx.params.appId = app.appId await destroyApp(ctx) } throw err @@ -341,7 +342,7 @@ const appPostCreate = async (ctx: any, appId: string) => { export const create = async (ctx: any) => { const newApplication = await quotas.addApp(() => performAppCreate(ctx)) - await appPostCreate(ctx, newApplication.appId) + await appPostCreate(ctx, newApplication) ctx.body = newApplication ctx.status = 200 } @@ -355,16 +356,16 @@ export const update = async (ctx: any) => { if (name) { checkAppName(ctx, apps, name, ctx.params.appId) } - const url = await exports.getAppUrl(ctx) + const url = getAppUrl(ctx) if (url) { checkAppUrl(ctx, apps, url, ctx.params.appId) ctx.request.body.url = url } - const data = await updateAppPackage(ctx.request.body, ctx.params.appId) - events.app.updated() + const app = await updateAppPackage(ctx.request.body, ctx.params.appId) + events.app.updated(app) ctx.status = 200 - ctx.body = data + ctx.body = app } export const updateClient = async (ctx: any) => { @@ -384,10 +385,10 @@ export const updateClient = async (ctx: any) => { version: packageJson.version, revertableVersion: currentVersion, } - const data = await updateAppPackage(appPackageUpdates, ctx.params.appId) - events.app.versionUpdated() + const app = await updateAppPackage(appPackageUpdates, ctx.params.appId) + events.app.versionUpdated(app) ctx.status = 200 - ctx.body = data + ctx.body = app } export const revertClient = async (ctx: any) => { @@ -408,10 +409,10 @@ export const revertClient = async (ctx: any) => { version: application.revertableVersion, revertableVersion: null, } - const data = await updateAppPackage(appPackageUpdates, ctx.params.appId) - events.app.versionReverted() + const app = await updateAppPackage(appPackageUpdates, ctx.params.appId) + events.app.versionReverted(app) ctx.status = 200 - ctx.body = data + ctx.body = app } const destroyApp = async (ctx: any) => { @@ -423,14 +424,15 @@ const destroyApp = async (ctx: any) => { } const db = isUnpublish ? getProdAppDB() : getAppDB() + const app = await db.get(DocumentTypes.APP_METADATA) const result = await db.destroy() if (isUnpublish) { await quotas.removePublishedApp() - events.app.unpublished() + events.app.unpublished(app) } else { await quotas.removeApp() - events.app.deleted() + events.app.deleted(app) } /* istanbul ignore next */ @@ -531,10 +533,10 @@ const updateAppPackage = async (appPackage: any, appId: any) => { // Redis, shouldn't ever store it delete newAppPackage.lockedBy - const response = await db.put(newAppPackage) + await db.put(newAppPackage) // remove any cached metadata, so that it will be updated await appCache.invalidateAppMetadata(appId) - return response + return newAppPackage } const createEmptyAppPackage = async (ctx: any, app: any) => { diff --git a/packages/server/src/api/controllers/deploy/index.ts b/packages/server/src/api/controllers/deploy/index.ts index cfe127fc5f..3520fc9f02 100644 --- a/packages/server/src/api/controllers/deploy/index.ts +++ b/packages/server/src/api/controllers/deploy/index.ts @@ -123,6 +123,7 @@ async function deployApp(deployment: any) { console.log("Deployed app initialised, setting deployment to successful") deployment.setStatus(DeploymentStatus.SUCCESS) await storeDeploymentHistory(deployment) + return appDoc } catch (err: any) { deployment.setStatus(DeploymentStatus.FAILURE, err.message) await storeDeploymentHistory(deployment) @@ -187,13 +188,14 @@ const _deployApp = async function (ctx: any) { console.log("Deploying app...") + let app if (await isFirstDeploy()) { - await quotas.addPublishedApp(() => deployApp(deployment)) + app = await quotas.addPublishedApp(() => deployApp(deployment)) } else { - await deployApp(deployment) + app = await deployApp(deployment) } - events.app.published() + events.app.published(app) ctx.body = deployment } diff --git a/packages/server/src/api/controllers/query/import/index.ts b/packages/server/src/api/controllers/query/import/index.ts index 745b9ea068..b8f11a65aa 100644 --- a/packages/server/src/api/controllers/query/import/index.ts +++ b/packages/server/src/api/controllers/query/import/index.ts @@ -84,9 +84,9 @@ export class RestImporter { const count = successQueries.length const importSource = this.source.getImportSource() const datasource = await db.get(datasourceId) - events.query.import(datasource, importSource, count) + events.query.imported(datasource, importSource, count) for (let query of successQueries) { - events.query.created(query) + events.query.created(datasource, query) } return { diff --git a/packages/server/src/api/controllers/query/import/tests/index.spec.js b/packages/server/src/api/controllers/query/import/tests/index.spec.js index ae7f6e3264..0e9c5f7543 100644 --- a/packages/server/src/api/controllers/query/import/tests/index.spec.js +++ b/packages/server/src/api/controllers/query/import/tests/index.spec.js @@ -106,8 +106,8 @@ describe("Rest Importer", () => { const importResult = await restImporter.importQueries(datasource._id) expect(importResult.errorQueries.length).toBe(0) expect(importResult.queries.length).toBe(assertions[key].count) - expect(events.query.import).toBeCalledTimes(1) - expect(events.query.import).toBeCalledWith(datasource, assertions[key].source, assertions[key].count) + expect(events.query.imported).toBeCalledTimes(1) + expect(events.query.imported).toBeCalledWith(datasource, assertions[key].source, assertions[key].count) jest.clearAllMocks() } diff --git a/packages/server/src/api/controllers/table/utils.ts b/packages/server/src/api/controllers/table/utils.ts index d72c8788fc..f598b757d2 100644 --- a/packages/server/src/api/controllers/table/utils.ts +++ b/packages/server/src/api/controllers/table/utils.ts @@ -149,7 +149,7 @@ export async function handleDataImport(user: any, table: any, dataImport: any) { } await quotas.addRows(finalData.length, () => db.bulkDocs(finalData)) - events.row.import(table, "csv", finalData.length) + events.row.imported(table, "csv", finalData.length) return table } diff --git a/packages/server/src/api/routes/tests/application.spec.js b/packages/server/src/api/routes/tests/application.spec.js index a0fca7a6c8..b80e874d39 100644 --- a/packages/server/src/api/routes/tests/application.spec.js +++ b/packages/server/src/api/routes/tests/application.spec.js @@ -134,7 +134,7 @@ describe("/applications", () => { .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) - expect(res.body.rev).toBeDefined() + expect(res.body._rev).toBeDefined() expect(events.app.updated).toBeCalledTimes(1) }) }) @@ -201,7 +201,7 @@ describe("/applications", () => { .set(headers) .expect("Content-Type", /json/) .expect(200) - expect(res.body.rev).toBeDefined() + expect(res.body._rev).toBeDefined() // retrieve the app to check it const getRes = await request .get(`/api/applications/${config.getAppId()}/appPackage`) diff --git a/packages/server/src/api/routes/tests/table.spec.js b/packages/server/src/api/routes/tests/table.spec.js index c3aafa6c1f..662bce141d 100644 --- a/packages/server/src/api/routes/tests/table.spec.js +++ b/packages/server/src/api/routes/tests/table.spec.js @@ -55,8 +55,8 @@ describe("/tables", () => { expect(events.table.created).toBeCalledWith(res.body) expect(events.table.imported).toBeCalledTimes(1) expect(events.table.imported).toBeCalledWith(res.body, "csv") - expect(events.row.import).toBeCalledTimes(1) - expect(events.row.import).toBeCalledWith(res.body, "csv", 1) + expect(events.row.imported).toBeCalledTimes(1) + expect(events.row.imported).toBeCalledWith(res.body, "csv", 1) }) it("should apply authorization to endpoint", async () => { @@ -163,8 +163,8 @@ describe("/tables", () => { .expect(200) expect(events.table.created).not.toHaveBeenCalled() - expect(events.row.import).toBeCalledTimes(1) - expect(events.row.import).toBeCalledWith(table, "csv", 1) + expect(events.row.imported).toBeCalledTimes(1) + expect(events.row.imported).toBeCalledWith(table, "csv", 1) }) }) diff --git a/packages/server/tsconfig.json b/packages/server/tsconfig.json index f556c271fe..d0fed6e824 100644 --- a/packages/server/tsconfig.json +++ b/packages/server/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "target": "es6", "module": "commonjs", - "lib": ["es2019"], + "lib": ["es2020"], "allowJs": true, "outDir": "dist", "strict": true, diff --git a/packages/types/package.json b/packages/types/package.json index 248f18708a..2b73c142c1 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -7,7 +7,7 @@ "author": "Budibase", "license": "GPL-3.0", "scripts": { - "build": "rimraf dist/ && tsc -p tsconfig.json" + "build": "rimraf dist/ && tsc" }, "jest": { }, diff --git a/packages/types/src/documents/app/app.ts b/packages/types/src/documents/app/app.ts index a27344a799..d1cb9a1ab3 100644 --- a/packages/types/src/documents/app/app.ts +++ b/packages/types/src/documents/app/app.ts @@ -1 +1,3 @@ -export interface App {} +export interface App { + appId: string +} diff --git a/packages/worker/tsconfig.json b/packages/worker/tsconfig.json index 6a5ba315a1..a66169ce54 100644 --- a/packages/worker/tsconfig.json +++ b/packages/worker/tsconfig.json @@ -2,14 +2,15 @@ "compilerOptions": { "target": "es6", "module": "commonjs", - "lib": ["es2019"], + "lib": ["es2020"], "allowJs": true, "outDir": "dist", "strict": true, "noImplicitAny": true, "esModuleInterop": true, "resolveJsonModule": true, - "incremental": true + "incremental": true, + "types": [ "node", "jest"], }, "include": [ "./src/**/*" diff --git a/packages/worker/yarn.lock b/packages/worker/yarn.lock index 99bc8263f7..3805a83b66 100644 --- a/packages/worker/yarn.lock +++ b/packages/worker/yarn.lock @@ -1297,6 +1297,13 @@ async-hook-jl@^1.7.6: dependencies: stack-chain "^1.3.7" +async@~2.1.4: + version "2.1.5" + resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc" + integrity sha1-5YfGhYCZSsZ/xW/4bTrFa9voELw= + dependencies: + lodash "^4.14.0" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -2879,6 +2886,23 @@ globby@^11.0.4: merge2 "^1.4.1" slash "^3.0.0" +google-auth-library@~0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-0.10.0.tgz#6e15babee85fd1dd14d8d128a295b6838d52136e" + integrity sha1-bhW6vuhf0d0U2NEoopW2g41SE24= + dependencies: + gtoken "^1.2.1" + jws "^3.1.4" + lodash.noop "^3.0.1" + request "^2.74.0" + +google-p12-pem@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-0.1.2.tgz#33c46ab021aa734fa0332b3960a9a3ffcb2f3177" + integrity sha1-M8RqsCGqc0+gMys5YKmj/8svMXc= + dependencies: + node-forge "^0.7.1" + googleapis@^16.0.0: version "16.1.0" resolved "https://registry.yarnpkg.com/googleapis/-/googleapis-16.1.0.tgz#0f19f2d70572d918881a0f626e3b1a2fa8629576" @@ -2927,6 +2951,16 @@ graceful-fs@^4.1.2, graceful-fs@^4.2.4: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== +gtoken@^1.2.1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-1.2.3.tgz#5509571b8afd4322e124cf66cf68115284c476d8" + integrity sha512-wQAJflfoqSgMWrSBk9Fg86q+sd6s7y6uJhIvvIPz++RElGlMtEqsdAR2oWwZ/WTEtp7P9xFbJRrT976oRgzJ/w== + dependencies: + google-p12-pem "^0.1.0" + jws "^3.0.0" + mime "^1.4.1" + request "^2.72.0" + har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -3986,7 +4020,7 @@ jwa@^1.4.1: ecdsa-sig-formatter "1.0.11" safe-buffer "^5.0.1" -jws@^3.2.2: +jws@^3.0.0, jws@^3.1.4, jws@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== @@ -4306,6 +4340,11 @@ lodash.memoize@4.x: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= +lodash.noop@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash.noop/-/lodash.noop-3.0.1.tgz#38188f4d650a3a474258439b96ec45b32617133c" + integrity sha1-OBiPTWUKOkdCWEObluxFsyYXEzw= + lodash.once@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" @@ -4316,7 +4355,7 @@ lodash.pick@^4.0.0: resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= -lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0: +lodash@^4.14.0, lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -4435,6 +4474,11 @@ mime-types@^2.1.12, mime-types@^2.1.18, mime-types@~2.1.19, mime-types@~2.1.24: dependencies: mime-db "1.51.0" +mime@^1.4.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + mime@^2.5.0: version "2.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" @@ -4541,6 +4585,11 @@ node-fetch@2.6.7, node-fetch@^2.6.1: dependencies: whatwg-url "^5.0.0" +node-forge@^0.7.1: + version "0.7.6" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac" + integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw== + node-gyp-build@~4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.1.1.tgz#d7270b5d86717068d114cc57fff352f96d745feb" @@ -5502,7 +5551,7 @@ remove-trailing-slash@^0.1.1: resolved "https://registry.yarnpkg.com/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz#be2285a59f39c74d1bce4f825950061915e3780d" integrity sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA== -request@^2.88.0: +request@^2.72.0, request@^2.74.0, request@^2.88.0: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -5931,6 +5980,11 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +string-template@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/string-template/-/string-template-1.0.0.tgz#9e9f2233dc00f218718ec379a28a5673ecca8b96" + integrity sha1-np8iM9wA8hhxjsN5oopWc+zKi5Y= + string-width@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" From 11ea9d90e31aa10aee22eed9f967aff59c8f309b Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 4 May 2022 11:22:50 +0100 Subject: [PATCH 33/75] Add ts watch support for backend-core + stub out migrations --- packages/backend-core/package.json | 1 + packages/backend-core/src/index.ts | 3 ++- packages/backend-core/tsconfig.build.json | 1 + packages/backend-core/tsconfig.json | 1 + packages/server/nodemon.json | 2 +- .../functions/backfill/app/account.ts | 4 +++ .../migrations/functions/backfill/app/app.ts | 10 +++++++ .../functions/backfill/app/automation.ts | 2 ++ .../functions/backfill/app/datasource.ts | 1 + .../functions/backfill/app/layout.ts | 1 + .../functions/backfill/app/query.ts | 3 +++ .../migrations/functions/backfill/app/role.ts | 2 ++ .../migrations/functions/backfill/app/row.ts | 1 + .../functions/backfill/app/screen.ts | 1 + .../functions/backfill/app/table.ts | 3 +++ .../migrations/functions/backfill/app/view.ts | 3 +++ .../functions/backfill/event_app_backfill.ts | 11 ++++++++ .../functions/backfill/event_backfill.ts | 11 ++++++++ .../functions/backfill/global/config.ts | 7 +++++ .../functions/backfill/global/user.ts | 3 +++ packages/server/src/migrations/index.ts | 27 ++++++++++++------- packages/worker/nodemon.json | 2 +- 22 files changed, 87 insertions(+), 13 deletions(-) create mode 100644 packages/server/src/migrations/functions/backfill/app/account.ts create mode 100644 packages/server/src/migrations/functions/backfill/app/app.ts create mode 100644 packages/server/src/migrations/functions/backfill/app/automation.ts create mode 100644 packages/server/src/migrations/functions/backfill/app/datasource.ts create mode 100644 packages/server/src/migrations/functions/backfill/app/layout.ts create mode 100644 packages/server/src/migrations/functions/backfill/app/query.ts create mode 100644 packages/server/src/migrations/functions/backfill/app/role.ts create mode 100644 packages/server/src/migrations/functions/backfill/app/row.ts create mode 100644 packages/server/src/migrations/functions/backfill/app/screen.ts create mode 100644 packages/server/src/migrations/functions/backfill/app/table.ts create mode 100644 packages/server/src/migrations/functions/backfill/app/view.ts create mode 100644 packages/server/src/migrations/functions/backfill/event_app_backfill.ts create mode 100644 packages/server/src/migrations/functions/backfill/event_backfill.ts create mode 100644 packages/server/src/migrations/functions/backfill/global/config.ts create mode 100644 packages/server/src/migrations/functions/backfill/global/user.ts diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 5d95315006..5ff88b6664 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -8,6 +8,7 @@ "license": "GPL-3.0", "scripts": { "build": "rimraf dist/ && tsc -p tsconfig.build.json", + "dev:builder": "rimraf dist/ && tsc -p tsconfig.build.json --watch", "test": "jest", "test:watch": "jest --watchAll" }, diff --git a/packages/backend-core/src/index.ts b/packages/backend-core/src/index.ts index 0003af280c..dab77d130a 100644 --- a/packages/backend-core/src/index.ts +++ b/packages/backend-core/src/index.ts @@ -1,6 +1,7 @@ import db from "./db" import errors from "./errors" import * as events from "./events" +import * as migrations from "./migrations" export = { init(opts: any = {}) { @@ -17,7 +18,7 @@ export = { cache: require("../cache"), auth: require("../auth"), constants: require("../constants"), - migrations: require("../migrations"), + migrations, errors, ...errors.errors, env: require("./environment"), diff --git a/packages/backend-core/tsconfig.build.json b/packages/backend-core/tsconfig.build.json index 9c67698e91..57783aab7a 100644 --- a/packages/backend-core/tsconfig.build.json +++ b/packages/backend-core/tsconfig.build.json @@ -3,6 +3,7 @@ "extends": "./tsconfig.json", "exclude": [ "node_modules", + "dist/**/*", "**/*.json", "**/*.spec.js", "**/*.spec.ts" diff --git a/packages/backend-core/tsconfig.json b/packages/backend-core/tsconfig.json index 5f9000c18f..373b8440f7 100644 --- a/packages/backend-core/tsconfig.json +++ b/packages/backend-core/tsconfig.json @@ -29,6 +29,7 @@ ], "exclude": [ "node_modules", + "dist/**/*", "**/*.spec.js", // "**/*.spec.ts" // don't exclude spec.ts files for editor support ] diff --git a/packages/server/nodemon.json b/packages/server/nodemon.json index a979dfb1cb..c27f3aeaf3 100644 --- a/packages/server/nodemon.json +++ b/packages/server/nodemon.json @@ -1,6 +1,6 @@ { "watch": ["src", "../backend-core", "../../../budibase-pro/packages/pro"], "ext": "js,ts,json", - "ignore": ["src/**/*.spec.ts", "src/**/*.spec.js"], + "ignore": ["src/**/*.spec.ts", "src/**/*.spec.js", "../backend-core/dist/**/*"], "exec": "ts-node src/index.ts" } \ No newline at end of file diff --git a/packages/server/src/migrations/functions/backfill/app/account.ts b/packages/server/src/migrations/functions/backfill/app/account.ts new file mode 100644 index 0000000000..2fe7003437 --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/app/account.ts @@ -0,0 +1,4 @@ +// TODO: Add migrations to account portal + +// ACCOUNT_CREATED = "account:created", +// ACCOUNT_VERIFIED = "account:verified", diff --git a/packages/server/src/migrations/functions/backfill/app/app.ts b/packages/server/src/migrations/functions/backfill/app/app.ts new file mode 100644 index 0000000000..6ebfc48297 --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/app/app.ts @@ -0,0 +1,10 @@ +export const backfillAppCreated = () => {} + +export const backfillAppPublished = () => {} + +// APP_CREATED = "app:created", +// APP_PUBLISHED = "app:published", + +// Maybe +// APP_TEMPLATE_IMPORTED = "app:template:imported", +// APP_FILE_IMPORTED = "app:file:imported", diff --git a/packages/server/src/migrations/functions/backfill/app/automation.ts b/packages/server/src/migrations/functions/backfill/app/automation.ts new file mode 100644 index 0000000000..a1f908b8b5 --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/app/automation.ts @@ -0,0 +1,2 @@ +// AUTOMATION_CREATED = "automation:created", +// AUTOMATION_STEP_CREATED = "automation:step:created", diff --git a/packages/server/src/migrations/functions/backfill/app/datasource.ts b/packages/server/src/migrations/functions/backfill/app/datasource.ts new file mode 100644 index 0000000000..1830d54924 --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/app/datasource.ts @@ -0,0 +1 @@ +// DATASOURCE_CREATED = "datasource:created", diff --git a/packages/server/src/migrations/functions/backfill/app/layout.ts b/packages/server/src/migrations/functions/backfill/app/layout.ts new file mode 100644 index 0000000000..e69d79238b --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/app/layout.ts @@ -0,0 +1 @@ +// LAYOUT_CREATED = "layout:created", diff --git a/packages/server/src/migrations/functions/backfill/app/query.ts b/packages/server/src/migrations/functions/backfill/app/query.ts new file mode 100644 index 0000000000..79b56466e3 --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/app/query.ts @@ -0,0 +1,3 @@ +// QUERY_CREATED = "query:created", +// +// QUERY_IMPORT = "query:import", diff --git a/packages/server/src/migrations/functions/backfill/app/role.ts b/packages/server/src/migrations/functions/backfill/app/role.ts new file mode 100644 index 0000000000..34cbd19569 --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/app/role.ts @@ -0,0 +1,2 @@ +// ROLE_CREATED = "role:created", +// ROLE_ASSIGNED = "role:assigned", diff --git a/packages/server/src/migrations/functions/backfill/app/row.ts b/packages/server/src/migrations/functions/backfill/app/row.ts new file mode 100644 index 0000000000..52b0664231 --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/app/row.ts @@ -0,0 +1 @@ +// ROW_IMPORT = "row:import", diff --git a/packages/server/src/migrations/functions/backfill/app/screen.ts b/packages/server/src/migrations/functions/backfill/app/screen.ts new file mode 100644 index 0000000000..d000321625 --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/app/screen.ts @@ -0,0 +1 @@ +// SCREEN_CREATED = "screen:created", diff --git a/packages/server/src/migrations/functions/backfill/app/table.ts b/packages/server/src/migrations/functions/backfill/app/table.ts new file mode 100644 index 0000000000..116e7bbbdf --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/app/table.ts @@ -0,0 +1,3 @@ +// TABLE_CREATED = "table:created", +// +// TABLE_DATA_IMPORTED = "table:data:imported", diff --git a/packages/server/src/migrations/functions/backfill/app/view.ts b/packages/server/src/migrations/functions/backfill/app/view.ts new file mode 100644 index 0000000000..f9f4544f0d --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/app/view.ts @@ -0,0 +1,3 @@ +// VIEW_CREATED = "view:created", +// VIEW_FILTER_CREATED = "view:filter:created", +// VIEW_CALCULATION_CREATED = "view:calculation:created", diff --git a/packages/server/src/migrations/functions/backfill/event_app_backfill.ts b/packages/server/src/migrations/functions/backfill/event_app_backfill.ts new file mode 100644 index 0000000000..5b46959b5b --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/event_app_backfill.ts @@ -0,0 +1,11 @@ +import * as app from "./app/app" + +/** + * Date: + * May 2022 + * + * Description: + * Backfill app events. + */ + +export const run = async (db: any) => {} diff --git a/packages/server/src/migrations/functions/backfill/event_backfill.ts b/packages/server/src/migrations/functions/backfill/event_backfill.ts new file mode 100644 index 0000000000..d010c1c149 --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/event_backfill.ts @@ -0,0 +1,11 @@ +import * as syncPublishedApps from "../usageQuotas/syncPublishedApps" + +/** + * Date: + * May 2022 + * + * Description: + * Backfill global events. + */ + +export const run = async (db: any) => {} diff --git a/packages/server/src/migrations/functions/backfill/global/config.ts b/packages/server/src/migrations/functions/backfill/global/config.ts new file mode 100644 index 0000000000..2c058952ca --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/global/config.ts @@ -0,0 +1,7 @@ +// EMAIL_SMTP_CREATED = "email:smtp:created", +// AUTH_SSO_CREATED = "auth:sso:created", +// AUTH_SSO_ACTIVATED = "auth:sso:activated", +// AUTH_SSO_DEACTIVATED = "auth:sso:deactivated", +// ORG_NAME_UPDATED = "org:info:name:updated", +// ORG_LOGO_UPDATED = "org:info:logo:updated", +// ORG_PLATFORM_URL_UPDATED = "org:platformurl:updated", diff --git a/packages/server/src/migrations/functions/backfill/global/user.ts b/packages/server/src/migrations/functions/backfill/global/user.ts new file mode 100644 index 0000000000..52eadeb3be --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/global/user.ts @@ -0,0 +1,3 @@ +// USER_CREATED = "user:created", +// USER_PERMISSION_ADMIN_ASSIGNED = "user:admin:assigned", +// USER_PERMISSION_BUILDER_ASSIGNED = "user:builder:assigned", diff --git a/packages/server/src/migrations/index.ts b/packages/server/src/migrations/index.ts index 6d63d81544..bf2e8f6aca 100644 --- a/packages/server/src/migrations/index.ts +++ b/packages/server/src/migrations/index.ts @@ -1,7 +1,4 @@ -const { - MIGRATION_TYPES, - runMigrations, -} = require("@budibase/backend-core/migrations") +import { migrations } from "@budibase/backend-core" // migration functions import * as userEmailViewCasing from "./functions/userEmailViewCasing" @@ -35,33 +32,43 @@ export interface MigrationOptions { export const MIGRATIONS: Migration[] = [ { - type: MIGRATION_TYPES.GLOBAL, + type: migrations.MIGRATION_TYPES.GLOBAL, name: "user_email_view_casing", fn: userEmailViewCasing.run, }, { - type: MIGRATION_TYPES.GLOBAL, + type: migrations.MIGRATION_TYPES.GLOBAL, name: "quotas_1", fn: quota1.run, }, { - type: MIGRATION_TYPES.APP, + type: migrations.MIGRATION_TYPES.APP, name: "app_urls", opts: { all: true }, fn: appUrls.run, }, { - type: MIGRATION_TYPES.GLOBAL, + type: migrations.MIGRATION_TYPES.GLOBAL, name: "developer_quota", fn: developerQuota.run, }, { - type: MIGRATION_TYPES.GLOBAL, + type: migrations.MIGRATION_TYPES.GLOBAL, name: "published_apps_quota", fn: publishedAppsQuota.run, }, + { + type: migrations.MIGRATION_TYPES.GLOBAL, + name: "event_backfill", + fn: publishedAppsQuota.run, + }, + { + type: migrations.MIGRATION_TYPES.APP, + name: "event_app_backfill", + fn: publishedAppsQuota.run, + }, ] export const migrate = async (options?: MigrationOptions) => { - await runMigrations(MIGRATIONS, options) + await migrations.runMigrations(MIGRATIONS, options) } diff --git a/packages/worker/nodemon.json b/packages/worker/nodemon.json index 3b941c4d0f..e5184939ff 100644 --- a/packages/worker/nodemon.json +++ b/packages/worker/nodemon.json @@ -1,6 +1,6 @@ { "watch": ["src", "../backend-core", "../../../budibase-pro/packages/pro"], "ext": "js,ts,json", - "ignore": ["src/**/*.spec.ts", "src/**/*.spec.js"], + "ignore": ["src/**/*.spec.ts", "src/**/*.spec.js", "../backend-core/dist/**/*"], "exec": "ts-node src/index.ts" } From 1f56d31ebb604887d6598974c1e37470eebb3461 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Thu, 5 May 2022 08:32:14 +0100 Subject: [PATCH 34/75] App backfill (wip) --- package.json | 4 +- packages/backend-core/package.json | 2 +- .../analytics/{analytics.js => Analytics.ts} | 44 +++++++---------- .../src/analytics/PosthogClient.ts | 27 ++++++++++ packages/backend-core/src/analytics/index.js | 4 -- packages/backend-core/src/analytics/index.ts | 4 ++ .../backend-core/src/analytics/posthog.js | 21 -------- .../src/events/{events.js => events.ts} | 9 ++-- .../src/events/handlers/account.ts | 8 +-- .../backend-core/src/events/handlers/app.ts | 24 ++++----- .../backend-core/src/events/handlers/auth.ts | 14 +++--- .../src/events/handlers/automation.ts | 14 +++--- .../src/events/handlers/datasource.ts | 8 +-- .../backend-core/src/events/handlers/email.ts | 6 +-- .../src/events/handlers/layout.ts | 6 +-- .../src/events/handlers/license.ts | 12 ++--- .../backend-core/src/events/handlers/org.ts | 12 ++--- .../backend-core/src/events/handlers/query.ts | 12 ++--- .../backend-core/src/events/handlers/role.ts | 12 ++--- .../backend-core/src/events/handlers/row.ts | 4 +- .../src/events/handlers/screen.ts | 6 +-- .../backend-core/src/events/handlers/serve.ts | 8 +-- .../backend-core/src/events/handlers/table.ts | 14 +++--- .../backend-core/src/events/handlers/user.ts | 28 +++++------ .../backend-core/src/events/handlers/view.ts | 22 ++++----- .../src/events/{index.js => index.ts} | 0 packages/backend-core/src/index.ts | 49 +++++++++++++------ packages/server/src/app.ts | 2 +- .../{event_app_backfill.ts => app.ts} | 4 +- .../migrations/functions/backfill/app/app.ts | 20 ++++---- .../backfill/{event_backfill.ts => global.ts} | 2 - .../migrations/functions/backfill/index.ts | 2 + packages/server/src/migrations/index.ts | 8 +-- packages/types/src/core/hosting.ts | 4 ++ packages/types/src/core/index.ts | 1 + .../types/src/documents/account/account.ts | 7 ++- .../types/src/events/{events.ts => event.ts} | 8 ++- packages/types/src/events/index.ts | 2 +- packages/types/src/index.ts | 1 + 39 files changed, 237 insertions(+), 198 deletions(-) rename packages/backend-core/src/analytics/{analytics.js => Analytics.ts} (57%) create mode 100644 packages/backend-core/src/analytics/PosthogClient.ts delete mode 100644 packages/backend-core/src/analytics/index.js create mode 100644 packages/backend-core/src/analytics/index.ts delete mode 100644 packages/backend-core/src/analytics/posthog.js rename packages/backend-core/src/events/{events.js => events.ts} (51%) rename packages/backend-core/src/events/{index.js => index.ts} (100%) rename packages/server/src/migrations/functions/backfill/{event_app_backfill.ts => app.ts} (59%) rename packages/server/src/migrations/functions/backfill/{event_backfill.ts => global.ts} (62%) create mode 100644 packages/server/src/migrations/functions/backfill/index.ts create mode 100644 packages/types/src/core/hosting.ts create mode 100644 packages/types/src/core/index.ts rename packages/types/src/events/{events.ts => event.ts} (97%) diff --git a/package.json b/package.json index 727104d830..294b15488d 100644 --- a/package.json +++ b/package.json @@ -36,8 +36,8 @@ "kill-server": "kill-port 4001 4002", "kill-all": "yarn run kill-builder && yarn run kill-server", "dev": "yarn run kill-all && lerna link && lerna run --parallel dev:builder --concurrency 1", - "dev:noserver": "yarn run kill-builder && lerna link && lerna run dev:stack:up && lerna run --parallel dev:builder --concurrency 1 --ignore @budibase/server --ignore @budibase/worker", - "dev:server": "yarn run kill-server && lerna run --parallel dev:builder --concurrency 1 --scope @budibase/worker --scope @budibase/server", + "dev:noserver": "yarn run kill-builder && lerna link && lerna run dev:stack:up && lerna run --parallel dev:builder --concurrency 1 --ignore @budibase/backend-core --ignore @budibase/server --ignore @budibase/worker", + "dev:server": "yarn run kill-server && lerna run --parallel dev:builder --concurrency 1 --scope @budibase/backend-core --scope @budibase/worker --scope @budibase/server", "test": "lerna run test", "lint:eslint": "eslint packages", "lint:prettier": "prettier --check \"packages/**/*.{js,ts,svelte}\"", diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 5ff88b6664..c9a581c73e 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -8,7 +8,7 @@ "license": "GPL-3.0", "scripts": { "build": "rimraf dist/ && tsc -p tsconfig.build.json", - "dev:builder": "rimraf dist/ && tsc -p tsconfig.build.json --watch", + "dev:builder": "rimraf dist/ && tsc -p tsconfig.build.json --watch --preserveWatchOutput", "test": "jest", "test:watch": "jest --watchAll" }, diff --git a/packages/backend-core/src/analytics/analytics.js b/packages/backend-core/src/analytics/Analytics.ts similarity index 57% rename from packages/backend-core/src/analytics/analytics.js rename to packages/backend-core/src/analytics/Analytics.ts index f7fc1360db..e2468c964b 100644 --- a/packages/backend-core/src/analytics/analytics.js +++ b/packages/backend-core/src/analytics/Analytics.ts @@ -1,32 +1,24 @@ -const PosthogClient = require("./posthog") -const env = require("../environment") -const { getTenantId } = require("../context") - -const IdentityType = { - TENANT: "tenant", - USER: "user", - ACCOUNT: "account", -} - -const Hosting = { - CLOUD: "cloud", - SELF: "self", -} +import PosthogClient from "./PosthogClient" +import env from "../environment" +import { getTenantId } from "../context" +import { Account, Hosting, Event, IdentityType } from "@budibase/types" class Analytics { + isEnabled: boolean + posthog: PosthogClient | undefined + constructor() { // check enabled before init - this.isEnabled = !!(!env.SELF_HOSTED && env.ENABLE_ANALYTICS) + this.isEnabled = !!env.ENABLE_ANALYTICS // TODO: use db flag instead if (!this.isEnabled) return - - this.posthog = new PosthogClient(process.env.POSTHOG_TOKEN) + this.posthog = new PosthogClient(env.POSTHOG_TOKEN) } enabled() { return this.isEnabled } - identify(type, id, hosting) { + identify(type: IdentityType, id: string, hosting?: Hosting) { if (!this.isEnabled) return const tenantId = getTenantId() if (!hosting) { @@ -35,12 +27,12 @@ class Analytics { const properties = { type, hosting, - tenant: tenantId, + tenantId, } - this.posthog.identify(id, properties) + this.posthog!.identify(id, properties) } - identifyUser(userId) { + identifyUser(userId: string) { this.identify(IdentityType.USER, userId) } @@ -54,23 +46,23 @@ class Analytics { this.identify(IdentityType.TENANT, distinctId) } - identifyAccount(account) { + identifyAccount(account: Account) { const distinctId = account.accountId const hosting = account.hosting this.identify(IdentityType.ACCOUNT, distinctId, hosting) } - captureEvent(eventName, properties) { + captureEvent(event: Event, properties: any) { if (!this.isEnabled) return // TODO: get the user id from context const userId = "TESTING_USER_ID" - this.posthog.capture(userId, eventName, properties) + this.posthog!.capture(userId, event, properties) } shutdown() { if (!this.isEnabled) return - this.posthog.shutdown() + this.posthog!.shutdown() } } -module.exports = Analytics +export default Analytics diff --git a/packages/backend-core/src/analytics/PosthogClient.ts b/packages/backend-core/src/analytics/PosthogClient.ts new file mode 100644 index 0000000000..f02ba3e7fb --- /dev/null +++ b/packages/backend-core/src/analytics/PosthogClient.ts @@ -0,0 +1,27 @@ +import PostHog from "posthog-node" +import { Event } from "@budibase/types" + +class PosthogClient { + posthog: PostHog + + constructor(token: string | undefined) { + if (!token) { + throw new Error("Posthog token is not defined") + } + this.posthog = new PostHog(token) + } + + identify(distinctId: string, properties: any) { + this.posthog.identify({ distinctId, properties }) + } + + capture(userId: string, event: Event, properties: any) { + this.posthog.capture({ distinctId: userId, event, properties }) + } + + shutdown() { + this.posthog.shutdown() + } +} + +export default PosthogClient diff --git a/packages/backend-core/src/analytics/index.js b/packages/backend-core/src/analytics/index.js deleted file mode 100644 index 5a097552e9..0000000000 --- a/packages/backend-core/src/analytics/index.js +++ /dev/null @@ -1,4 +0,0 @@ -const Analytics = require("./analytics") - -const analytics = new Analytics() -module.exports = analytics diff --git a/packages/backend-core/src/analytics/index.ts b/packages/backend-core/src/analytics/index.ts new file mode 100644 index 0000000000..9be70a1af4 --- /dev/null +++ b/packages/backend-core/src/analytics/index.ts @@ -0,0 +1,4 @@ +import Analytics from "./Analytics" + +const analytics = new Analytics() +export default analytics diff --git a/packages/backend-core/src/analytics/posthog.js b/packages/backend-core/src/analytics/posthog.js deleted file mode 100644 index afe1fb42af..0000000000 --- a/packages/backend-core/src/analytics/posthog.js +++ /dev/null @@ -1,21 +0,0 @@ -const PostHog = require("posthog-node") - -class PosthogClient { - constructor(token) { - this.posthog = new PostHog(token) - } - - identify(distinctId, properties) { - this.posthog.identify({ distinctId, properties }) - } - - capture(userId, event, properties) { - this.posthog.capture({ distinctId: userId, event, properties }) - } - - shutdown() { - this.posthog.shutdown() - } -} - -module.exports = PosthogClient diff --git a/packages/backend-core/src/events/events.js b/packages/backend-core/src/events/events.ts similarity index 51% rename from packages/backend-core/src/events/events.js rename to packages/backend-core/src/events/events.ts index eb00fc5d47..4ae916db16 100644 --- a/packages/backend-core/src/events/events.js +++ b/packages/backend-core/src/events/events.ts @@ -1,16 +1,17 @@ import { getTenantId } from "../context" -import { captureEvent } from "../analytics" +import analytics from "../analytics" +import { Event } from "@budibase/types" -const logEvent = messsage => { +const logEvent = (messsage: string) => { const tenantId = getTenantId() const userId = getTenantId() // TODO console.log(`[audit] [tenant=${tenantId}] [user=${userId}] ${messsage}`) } -export const processEvent = (event, properties) => { +export const processEvent = (event: Event, properties: any) => { // logging logEvent(event) // analytics - captureEvent(event, properties) + analytics.captureEvent(event, properties) } diff --git a/packages/backend-core/src/events/handlers/account.ts b/packages/backend-core/src/events/handlers/account.ts index 9577edc43b..0d3c9ba0c0 100644 --- a/packages/backend-core/src/events/handlers/account.ts +++ b/packages/backend-core/src/events/handlers/account.ts @@ -1,17 +1,17 @@ import { processEvent } from "../events" -import { Events, Account } from "@budibase/types" +import { Event, Account } from "@budibase/types" export function created(account: Account) { const properties = {} - processEvent(Events.ACCOUNT_CREATED, properties) + processEvent(Event.ACCOUNT_CREATED, properties) } export function deleted(account: Account) { const properties = {} - processEvent(Events.ACCOUNT_DELETED, properties) + processEvent(Event.ACCOUNT_DELETED, properties) } export function verified(account: Account) { const properties = {} - processEvent(Events.ACCOUNT_VERIFIED, properties) + processEvent(Event.ACCOUNT_VERIFIED, properties) } diff --git a/packages/backend-core/src/events/handlers/app.ts b/packages/backend-core/src/events/handlers/app.ts index 904f8efeda..197c778679 100644 --- a/packages/backend-core/src/events/handlers/app.ts +++ b/packages/backend-core/src/events/handlers/app.ts @@ -1,6 +1,6 @@ import { processEvent } from "../events" import { - Events, + Event, App, AppCreatedEvent, AppUpdatedEvent, @@ -17,57 +17,57 @@ import { export function created(app: App) { const properties: AppCreatedEvent = {} - processEvent(Events.APP_CREATED, properties) + processEvent(Event.APP_CREATED, properties) } export function updated(app: App) { const properties: AppUpdatedEvent = {} - processEvent(Events.APP_UPDATED, properties) + processEvent(Event.APP_UPDATED, properties) } export function deleted(app: App) { const properties: AppDeletedEvent = {} - processEvent(Events.APP_DELETED, properties) + processEvent(Event.APP_DELETED, properties) } export function published(app: App) { const properties: AppPublishedEvent = {} - processEvent(Events.APP_PUBLISHED, properties) + processEvent(Event.APP_PUBLISHED, properties) } export function unpublished(app: App) { const properties: AppUnpublishedEvent = {} - processEvent(Events.APP_UNPUBLISHED, properties) + processEvent(Event.APP_UNPUBLISHED, properties) } export function fileImported(app: App) { const properties: AppFileImportedEvent = {} - processEvent(Events.APP_FILE_IMPORTED, properties) + processEvent(Event.APP_FILE_IMPORTED, properties) } export function templateImported(templateKey: string) { const properties: AppTemplateImportedEvent = { templateKey, } - processEvent(Events.APP_TEMPLATE_IMPORTED, properties) + processEvent(Event.APP_TEMPLATE_IMPORTED, properties) } export function versionUpdated(app: App) { const properties: AppVersionUpdatedEvent = {} - processEvent(Events.APP_VERSION_UPDATED, properties) + processEvent(Event.APP_VERSION_UPDATED, properties) } export function versionReverted(app: App) { const properties: AppVersionRevertedEvent = {} - processEvent(Events.APP_VERSION_REVERTED, properties) + processEvent(Event.APP_VERSION_REVERTED, properties) } export function reverted(app: App) { const properties: AppRevertedEvent = {} - processEvent(Events.APP_REVERTED, properties) + processEvent(Event.APP_REVERTED, properties) } export function exported(app: App) { const properties: AppExportedEvent = {} - processEvent(Events.APP_EXPORTED, properties) + processEvent(Event.APP_EXPORTED, properties) } diff --git a/packages/backend-core/src/events/handlers/auth.ts b/packages/backend-core/src/events/handlers/auth.ts index 8d74a9a103..d925d1a340 100644 --- a/packages/backend-core/src/events/handlers/auth.ts +++ b/packages/backend-core/src/events/handlers/auth.ts @@ -1,6 +1,6 @@ import { processEvent } from "../events" import { - Events, + Event, LoginEvent, LoginSource, LogoutEvent, @@ -15,38 +15,38 @@ export function login(source: LoginSource) { const properties: LoginEvent = { source, } - processEvent(Events.AUTH_LOGIN, properties) + processEvent(Event.AUTH_LOGIN, properties) } export function logout() { const properties: LogoutEvent = {} - processEvent(Events.AUTH_LOGOUT, properties) + processEvent(Event.AUTH_LOGOUT, properties) } export function SSOCreated(type: SSOType) { const properties: SSOCreatedEvent = { type, } - processEvent(Events.AUTH_SSO_CREATED, properties) + processEvent(Event.AUTH_SSO_CREATED, properties) } export function SSOUpdated(type: SSOType) { const properties: SSOUpdatedEvent = { type, } - processEvent(Events.AUTH_SSO_UPDATED, properties) + processEvent(Event.AUTH_SSO_UPDATED, properties) } export function SSOActivated(type: SSOType) { const properties: SSOActivatedEvent = { type, } - processEvent(Events.AUTH_SSO_ACTIVATED, properties) + processEvent(Event.AUTH_SSO_ACTIVATED, properties) } export function SSODeactivated(type: SSOType) { const properties: SSODeactivatedEvent = { type, } - processEvent(Events.AUTH_SSO_DEACTIVATED, properties) + processEvent(Event.AUTH_SSO_DEACTIVATED, properties) } diff --git a/packages/backend-core/src/events/handlers/automation.ts b/packages/backend-core/src/events/handlers/automation.ts index 22986bf935..fa9b71a8cf 100644 --- a/packages/backend-core/src/events/handlers/automation.ts +++ b/packages/backend-core/src/events/handlers/automation.ts @@ -1,7 +1,7 @@ import { processEvent } from "../events" import { Automation, - Events, + Event, AutomationStep, AutomationTrigger, AutomationCreatedEvent, @@ -14,17 +14,17 @@ import { export function created(automation: Automation) { const properties: AutomationCreatedEvent = {} - processEvent(Events.AUTOMATION_CREATED, properties) + processEvent(Event.AUTOMATION_CREATED, properties) } export function deleted(automation: Automation) { const properties: AutomationDeletedEvent = {} - processEvent(Events.AUTOMATION_DELETED, properties) + processEvent(Event.AUTOMATION_DELETED, properties) } export function tested(automation: Automation) { const properties: AutomationTestedEvent = {} - processEvent(Events.AUTOMATION_TESTED, properties) + processEvent(Event.AUTOMATION_TESTED, properties) } // TODO @@ -35,12 +35,12 @@ export function tested(automation: Automation) { export function stepCreated(automation: Automation, step: AutomationStep) { const properties: AutomationStepCreatedEvent = {} - processEvent(Events.AUTOMATION_STEP_CREATED, properties) + processEvent(Event.AUTOMATION_STEP_CREATED, properties) } export function stepDeleted(automation: Automation, step: AutomationStep) { const properties: AutomationStepDeletedEvent = {} - processEvent(Events.AUTOMATION_STEP_DELETED, properties) + processEvent(Event.AUTOMATION_STEP_DELETED, properties) } export function triggerUpdated( @@ -48,5 +48,5 @@ export function triggerUpdated( trigger: AutomationTrigger ) { const properties: AutomationTriggerUpdatedEvent = {} - processEvent(Events.AUTOMATION_TRIGGER_UPDATED, properties) + processEvent(Event.AUTOMATION_TRIGGER_UPDATED, properties) } diff --git a/packages/backend-core/src/events/handlers/datasource.ts b/packages/backend-core/src/events/handlers/datasource.ts index c32d4292ba..9ebe73928e 100644 --- a/packages/backend-core/src/events/handlers/datasource.ts +++ b/packages/backend-core/src/events/handlers/datasource.ts @@ -1,6 +1,6 @@ import { processEvent } from "../events" import { - Events, + Event, Datasource, DatasourceCreatedEvent, DatasourceUpdatedEvent, @@ -9,15 +9,15 @@ import { export function created(datasource: Datasource) { const properties: DatasourceCreatedEvent = {} - processEvent(Events.DATASOURCE_CREATED, properties) + processEvent(Event.DATASOURCE_CREATED, properties) } export function updated(datasource: Datasource) { const properties: DatasourceUpdatedEvent = {} - processEvent(Events.DATASOURCE_UPDATED, properties) + processEvent(Event.DATASOURCE_UPDATED, properties) } export function deleted(datasource: Datasource) { const properties: DatasourceDeletedEvent = {} - processEvent(Events.DATASOURCE_DELETED, properties) + processEvent(Event.DATASOURCE_DELETED, properties) } diff --git a/packages/backend-core/src/events/handlers/email.ts b/packages/backend-core/src/events/handlers/email.ts index d9f0320540..ff1783b682 100644 --- a/packages/backend-core/src/events/handlers/email.ts +++ b/packages/backend-core/src/events/handlers/email.ts @@ -1,6 +1,6 @@ import { processEvent } from "../events" import { - Events, + Event, SMTPConfig, SMTPCreatedEvent, SMTPUpdatedEvent, @@ -8,10 +8,10 @@ import { export function SMTPCreated(config: SMTPConfig) { const properties: SMTPCreatedEvent = {} - processEvent(Events.EMAIL_SMTP_CREATED, properties) + processEvent(Event.EMAIL_SMTP_CREATED, properties) } export function SMTPUpdated(config: SMTPConfig) { const properties: SMTPUpdatedEvent = {} - processEvent(Events.EMAIL_SMTP_UPDATED, properties) + processEvent(Event.EMAIL_SMTP_UPDATED, properties) } diff --git a/packages/backend-core/src/events/handlers/layout.ts b/packages/backend-core/src/events/handlers/layout.ts index 325f4dda50..6d4ff9f4a0 100644 --- a/packages/backend-core/src/events/handlers/layout.ts +++ b/packages/backend-core/src/events/handlers/layout.ts @@ -1,6 +1,6 @@ import { processEvent } from "../events" import { - Events, + Event, Layout, LayoutCreatedEvent, LayoutDeletedEvent, @@ -8,10 +8,10 @@ import { export function created(layout: Layout) { const properties: LayoutCreatedEvent = {} - processEvent(Events.LAYOUT_CREATED, properties) + processEvent(Event.LAYOUT_CREATED, properties) } export function deleted(layout: Layout) { const properties: LayoutDeletedEvent = {} - processEvent(Events.LAYOUT_DELETED, properties) + processEvent(Event.LAYOUT_DELETED, properties) } diff --git a/packages/backend-core/src/events/handlers/license.ts b/packages/backend-core/src/events/handlers/license.ts index f33deb2018..91b97c00ae 100644 --- a/packages/backend-core/src/events/handlers/license.ts +++ b/packages/backend-core/src/events/handlers/license.ts @@ -1,6 +1,6 @@ import { processEvent } from "../events" import { - Events, + Event, License, LicenseActivatedEvent, LicenseDowngradedEvent, @@ -12,25 +12,25 @@ import { // TODO export function updgraded(license: License) { const properties: LicenseUpgradedEvent = {} - processEvent(Events.LICENSE_UPGRADED, properties) + processEvent(Event.LICENSE_UPGRADED, properties) } // TODO export function downgraded(license: License) { const properties: LicenseDowngradedEvent = {} - processEvent(Events.LICENSE_DOWNGRADED, properties) + processEvent(Event.LICENSE_DOWNGRADED, properties) } // TODO export function updated(license: License) { const properties: LicenseUpdatedEvent = {} - processEvent(Events.LICENSE_UPDATED, properties) + processEvent(Event.LICENSE_UPDATED, properties) } // TODO export function activated(license: License) { const properties: LicenseActivatedEvent = {} - processEvent(Events.LICENSE_ACTIVATED, properties) + processEvent(Event.LICENSE_ACTIVATED, properties) } // TODO @@ -39,5 +39,5 @@ export function quotaExceeded(quotaName: string, value: number) { name: quotaName, value, } - processEvent(Events.LICENSE_QUOTA_EXCEEDED, properties) + processEvent(Event.LICENSE_QUOTA_EXCEEDED, properties) } diff --git a/packages/backend-core/src/events/handlers/org.ts b/packages/backend-core/src/events/handlers/org.ts index 3f8658f2f6..5e1fc12b80 100644 --- a/packages/backend-core/src/events/handlers/org.ts +++ b/packages/backend-core/src/events/handlers/org.ts @@ -1,30 +1,30 @@ import { processEvent } from "../events" -import { Events, VersionCheckedEvent } from "@budibase/types" +import { Event, VersionCheckedEvent } from "@budibase/types" export function nameUpdated() { const properties = {} - processEvent(Events.ORG_NAME_UPDATED, properties) + processEvent(Event.ORG_NAME_UPDATED, properties) } export function logoUpdated() { const properties = {} - processEvent(Events.ORG_LOGO_UPDATED, properties) + processEvent(Event.ORG_LOGO_UPDATED, properties) } export function platformURLUpdated() { const properties = {} - processEvent(Events.ORG_PLATFORM_URL_UPDATED, properties) + processEvent(Event.ORG_PLATFORM_URL_UPDATED, properties) } export function versionChecked(version: number) { const properties: VersionCheckedEvent = { version, } - processEvent(Events.UPDATE_VERSION_CHECKED, properties) + processEvent(Event.UPDATE_VERSION_CHECKED, properties) } // TODO export function analyticsOptOut() { const properties = {} - processEvent(Events.ANALYTICS_OPT_OUT, properties) + processEvent(Event.ANALYTICS_OPT_OUT, properties) } diff --git a/packages/backend-core/src/events/handlers/query.ts b/packages/backend-core/src/events/handlers/query.ts index bc173008bd..059381ba31 100644 --- a/packages/backend-core/src/events/handlers/query.ts +++ b/packages/backend-core/src/events/handlers/query.ts @@ -1,6 +1,6 @@ import { processEvent } from "../events" import { - Events, + Event, Datasource, Query, QueryCreatedEvent, @@ -14,17 +14,17 @@ import { export const created = (datasource: Datasource, query: Query) => { const properties: QueryCreatedEvent = {} - processEvent(Events.QUERY_CREATED, properties) + processEvent(Event.QUERY_CREATED, properties) } export const updated = (datasource: Datasource, query: Query) => { const properties: QueryUpdatedEvent = {} - processEvent(Events.QUERY_UPDATED, properties) + processEvent(Event.QUERY_UPDATED, properties) } export const deleted = (datasource: Datasource, query: Query) => { const properties: QueryDeletedEvent = {} - processEvent(Events.QUERY_DELETED, properties) + processEvent(Event.QUERY_DELETED, properties) } export const imported = ( @@ -33,7 +33,7 @@ export const imported = ( count: any ) => { const properties: QueryImportedEvent = {} - processEvent(Events.QUERY_IMPORT, properties) + processEvent(Event.QUERY_IMPORT, properties) } // TODO @@ -44,5 +44,5 @@ export const imported = ( export const previewed = (datasource: Datasource) => { const properties: QueryPreviewedEvent = {} - processEvent(Events.QUERY_PREVIEWED, properties) + processEvent(Event.QUERY_PREVIEWED, properties) } diff --git a/packages/backend-core/src/events/handlers/role.ts b/packages/backend-core/src/events/handlers/role.ts index e41b50389b..58abd4bdca 100644 --- a/packages/backend-core/src/events/handlers/role.ts +++ b/packages/backend-core/src/events/handlers/role.ts @@ -1,6 +1,6 @@ import { processEvent } from "../events" import { - Events, + Event, Role, RoleAssignedEvent, RoleCreatedEvent, @@ -14,25 +14,25 @@ import { export function created(role: Role) { const properties: RoleCreatedEvent = {} - processEvent(Events.ROLE_CREATED, properties) + processEvent(Event.ROLE_CREATED, properties) } export function updated(role: Role) { const properties: RoleUpdatedEvent = {} - processEvent(Events.ROLE_UPDATED, properties) + processEvent(Event.ROLE_UPDATED, properties) } export function deleted(role: Role) { const properties: RoleDeletedEvent = {} - processEvent(Events.ROLE_DELETED, properties) + processEvent(Event.ROLE_DELETED, properties) } export function assigned(user: User, role: Role) { const properties: RoleAssignedEvent = {} - processEvent(Events.ROLE_ASSIGNED, properties) + processEvent(Event.ROLE_ASSIGNED, properties) } export function unassigned(user: User, role: Role) { const properties: RoleUnassignedEvent = {} - processEvent(Events.ROLE_UNASSIGNED, properties) + processEvent(Event.ROLE_UNASSIGNED, properties) } diff --git a/packages/backend-core/src/events/handlers/row.ts b/packages/backend-core/src/events/handlers/row.ts index c5e34f786d..0f6cf3a00c 100644 --- a/packages/backend-core/src/events/handlers/row.ts +++ b/packages/backend-core/src/events/handlers/row.ts @@ -1,6 +1,6 @@ import { processEvent } from "../events" import { - Events, + Event, RowImportedEvent, RowImportFormat, Table, @@ -19,5 +19,5 @@ export const imported = ( count: number ) => { const properties: RowImportedEvent = {} - processEvent(Events.ROW_IMPORT, properties) + processEvent(Event.ROW_IMPORT, properties) } diff --git a/packages/backend-core/src/events/handlers/screen.ts b/packages/backend-core/src/events/handlers/screen.ts index eade48bf30..d152c43a9c 100644 --- a/packages/backend-core/src/events/handlers/screen.ts +++ b/packages/backend-core/src/events/handlers/screen.ts @@ -1,6 +1,6 @@ import { processEvent } from "../events" import { - Events, + Event, Screen, ScreenCreatedEvent, ScreenDeletedEvent, @@ -8,10 +8,10 @@ import { export function created(screen: Screen) { const properties: ScreenCreatedEvent = {} - processEvent(Events.SCREEN_CREATED, properties) + processEvent(Event.SCREEN_CREATED, properties) } export function deleted(screen: Screen) { const properties: ScreenDeletedEvent = {} - processEvent(Events.SCREEN_DELETED, properties) + processEvent(Event.SCREEN_DELETED, properties) } diff --git a/packages/backend-core/src/events/handlers/serve.ts b/packages/backend-core/src/events/handlers/serve.ts index fd7694008c..c1cb513d0b 100644 --- a/packages/backend-core/src/events/handlers/serve.ts +++ b/packages/backend-core/src/events/handlers/serve.ts @@ -2,7 +2,7 @@ import { processEvent } from "../events" import { App, BuilderServedEvent, - Events, + Event, AppPreviewServedEvent, AppServedEvent, } from "@budibase/types" @@ -11,15 +11,15 @@ import { export function servedBuilder(version: number) { const properties: BuilderServedEvent = {} - processEvent(Events.SERVED_BUILDER, properties) + processEvent(Event.SERVED_BUILDER, properties) } export function servedApp(app: App) { const properties: AppServedEvent = {} - processEvent(Events.SERVED_APP, properties) + processEvent(Event.SERVED_APP, properties) } export function servedAppPreview(app: App) { const properties: AppPreviewServedEvent = {} - processEvent(Events.SERVED_APP_PREVIEW, properties) + processEvent(Event.SERVED_APP_PREVIEW, properties) } diff --git a/packages/backend-core/src/events/handlers/table.ts b/packages/backend-core/src/events/handlers/table.ts index 5ebadd14fd..ca0109a8ea 100644 --- a/packages/backend-core/src/events/handlers/table.ts +++ b/packages/backend-core/src/events/handlers/table.ts @@ -1,6 +1,6 @@ import { processEvent } from "../events" import { - Events, + Event, TableExportFormat, TableImportFormat, Table, @@ -15,31 +15,31 @@ import { export function created(table: Table) { const properties: TableCreatedEvent = {} - processEvent(Events.TABLE_CREATED, properties) + processEvent(Event.TABLE_CREATED, properties) } export function updated(table: Table) { const properties: TableUpdatedEvent = {} - processEvent(Events.TABLE_UPDATED, properties) + processEvent(Event.TABLE_UPDATED, properties) } export function deleted(table: Table) { const properties: TableDeletedEvent = {} - processEvent(Events.TABLE_DELETED, properties) + processEvent(Event.TABLE_DELETED, properties) } export function exported(table: Table, format: TableExportFormat) { const properties: TableExportedEvent = {} - processEvent(Events.TABLE_EXPORTED, properties) + processEvent(Event.TABLE_EXPORTED, properties) } export function imported(table: Table, format: TableImportFormat) { const properties: TableImportedEvent = {} - processEvent(Events.TABLE_IMPORTED, properties) + processEvent(Event.TABLE_IMPORTED, properties) } // TODO export function permissionUpdated() { const properties = {} - processEvent(Events.TABLE_PERMISSION_UPDATED, properties) + processEvent(Event.TABLE_PERMISSION_UPDATED, properties) } diff --git a/packages/backend-core/src/events/handlers/user.ts b/packages/backend-core/src/events/handlers/user.ts index 6328059cb1..48ae7b698a 100644 --- a/packages/backend-core/src/events/handlers/user.ts +++ b/packages/backend-core/src/events/handlers/user.ts @@ -1,6 +1,6 @@ import { processEvent } from "../events" import { - Events, + Event, User, UserCreatedEvent, UserDeletedEvent, @@ -19,71 +19,71 @@ import { export function created(user: User) { const properties: UserCreatedEvent = {} - processEvent(Events.USER_CREATED, properties) + processEvent(Event.USER_CREATED, properties) } export function updated(user: User) { const properties: UserUpdatedEvent = {} - processEvent(Events.USER_UPDATED, properties) + processEvent(Event.USER_UPDATED, properties) } export function deleted(user: User) { const properties: UserDeletedEvent = {} - processEvent(Events.USER_DELETED, properties) + processEvent(Event.USER_DELETED, properties) } // PERMISSIONS export function permissionAdminAssigned(user: User) { const properties: UserPermissionAssignedEvent = {} - processEvent(Events.USER_PERMISSION_ADMIN_ASSIGNED, properties) + processEvent(Event.USER_PERMISSION_ADMIN_ASSIGNED, properties) } export function permissionAdminRemoved(user: User) { const properties: UserPermissionRemovedEvent = {} - processEvent(Events.USER_PERMISSION_ADMIN_REMOVED, properties) + processEvent(Event.USER_PERMISSION_ADMIN_REMOVED, properties) } export function permissionBuilderAssigned(user: User) { const properties: UserPermissionAssignedEvent = {} - processEvent(Events.USER_PERMISSION_BUILDER_ASSIGNED, properties) + processEvent(Event.USER_PERMISSION_BUILDER_ASSIGNED, properties) } export function permissionBuilderRemoved(user: User) { const properties: UserPermissionRemovedEvent = {} - processEvent(Events.USER_PERMISSION_BUILDER_REMOVED, properties) + processEvent(Event.USER_PERMISSION_BUILDER_REMOVED, properties) } // INVITE export function invited(userInfo: any) { const properties: UserInvitedEvent = {} - processEvent(Events.USER_INVITED, properties) + processEvent(Event.USER_INVITED, properties) } export function inviteAccepted(user: User) { const properties: UserInviteAcceptedEvent = {} - processEvent(Events.USER_INVITED_ACCEPTED, properties) + processEvent(Event.USER_INVITED_ACCEPTED, properties) } // PASSWORD export function passwordForceReset(user: User) { const properties: UserPasswordForceResetEvent = {} - processEvent(Events.USER_PASSWORD_FORCE_RESET, properties) + processEvent(Event.USER_PASSWORD_FORCE_RESET, properties) } export function passwordUpdated(user: User) { const properties: UserPasswordUpdatedEvent = {} - processEvent(Events.USER_PASSWORD_UPDATED, properties) + processEvent(Event.USER_PASSWORD_UPDATED, properties) } export function passwordResetRequested(user: User) { const properties: UserPasswordResetRequestedEvent = {} - processEvent(Events.USER_PASSWORD_RESET_REQUESTED, properties) + processEvent(Event.USER_PASSWORD_RESET_REQUESTED, properties) } export function passwordReset(user: User) { const properties: UserPasswordResetEvent = {} - processEvent(Events.USER_PASSWORD_RESET, properties) + processEvent(Event.USER_PASSWORD_RESET, properties) } diff --git a/packages/backend-core/src/events/handlers/view.ts b/packages/backend-core/src/events/handlers/view.ts index 77b6c573ad..9fd3370f57 100644 --- a/packages/backend-core/src/events/handlers/view.ts +++ b/packages/backend-core/src/events/handlers/view.ts @@ -1,6 +1,6 @@ import { processEvent } from "../events" import { - Events, + Event, ViewCalculationCreatedEvent, ViewCalculationDeletedEvent, ViewCalculationUpdatedEvent, @@ -20,50 +20,50 @@ import { export function created(view: View) { const properties: ViewCreatedEvent = {} - processEvent(Events.VIEW_CREATED, properties) + processEvent(Event.VIEW_CREATED, properties) } export function updated(view: View) { const properties: ViewUpdatedEvent = {} - processEvent(Events.VIEW_UPDATED, properties) + processEvent(Event.VIEW_UPDATED, properties) } export function deleted() { const properties: ViewDeletedEvent = {} - processEvent(Events.VIEW_DELETED, properties) + processEvent(Event.VIEW_DELETED, properties) } export function exported(table: Table, format: TableExportFormat) { const properties: ViewExportedEvent = {} - processEvent(Events.VIEW_EXPORTED, properties) + processEvent(Event.VIEW_EXPORTED, properties) } export function filterCreated() { const properties: ViewFilterCreatedEvent = {} - processEvent(Events.VIEW_FILTER_CREATED, properties) + processEvent(Event.VIEW_FILTER_CREATED, properties) } export function filterUpdated() { const properties: ViewFilterUpdatedEvent = {} - processEvent(Events.VIEW_FILTER_UPDATED, properties) + processEvent(Event.VIEW_FILTER_UPDATED, properties) } export function filterDeleted() { const properties: ViewFilterDeletedEvent = {} - processEvent(Events.VIEW_FILTER_DELETED, properties) + processEvent(Event.VIEW_FILTER_DELETED, properties) } export function calculationCreated() { const properties: ViewCalculationCreatedEvent = {} - processEvent(Events.VIEW_CALCULATION_CREATED, properties) + processEvent(Event.VIEW_CALCULATION_CREATED, properties) } export function calculationUpdated() { const properties: ViewCalculationUpdatedEvent = {} - processEvent(Events.VIEW_CALCULATION_UPDATED, properties) + processEvent(Event.VIEW_CALCULATION_UPDATED, properties) } export function calculationDeleted() { const properties: ViewCalculationDeletedEvent = {} - processEvent(Events.VIEW_CALCULATION_DELETED, properties) + processEvent(Event.VIEW_CALCULATION_DELETED, properties) } diff --git a/packages/backend-core/src/events/index.js b/packages/backend-core/src/events/index.ts similarity index 100% rename from packages/backend-core/src/events/index.js rename to packages/backend-core/src/events/index.ts diff --git a/packages/backend-core/src/index.ts b/packages/backend-core/src/index.ts index dab77d130a..3638c8fd0d 100644 --- a/packages/backend-core/src/index.ts +++ b/packages/backend-core/src/index.ts @@ -2,6 +2,23 @@ import db from "./db" import errors from "./errors" import * as events from "./events" import * as migrations from "./migrations" +import * as users from "./users" +import env from "./environment" +import accounts from "./cloud/accounts" +import tenancy from "./tenancy" +import featureFlags from "./featureFlags" +import analytics from "./analytics" +import sessions from "./security/sessions" +import deprovisioning from "./context/deprovision" + +// outer packages +import dbPkg from "../db" +import redis from "../redis" +import objectStore from "../objectStore" +import utils from "../utils" +import cache from "../cache" +import auth from "../auth" +import constants from "../constants" export = { init(opts: any = {}) { @@ -9,24 +26,24 @@ export = { }, // some default exports from the library, however these ideally shouldn't // be used, instead the syntax require("@budibase/backend-core/db") should be used - StaticDatabases: require("./db/utils").StaticDatabases, - db: require("../db"), - redis: require("../redis"), - objectStore: require("../objectStore"), - utils: require("../utils"), - users: require("./users"), - cache: require("../cache"), - auth: require("../auth"), - constants: require("../constants"), + StaticDatabases: dbPkg.StaticDatabases, + db: dbPkg, + redis, + objectStore, + utils, + users, + cache, + auth, + constants, migrations, errors, ...errors.errors, - env: require("./environment"), - accounts: require("./cloud/accounts"), - tenancy: require("./tenancy"), - featureFlags: require("./featureFlags"), + env, + accounts, + tenancy, + featureFlags, events, - analytics: require("./analytics"), - sessions: require("./security/sessions"), - deprovisioning: require("./context/deprovision"), + analytics, + sessions, + deprovisioning, } diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts index 4060749629..cdd087d68d 100644 --- a/packages/server/src/app.ts +++ b/packages/server/src/app.ts @@ -102,7 +102,7 @@ process.on("SIGTERM", () => { // run migrations on startup if not done via http // not recommended in a clustered environment -if (!env.HTTP_MIGRATIONS) { +if (!env.HTTP_MIGRATIONS && !env.isTest()) { migrations.migrate().catch(err => { console.error("Error performing migrations. Exiting.\n", err) shutdown() diff --git a/packages/server/src/migrations/functions/backfill/event_app_backfill.ts b/packages/server/src/migrations/functions/backfill/app.ts similarity index 59% rename from packages/server/src/migrations/functions/backfill/event_app_backfill.ts rename to packages/server/src/migrations/functions/backfill/app.ts index 5b46959b5b..6080f75b1f 100644 --- a/packages/server/src/migrations/functions/backfill/event_app_backfill.ts +++ b/packages/server/src/migrations/functions/backfill/app.ts @@ -8,4 +8,6 @@ import * as app from "./app/app" * Backfill app events. */ -export const run = async (db: any) => {} +export const run = async (appDb: any) => { + await app.backfill(appDb) +} diff --git a/packages/server/src/migrations/functions/backfill/app/app.ts b/packages/server/src/migrations/functions/backfill/app/app.ts index 6ebfc48297..ab195636cc 100644 --- a/packages/server/src/migrations/functions/backfill/app/app.ts +++ b/packages/server/src/migrations/functions/backfill/app/app.ts @@ -1,10 +1,12 @@ -export const backfillAppCreated = () => {} +import { events, db } from "@budibase/backend-core" +import { App } from "@budibase/types" -export const backfillAppPublished = () => {} - -// APP_CREATED = "app:created", -// APP_PUBLISHED = "app:published", - -// Maybe -// APP_TEMPLATE_IMPORTED = "app:template:imported", -// APP_FILE_IMPORTED = "app:file:imported", +export const backfill = async (appDb: any) => { + const app: App = await appDb.get(db.DocumentTypes.APP_METADATA) + if (db.isDevAppID(app.appId)) { + events.app.created(app) + } + if (db.isProdAppID(app.appId)) { + events.app.published(app) + } +} diff --git a/packages/server/src/migrations/functions/backfill/event_backfill.ts b/packages/server/src/migrations/functions/backfill/global.ts similarity index 62% rename from packages/server/src/migrations/functions/backfill/event_backfill.ts rename to packages/server/src/migrations/functions/backfill/global.ts index d010c1c149..b28cf48d99 100644 --- a/packages/server/src/migrations/functions/backfill/event_backfill.ts +++ b/packages/server/src/migrations/functions/backfill/global.ts @@ -1,5 +1,3 @@ -import * as syncPublishedApps from "../usageQuotas/syncPublishedApps" - /** * Date: * May 2022 diff --git a/packages/server/src/migrations/functions/backfill/index.ts b/packages/server/src/migrations/functions/backfill/index.ts new file mode 100644 index 0000000000..fb9859a581 --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/index.ts @@ -0,0 +1,2 @@ +export * as app from "./app" +export * as global from "./global" diff --git a/packages/server/src/migrations/index.ts b/packages/server/src/migrations/index.ts index bf2e8f6aca..991cde650d 100644 --- a/packages/server/src/migrations/index.ts +++ b/packages/server/src/migrations/index.ts @@ -6,6 +6,7 @@ import * as quota1 from "./functions/quotas1" import * as appUrls from "./functions/appUrls" import * as developerQuota from "./functions/developerQuota" import * as publishedAppsQuota from "./functions/publishedAppsQuota" +import * as backfill from "./functions/backfill" export interface Migration { type: string @@ -59,13 +60,14 @@ export const MIGRATIONS: Migration[] = [ }, { type: migrations.MIGRATION_TYPES.GLOBAL, - name: "event_backfill", - fn: publishedAppsQuota.run, + name: "event_global_backfill", + fn: backfill.global.run, }, { type: migrations.MIGRATION_TYPES.APP, name: "event_app_backfill", - fn: publishedAppsQuota.run, + opts: { all: true }, + fn: backfill.app.run, }, ] diff --git a/packages/types/src/core/hosting.ts b/packages/types/src/core/hosting.ts new file mode 100644 index 0000000000..44d46caa56 --- /dev/null +++ b/packages/types/src/core/hosting.ts @@ -0,0 +1,4 @@ +export enum Hosting { + CLOUD = "cloud", + SELF = "self", +} diff --git a/packages/types/src/core/index.ts b/packages/types/src/core/index.ts new file mode 100644 index 0000000000..11d05c91f4 --- /dev/null +++ b/packages/types/src/core/index.ts @@ -0,0 +1 @@ +export * from "./hosting" diff --git a/packages/types/src/documents/account/account.ts b/packages/types/src/documents/account/account.ts index bfafd9fd85..3822d63d58 100644 --- a/packages/types/src/documents/account/account.ts +++ b/packages/types/src/documents/account/account.ts @@ -1 +1,6 @@ -export interface Account {} +import { Hosting } from "../../core" + +export interface Account { + accountId: string + hosting: Hosting +} diff --git a/packages/types/src/events/events.ts b/packages/types/src/events/event.ts similarity index 97% rename from packages/types/src/events/events.ts rename to packages/types/src/events/event.ts index 9c6a6031e2..2e1f052297 100644 --- a/packages/types/src/events/events.ts +++ b/packages/types/src/events/event.ts @@ -1,4 +1,4 @@ -export enum Events { +export enum Event { // USER USER_CREATED = "user:created", USER_UPDATED = "user:updated", @@ -140,6 +140,12 @@ export enum Events { ACCOUNT_VERIFIED = "account:verified", } +export enum IdentityType { + TENANT = "tenant", + USER = "user", + ACCOUNT = "account", +} + export type RowImportFormat = "csv" export type TableExportFormat = "json" | "csv" export type TableImportFormat = "csv" diff --git a/packages/types/src/events/index.ts b/packages/types/src/events/index.ts index 22a3f9af8d..a47ab8104e 100644 --- a/packages/types/src/events/index.ts +++ b/packages/types/src/events/index.ts @@ -3,7 +3,7 @@ export * from "./auth" export * from "./automation" export * from "./config" export * from "./datasource" -export * from "./events" +export * from "./event" export * from "./layout" export * from "./license" export * from "./org" diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index b6c01a2fe5..b0c3e10d88 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,3 +1,4 @@ export * from "./documents" export * from "./events" export * from "./licensing" +export * from "./core" From 8e885075b63f090ee8bb5a48e0d7b8bd6c72e13d Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 10 May 2022 10:33:59 +0100 Subject: [PATCH 35/75] handlers -> publishers and analtics -> processors --- .../backend-core/src/analytics/Analytics.ts | 68 --- packages/backend-core/src/analytics/index.ts | 4 - packages/backend-core/src/events/events.ts | 20 +- packages/backend-core/src/events/index.ts | 8 +- .../src/events/processors/LoggingProcessor.ts | 15 + .../processors/PosthogProcessor.ts} | 15 +- .../src/events/processors/Processors.ts | 63 +++ .../src/events/processors/index.ts | 2 + .../src/events/processors/types.ts | 11 + .../{handlers => publishers}/account.ts | 8 +- .../events/{handlers => publishers}/app.ts | 34 +- .../events/{handlers => publishers}/auth.ts | 14 +- .../{handlers => publishers}/automation.ts | 14 +- .../{handlers => publishers}/datasource.ts | 8 +- .../events/{handlers => publishers}/email.ts | 6 +- .../events/{handlers => publishers}/index.ts | 0 .../events/{handlers => publishers}/layout.ts | 6 +- .../{handlers => publishers}/license.ts | 12 +- .../events/{handlers => publishers}/org.ts | 12 +- .../events/{handlers => publishers}/query.ts | 12 +- .../events/{handlers => publishers}/role.ts | 12 +- .../events/{handlers => publishers}/row.ts | 4 +- .../events/{handlers => publishers}/screen.ts | 6 +- .../events/{handlers => publishers}/serve.ts | 8 +- .../events/{handlers => publishers}/table.ts | 14 +- .../events/{handlers => publishers}/user.ts | 28 +- .../events/{handlers => publishers}/view.ts | 22 +- packages/backend-core/src/index.ts | 2 - .../server/src/api/controllers/application.ts | 2 +- packages/server/src/app.ts | 4 +- .../migrations/functions/backfill/app/app.ts | 2 + packages/server/yarn.lock | 506 +----------------- packages/types/src/documents/app/app.ts | 17 +- packages/types/src/documents/app/document.ts | 6 + packages/types/src/documents/app/index.ts | 1 + packages/worker/src/index.ts | 2 + packages/worker/yarn.lock | 18 +- 37 files changed, 281 insertions(+), 705 deletions(-) delete mode 100644 packages/backend-core/src/analytics/Analytics.ts delete mode 100644 packages/backend-core/src/analytics/index.ts create mode 100644 packages/backend-core/src/events/processors/LoggingProcessor.ts rename packages/backend-core/src/{analytics/PosthogClient.ts => events/processors/PosthogProcessor.ts} (66%) create mode 100644 packages/backend-core/src/events/processors/Processors.ts create mode 100644 packages/backend-core/src/events/processors/index.ts create mode 100644 packages/backend-core/src/events/processors/types.ts rename packages/backend-core/src/events/{handlers => publishers}/account.ts (57%) rename packages/backend-core/src/events/{handlers => publishers}/app.ts (59%) rename packages/backend-core/src/events/{handlers => publishers}/auth.ts (69%) rename packages/backend-core/src/events/{handlers => publishers}/automation.ts (74%) rename packages/backend-core/src/events/{handlers => publishers}/datasource.ts (68%) rename packages/backend-core/src/events/{handlers => publishers}/email.ts (66%) rename packages/backend-core/src/events/{handlers => publishers}/index.ts (100%) rename packages/backend-core/src/events/{handlers => publishers}/layout.ts (66%) rename packages/backend-core/src/events/{handlers => publishers}/license.ts (71%) rename packages/backend-core/src/events/{handlers => publishers}/org.ts (58%) rename packages/backend-core/src/events/{handlers => publishers}/query.ts (76%) rename packages/backend-core/src/events/{handlers => publishers}/role.ts (69%) rename packages/backend-core/src/events/{handlers => publishers}/row.ts (81%) rename packages/backend-core/src/events/{handlers => publishers}/screen.ts (66%) rename packages/backend-core/src/events/{handlers => publishers}/serve.ts (68%) rename packages/backend-core/src/events/{handlers => publishers}/table.ts (69%) rename packages/backend-core/src/events/{handlers => publishers}/user.ts (68%) rename packages/backend-core/src/events/{handlers => publishers}/view.ts (68%) create mode 100644 packages/types/src/documents/app/document.ts diff --git a/packages/backend-core/src/analytics/Analytics.ts b/packages/backend-core/src/analytics/Analytics.ts deleted file mode 100644 index e2468c964b..0000000000 --- a/packages/backend-core/src/analytics/Analytics.ts +++ /dev/null @@ -1,68 +0,0 @@ -import PosthogClient from "./PosthogClient" -import env from "../environment" -import { getTenantId } from "../context" -import { Account, Hosting, Event, IdentityType } from "@budibase/types" - -class Analytics { - isEnabled: boolean - posthog: PosthogClient | undefined - - constructor() { - // check enabled before init - this.isEnabled = !!env.ENABLE_ANALYTICS // TODO: use db flag instead - if (!this.isEnabled) return - this.posthog = new PosthogClient(env.POSTHOG_TOKEN) - } - - enabled() { - return this.isEnabled - } - - identify(type: IdentityType, id: string, hosting?: Hosting) { - if (!this.isEnabled) return - const tenantId = getTenantId() - if (!hosting) { - hosting = env.SELF_HOSTED ? Hosting.SELF : Hosting.CLOUD - } - const properties = { - type, - hosting, - tenantId, - } - this.posthog!.identify(id, properties) - } - - identifyUser(userId: string) { - this.identify(IdentityType.USER, userId) - } - - identifyTenant() { - let distinctId - if (env.SELF_HOSTED) { - distinctId = getTenantId() // TODO: Get installation ID - } else { - distinctId = getTenantId() - } - this.identify(IdentityType.TENANT, distinctId) - } - - identifyAccount(account: Account) { - const distinctId = account.accountId - const hosting = account.hosting - this.identify(IdentityType.ACCOUNT, distinctId, hosting) - } - - captureEvent(event: Event, properties: any) { - if (!this.isEnabled) return - // TODO: get the user id from context - const userId = "TESTING_USER_ID" - this.posthog!.capture(userId, event, properties) - } - - shutdown() { - if (!this.isEnabled) return - this.posthog!.shutdown() - } -} - -export default Analytics diff --git a/packages/backend-core/src/analytics/index.ts b/packages/backend-core/src/analytics/index.ts deleted file mode 100644 index 9be70a1af4..0000000000 --- a/packages/backend-core/src/analytics/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import Analytics from "./Analytics" - -const analytics = new Analytics() -export default analytics diff --git a/packages/backend-core/src/events/events.ts b/packages/backend-core/src/events/events.ts index 4ae916db16..8234a13b13 100644 --- a/packages/backend-core/src/events/events.ts +++ b/packages/backend-core/src/events/events.ts @@ -1,17 +1,9 @@ -import { getTenantId } from "../context" -import analytics from "../analytics" import { Event } from "@budibase/types" +import { processors } from "./processors" -const logEvent = (messsage: string) => { - const tenantId = getTenantId() - const userId = getTenantId() // TODO - console.log(`[audit] [tenant=${tenantId}] [user=${userId}] ${messsage}`) -} - -export const processEvent = (event: Event, properties: any) => { - // logging - logEvent(event) - - // analytics - analytics.captureEvent(event, properties) +export const publishEvent = (event: Event, properties: any) => { + // in future this should use async events + // via a queue. For now we can use sync as + // this is non-blocking + processors.processEvent(event, properties) } diff --git a/packages/backend-core/src/events/index.ts b/packages/backend-core/src/events/index.ts index 92fa300a33..0fa559661a 100644 --- a/packages/backend-core/src/events/index.ts +++ b/packages/backend-core/src/events/index.ts @@ -1 +1,7 @@ -export * from "./handlers" +export * from "./publishers" + +import { processors } from "./processors" + +export const shutdown = () => { + processors.shutdown() +} diff --git a/packages/backend-core/src/events/processors/LoggingProcessor.ts b/packages/backend-core/src/events/processors/LoggingProcessor.ts new file mode 100644 index 0000000000..8c46f0649a --- /dev/null +++ b/packages/backend-core/src/events/processors/LoggingProcessor.ts @@ -0,0 +1,15 @@ +import { Event } from "@budibase/types" +import { getTenantId } from "../../context" +import { EventProcessor } from "./types" + +export default class LoggingProcessor implements EventProcessor { + processEvent(event: Event, properties: any): void { + const tenantId = getTenantId() + const userId = getTenantId() // TODO + console.log(`[audit] [tenant=${tenantId}] [user=${userId}] ${event}`) + } + + shutdown(): void { + // no-op + } +} diff --git a/packages/backend-core/src/analytics/PosthogClient.ts b/packages/backend-core/src/events/processors/PosthogProcessor.ts similarity index 66% rename from packages/backend-core/src/analytics/PosthogClient.ts rename to packages/backend-core/src/events/processors/PosthogProcessor.ts index f02ba3e7fb..e97ae6680c 100644 --- a/packages/backend-core/src/analytics/PosthogClient.ts +++ b/packages/backend-core/src/events/processors/PosthogProcessor.ts @@ -1,7 +1,9 @@ import PostHog from "posthog-node" import { Event } from "@budibase/types" +import { EventProcessor } from "./types" +import { getTenantId } from "../../context" -class PosthogClient { +export default class PosthogProcessor implements EventProcessor { posthog: PostHog constructor(token: string | undefined) { @@ -11,17 +13,16 @@ class PosthogClient { this.posthog = new PostHog(token) } - identify(distinctId: string, properties: any) { - this.posthog.identify({ distinctId, properties }) + processEvent(event: Event, properties: any): void { + const userId = getTenantId() // TODO + this.posthog.capture({ distinctId: userId, event, properties }) } - capture(userId: string, event: Event, properties: any) { - this.posthog.capture({ distinctId: userId, event, properties }) + identify(distinctId: string, properties: any) { + this.posthog.identify({ distinctId, properties }) } shutdown() { this.posthog.shutdown() } } - -export default PosthogClient diff --git a/packages/backend-core/src/events/processors/Processors.ts b/packages/backend-core/src/events/processors/Processors.ts new file mode 100644 index 0000000000..2b77fa90b2 --- /dev/null +++ b/packages/backend-core/src/events/processors/Processors.ts @@ -0,0 +1,63 @@ +import { Event } from "@budibase/types" +import { EventProcessor } from "./types" +import env from "../../environment" +import LoggingProcessor from "./LoggingProcessor" +import PosthogProcessor from "./PosthogProcessor" + +export default class Processor implements EventProcessor { + processors: EventProcessor[] = [] + + constructor() { + if (env.ENABLE_ANALYTICS && env.POSTHOG_TOKEN) { + this.processors.push(new PosthogProcessor(env.POSTHOG_TOKEN)) + } + this.processors.push(new LoggingProcessor()) + } + + processEvent(event: Event, properties: any): void { + for (const eventProcessor of this.processors) { + eventProcessor.processEvent(event, properties) + } + } + + shutdown() { + for (const eventProcessor of this.processors) { + eventProcessor.shutdown() + } + } + + // Identity todo + + // export const identify(type: IdentityType, id: string, hosting?: Hosting) { + // const tenantId = getTenantId() + // if (!hosting) { + // hosting = env.SELF_HOSTED ? Hosting.SELF : Hosting.CLOUD + // } + // const properties = { + // type, + // hosting, + // tenantId, + // } + // this.posthog!.identify(id, properties) + // } + + // identifyUser(userId: string) { + // this.identify(IdentityType.USER, userId) + // } + + // identifyTenant() { + // let distinctId + // if (env.SELF_HOSTED) { + // distinctId = getTenantId() // TODO: Get installation ID + // } else { + // distinctId = getTenantId() + // } + // this.identify(IdentityType.TENANT, distinctId) + // } + + // identifyAccount(account: Account) { + // const distinctId = account.accountId + // const hosting = account.hosting + // this.identify(IdentityType.ACCOUNT, distinctId, hosting) + // } +} diff --git a/packages/backend-core/src/events/processors/index.ts b/packages/backend-core/src/events/processors/index.ts new file mode 100644 index 0000000000..9269648ff2 --- /dev/null +++ b/packages/backend-core/src/events/processors/index.ts @@ -0,0 +1,2 @@ +import Processors from "./Processors" +export const processors = new Processors() diff --git a/packages/backend-core/src/events/processors/types.ts b/packages/backend-core/src/events/processors/types.ts new file mode 100644 index 0000000000..c5d32f6c75 --- /dev/null +++ b/packages/backend-core/src/events/processors/types.ts @@ -0,0 +1,11 @@ +import { Event } from "@budibase/types" + +export enum EventProcessorType { + POSTHOG = "posthog", + LOGGING = "logging", +} + +export interface EventProcessor { + processEvent(event: Event, properties: any): void + shutdown(): void +} diff --git a/packages/backend-core/src/events/handlers/account.ts b/packages/backend-core/src/events/publishers/account.ts similarity index 57% rename from packages/backend-core/src/events/handlers/account.ts rename to packages/backend-core/src/events/publishers/account.ts index 0d3c9ba0c0..34ca42db02 100644 --- a/packages/backend-core/src/events/handlers/account.ts +++ b/packages/backend-core/src/events/publishers/account.ts @@ -1,17 +1,17 @@ -import { processEvent } from "../events" +import { publishEvent } from "../events" import { Event, Account } from "@budibase/types" export function created(account: Account) { const properties = {} - processEvent(Event.ACCOUNT_CREATED, properties) + publishEvent(Event.ACCOUNT_CREATED, properties) } export function deleted(account: Account) { const properties = {} - processEvent(Event.ACCOUNT_DELETED, properties) + publishEvent(Event.ACCOUNT_DELETED, properties) } export function verified(account: Account) { const properties = {} - processEvent(Event.ACCOUNT_VERIFIED, properties) + publishEvent(Event.ACCOUNT_VERIFIED, properties) } diff --git a/packages/backend-core/src/events/handlers/app.ts b/packages/backend-core/src/events/publishers/app.ts similarity index 59% rename from packages/backend-core/src/events/handlers/app.ts rename to packages/backend-core/src/events/publishers/app.ts index 197c778679..2bce8f81c1 100644 --- a/packages/backend-core/src/events/handlers/app.ts +++ b/packages/backend-core/src/events/publishers/app.ts @@ -1,4 +1,5 @@ -import { processEvent } from "../events" +import { publishEvent } from "../events" +import { getAppDB } from "../../context" import { Event, App, @@ -15,59 +16,66 @@ import { AppExportedEvent, } from "@budibase/types" -export function created(app: App) { +const saveApp = async (app: App): Promise => { + const db = getAppDB() + const response = await db.save(app) + app._rev = response.rev + return app +} + +export const created = async (app: App) => { const properties: AppCreatedEvent = {} - processEvent(Event.APP_CREATED, properties) + await publishEvent(Event.APP_CREATED, properties) } export function updated(app: App) { const properties: AppUpdatedEvent = {} - processEvent(Event.APP_UPDATED, properties) + publishEvent(Event.APP_UPDATED, properties) } export function deleted(app: App) { const properties: AppDeletedEvent = {} - processEvent(Event.APP_DELETED, properties) + publishEvent(Event.APP_DELETED, properties) } export function published(app: App) { const properties: AppPublishedEvent = {} - processEvent(Event.APP_PUBLISHED, properties) + publishEvent(Event.APP_PUBLISHED, properties) } export function unpublished(app: App) { const properties: AppUnpublishedEvent = {} - processEvent(Event.APP_UNPUBLISHED, properties) + publishEvent(Event.APP_UNPUBLISHED, properties) } export function fileImported(app: App) { const properties: AppFileImportedEvent = {} - processEvent(Event.APP_FILE_IMPORTED, properties) + publishEvent(Event.APP_FILE_IMPORTED, properties) } export function templateImported(templateKey: string) { const properties: AppTemplateImportedEvent = { templateKey, } - processEvent(Event.APP_TEMPLATE_IMPORTED, properties) + publishEvent(Event.APP_TEMPLATE_IMPORTED, properties) } export function versionUpdated(app: App) { const properties: AppVersionUpdatedEvent = {} - processEvent(Event.APP_VERSION_UPDATED, properties) + publishEvent(Event.APP_VERSION_UPDATED, properties) } export function versionReverted(app: App) { const properties: AppVersionRevertedEvent = {} - processEvent(Event.APP_VERSION_REVERTED, properties) + publishEvent(Event.APP_VERSION_REVERTED, properties) } export function reverted(app: App) { const properties: AppRevertedEvent = {} - processEvent(Event.APP_REVERTED, properties) + publishEvent(Event.APP_REVERTED, properties) } export function exported(app: App) { const properties: AppExportedEvent = {} - processEvent(Event.APP_EXPORTED, properties) + publishEvent(Event.APP_EXPORTED, properties) } diff --git a/packages/backend-core/src/events/handlers/auth.ts b/packages/backend-core/src/events/publishers/auth.ts similarity index 69% rename from packages/backend-core/src/events/handlers/auth.ts rename to packages/backend-core/src/events/publishers/auth.ts index d925d1a340..61a73fbfc5 100644 --- a/packages/backend-core/src/events/handlers/auth.ts +++ b/packages/backend-core/src/events/publishers/auth.ts @@ -1,4 +1,4 @@ -import { processEvent } from "../events" +import { publishEvent } from "../events" import { Event, LoginEvent, @@ -15,38 +15,38 @@ export function login(source: LoginSource) { const properties: LoginEvent = { source, } - processEvent(Event.AUTH_LOGIN, properties) + publishEvent(Event.AUTH_LOGIN, properties) } export function logout() { const properties: LogoutEvent = {} - processEvent(Event.AUTH_LOGOUT, properties) + publishEvent(Event.AUTH_LOGOUT, properties) } export function SSOCreated(type: SSOType) { const properties: SSOCreatedEvent = { type, } - processEvent(Event.AUTH_SSO_CREATED, properties) + publishEvent(Event.AUTH_SSO_CREATED, properties) } export function SSOUpdated(type: SSOType) { const properties: SSOUpdatedEvent = { type, } - processEvent(Event.AUTH_SSO_UPDATED, properties) + publishEvent(Event.AUTH_SSO_UPDATED, properties) } export function SSOActivated(type: SSOType) { const properties: SSOActivatedEvent = { type, } - processEvent(Event.AUTH_SSO_ACTIVATED, properties) + publishEvent(Event.AUTH_SSO_ACTIVATED, properties) } export function SSODeactivated(type: SSOType) { const properties: SSODeactivatedEvent = { type, } - processEvent(Event.AUTH_SSO_DEACTIVATED, properties) + publishEvent(Event.AUTH_SSO_DEACTIVATED, properties) } diff --git a/packages/backend-core/src/events/handlers/automation.ts b/packages/backend-core/src/events/publishers/automation.ts similarity index 74% rename from packages/backend-core/src/events/handlers/automation.ts rename to packages/backend-core/src/events/publishers/automation.ts index fa9b71a8cf..dfdddc86db 100644 --- a/packages/backend-core/src/events/handlers/automation.ts +++ b/packages/backend-core/src/events/publishers/automation.ts @@ -1,4 +1,4 @@ -import { processEvent } from "../events" +import { publishEvent } from "../events" import { Automation, Event, @@ -14,17 +14,17 @@ import { export function created(automation: Automation) { const properties: AutomationCreatedEvent = {} - processEvent(Event.AUTOMATION_CREATED, properties) + publishEvent(Event.AUTOMATION_CREATED, properties) } export function deleted(automation: Automation) { const properties: AutomationDeletedEvent = {} - processEvent(Event.AUTOMATION_DELETED, properties) + publishEvent(Event.AUTOMATION_DELETED, properties) } export function tested(automation: Automation) { const properties: AutomationTestedEvent = {} - processEvent(Event.AUTOMATION_TESTED, properties) + publishEvent(Event.AUTOMATION_TESTED, properties) } // TODO @@ -35,12 +35,12 @@ export function tested(automation: Automation) { export function stepCreated(automation: Automation, step: AutomationStep) { const properties: AutomationStepCreatedEvent = {} - processEvent(Event.AUTOMATION_STEP_CREATED, properties) + publishEvent(Event.AUTOMATION_STEP_CREATED, properties) } export function stepDeleted(automation: Automation, step: AutomationStep) { const properties: AutomationStepDeletedEvent = {} - processEvent(Event.AUTOMATION_STEP_DELETED, properties) + publishEvent(Event.AUTOMATION_STEP_DELETED, properties) } export function triggerUpdated( @@ -48,5 +48,5 @@ export function triggerUpdated( trigger: AutomationTrigger ) { const properties: AutomationTriggerUpdatedEvent = {} - processEvent(Event.AUTOMATION_TRIGGER_UPDATED, properties) + publishEvent(Event.AUTOMATION_TRIGGER_UPDATED, properties) } diff --git a/packages/backend-core/src/events/handlers/datasource.ts b/packages/backend-core/src/events/publishers/datasource.ts similarity index 68% rename from packages/backend-core/src/events/handlers/datasource.ts rename to packages/backend-core/src/events/publishers/datasource.ts index 9ebe73928e..4ca4b1f9b7 100644 --- a/packages/backend-core/src/events/handlers/datasource.ts +++ b/packages/backend-core/src/events/publishers/datasource.ts @@ -1,4 +1,4 @@ -import { processEvent } from "../events" +import { publishEvent } from "../events" import { Event, Datasource, @@ -9,15 +9,15 @@ import { export function created(datasource: Datasource) { const properties: DatasourceCreatedEvent = {} - processEvent(Event.DATASOURCE_CREATED, properties) + publishEvent(Event.DATASOURCE_CREATED, properties) } export function updated(datasource: Datasource) { const properties: DatasourceUpdatedEvent = {} - processEvent(Event.DATASOURCE_UPDATED, properties) + publishEvent(Event.DATASOURCE_UPDATED, properties) } export function deleted(datasource: Datasource) { const properties: DatasourceDeletedEvent = {} - processEvent(Event.DATASOURCE_DELETED, properties) + publishEvent(Event.DATASOURCE_DELETED, properties) } diff --git a/packages/backend-core/src/events/handlers/email.ts b/packages/backend-core/src/events/publishers/email.ts similarity index 66% rename from packages/backend-core/src/events/handlers/email.ts rename to packages/backend-core/src/events/publishers/email.ts index ff1783b682..72d14120e7 100644 --- a/packages/backend-core/src/events/handlers/email.ts +++ b/packages/backend-core/src/events/publishers/email.ts @@ -1,4 +1,4 @@ -import { processEvent } from "../events" +import { publishEvent } from "../events" import { Event, SMTPConfig, @@ -8,10 +8,10 @@ import { export function SMTPCreated(config: SMTPConfig) { const properties: SMTPCreatedEvent = {} - processEvent(Event.EMAIL_SMTP_CREATED, properties) + publishEvent(Event.EMAIL_SMTP_CREATED, properties) } export function SMTPUpdated(config: SMTPConfig) { const properties: SMTPUpdatedEvent = {} - processEvent(Event.EMAIL_SMTP_UPDATED, properties) + publishEvent(Event.EMAIL_SMTP_UPDATED, properties) } diff --git a/packages/backend-core/src/events/handlers/index.ts b/packages/backend-core/src/events/publishers/index.ts similarity index 100% rename from packages/backend-core/src/events/handlers/index.ts rename to packages/backend-core/src/events/publishers/index.ts diff --git a/packages/backend-core/src/events/handlers/layout.ts b/packages/backend-core/src/events/publishers/layout.ts similarity index 66% rename from packages/backend-core/src/events/handlers/layout.ts rename to packages/backend-core/src/events/publishers/layout.ts index 6d4ff9f4a0..ae0129ab35 100644 --- a/packages/backend-core/src/events/handlers/layout.ts +++ b/packages/backend-core/src/events/publishers/layout.ts @@ -1,4 +1,4 @@ -import { processEvent } from "../events" +import { publishEvent } from "../events" import { Event, Layout, @@ -8,10 +8,10 @@ import { export function created(layout: Layout) { const properties: LayoutCreatedEvent = {} - processEvent(Event.LAYOUT_CREATED, properties) + publishEvent(Event.LAYOUT_CREATED, properties) } export function deleted(layout: Layout) { const properties: LayoutDeletedEvent = {} - processEvent(Event.LAYOUT_DELETED, properties) + publishEvent(Event.LAYOUT_DELETED, properties) } diff --git a/packages/backend-core/src/events/handlers/license.ts b/packages/backend-core/src/events/publishers/license.ts similarity index 71% rename from packages/backend-core/src/events/handlers/license.ts rename to packages/backend-core/src/events/publishers/license.ts index 91b97c00ae..22fc19cd10 100644 --- a/packages/backend-core/src/events/handlers/license.ts +++ b/packages/backend-core/src/events/publishers/license.ts @@ -1,4 +1,4 @@ -import { processEvent } from "../events" +import { publishEvent } from "../events" import { Event, License, @@ -12,25 +12,25 @@ import { // TODO export function updgraded(license: License) { const properties: LicenseUpgradedEvent = {} - processEvent(Event.LICENSE_UPGRADED, properties) + publishEvent(Event.LICENSE_UPGRADED, properties) } // TODO export function downgraded(license: License) { const properties: LicenseDowngradedEvent = {} - processEvent(Event.LICENSE_DOWNGRADED, properties) + publishEvent(Event.LICENSE_DOWNGRADED, properties) } // TODO export function updated(license: License) { const properties: LicenseUpdatedEvent = {} - processEvent(Event.LICENSE_UPDATED, properties) + publishEvent(Event.LICENSE_UPDATED, properties) } // TODO export function activated(license: License) { const properties: LicenseActivatedEvent = {} - processEvent(Event.LICENSE_ACTIVATED, properties) + publishEvent(Event.LICENSE_ACTIVATED, properties) } // TODO @@ -39,5 +39,5 @@ export function quotaExceeded(quotaName: string, value: number) { name: quotaName, value, } - processEvent(Event.LICENSE_QUOTA_EXCEEDED, properties) + publishEvent(Event.LICENSE_QUOTA_EXCEEDED, properties) } diff --git a/packages/backend-core/src/events/handlers/org.ts b/packages/backend-core/src/events/publishers/org.ts similarity index 58% rename from packages/backend-core/src/events/handlers/org.ts rename to packages/backend-core/src/events/publishers/org.ts index 5e1fc12b80..c1e5948f8a 100644 --- a/packages/backend-core/src/events/handlers/org.ts +++ b/packages/backend-core/src/events/publishers/org.ts @@ -1,30 +1,30 @@ -import { processEvent } from "../events" +import { publishEvent } from "../events" import { Event, VersionCheckedEvent } from "@budibase/types" export function nameUpdated() { const properties = {} - processEvent(Event.ORG_NAME_UPDATED, properties) + publishEvent(Event.ORG_NAME_UPDATED, properties) } export function logoUpdated() { const properties = {} - processEvent(Event.ORG_LOGO_UPDATED, properties) + publishEvent(Event.ORG_LOGO_UPDATED, properties) } export function platformURLUpdated() { const properties = {} - processEvent(Event.ORG_PLATFORM_URL_UPDATED, properties) + publishEvent(Event.ORG_PLATFORM_URL_UPDATED, properties) } export function versionChecked(version: number) { const properties: VersionCheckedEvent = { version, } - processEvent(Event.UPDATE_VERSION_CHECKED, properties) + publishEvent(Event.UPDATE_VERSION_CHECKED, properties) } // TODO export function analyticsOptOut() { const properties = {} - processEvent(Event.ANALYTICS_OPT_OUT, properties) + publishEvent(Event.ANALYTICS_OPT_OUT, properties) } diff --git a/packages/backend-core/src/events/handlers/query.ts b/packages/backend-core/src/events/publishers/query.ts similarity index 76% rename from packages/backend-core/src/events/handlers/query.ts rename to packages/backend-core/src/events/publishers/query.ts index 059381ba31..94e83c2fa7 100644 --- a/packages/backend-core/src/events/handlers/query.ts +++ b/packages/backend-core/src/events/publishers/query.ts @@ -1,4 +1,4 @@ -import { processEvent } from "../events" +import { publishEvent } from "../events" import { Event, Datasource, @@ -14,17 +14,17 @@ import { export const created = (datasource: Datasource, query: Query) => { const properties: QueryCreatedEvent = {} - processEvent(Event.QUERY_CREATED, properties) + publishEvent(Event.QUERY_CREATED, properties) } export const updated = (datasource: Datasource, query: Query) => { const properties: QueryUpdatedEvent = {} - processEvent(Event.QUERY_UPDATED, properties) + publishEvent(Event.QUERY_UPDATED, properties) } export const deleted = (datasource: Datasource, query: Query) => { const properties: QueryDeletedEvent = {} - processEvent(Event.QUERY_DELETED, properties) + publishEvent(Event.QUERY_DELETED, properties) } export const imported = ( @@ -33,7 +33,7 @@ export const imported = ( count: any ) => { const properties: QueryImportedEvent = {} - processEvent(Event.QUERY_IMPORT, properties) + publishEvent(Event.QUERY_IMPORT, properties) } // TODO @@ -44,5 +44,5 @@ export const imported = ( export const previewed = (datasource: Datasource) => { const properties: QueryPreviewedEvent = {} - processEvent(Event.QUERY_PREVIEWED, properties) + publishEvent(Event.QUERY_PREVIEWED, properties) } diff --git a/packages/backend-core/src/events/handlers/role.ts b/packages/backend-core/src/events/publishers/role.ts similarity index 69% rename from packages/backend-core/src/events/handlers/role.ts rename to packages/backend-core/src/events/publishers/role.ts index 58abd4bdca..a7f7e60a32 100644 --- a/packages/backend-core/src/events/handlers/role.ts +++ b/packages/backend-core/src/events/publishers/role.ts @@ -1,4 +1,4 @@ -import { processEvent } from "../events" +import { publishEvent } from "../events" import { Event, Role, @@ -14,25 +14,25 @@ import { export function created(role: Role) { const properties: RoleCreatedEvent = {} - processEvent(Event.ROLE_CREATED, properties) + publishEvent(Event.ROLE_CREATED, properties) } export function updated(role: Role) { const properties: RoleUpdatedEvent = {} - processEvent(Event.ROLE_UPDATED, properties) + publishEvent(Event.ROLE_UPDATED, properties) } export function deleted(role: Role) { const properties: RoleDeletedEvent = {} - processEvent(Event.ROLE_DELETED, properties) + publishEvent(Event.ROLE_DELETED, properties) } export function assigned(user: User, role: Role) { const properties: RoleAssignedEvent = {} - processEvent(Event.ROLE_ASSIGNED, properties) + publishEvent(Event.ROLE_ASSIGNED, properties) } export function unassigned(user: User, role: Role) { const properties: RoleUnassignedEvent = {} - processEvent(Event.ROLE_UNASSIGNED, properties) + publishEvent(Event.ROLE_UNASSIGNED, properties) } diff --git a/packages/backend-core/src/events/handlers/row.ts b/packages/backend-core/src/events/publishers/row.ts similarity index 81% rename from packages/backend-core/src/events/handlers/row.ts rename to packages/backend-core/src/events/publishers/row.ts index 0f6cf3a00c..e233c78a2b 100644 --- a/packages/backend-core/src/events/handlers/row.ts +++ b/packages/backend-core/src/events/publishers/row.ts @@ -1,4 +1,4 @@ -import { processEvent } from "../events" +import { publishEvent } from "../events" import { Event, RowImportedEvent, @@ -19,5 +19,5 @@ export const imported = ( count: number ) => { const properties: RowImportedEvent = {} - processEvent(Event.ROW_IMPORT, properties) + publishEvent(Event.ROW_IMPORT, properties) } diff --git a/packages/backend-core/src/events/handlers/screen.ts b/packages/backend-core/src/events/publishers/screen.ts similarity index 66% rename from packages/backend-core/src/events/handlers/screen.ts rename to packages/backend-core/src/events/publishers/screen.ts index d152c43a9c..bf5ffef811 100644 --- a/packages/backend-core/src/events/handlers/screen.ts +++ b/packages/backend-core/src/events/publishers/screen.ts @@ -1,4 +1,4 @@ -import { processEvent } from "../events" +import { publishEvent } from "../events" import { Event, Screen, @@ -8,10 +8,10 @@ import { export function created(screen: Screen) { const properties: ScreenCreatedEvent = {} - processEvent(Event.SCREEN_CREATED, properties) + publishEvent(Event.SCREEN_CREATED, properties) } export function deleted(screen: Screen) { const properties: ScreenDeletedEvent = {} - processEvent(Event.SCREEN_DELETED, properties) + publishEvent(Event.SCREEN_DELETED, properties) } diff --git a/packages/backend-core/src/events/handlers/serve.ts b/packages/backend-core/src/events/publishers/serve.ts similarity index 68% rename from packages/backend-core/src/events/handlers/serve.ts rename to packages/backend-core/src/events/publishers/serve.ts index c1cb513d0b..3a61751a8b 100644 --- a/packages/backend-core/src/events/handlers/serve.ts +++ b/packages/backend-core/src/events/publishers/serve.ts @@ -1,4 +1,4 @@ -import { processEvent } from "../events" +import { publishEvent } from "../events" import { App, BuilderServedEvent, @@ -11,15 +11,15 @@ import { export function servedBuilder(version: number) { const properties: BuilderServedEvent = {} - processEvent(Event.SERVED_BUILDER, properties) + publishEvent(Event.SERVED_BUILDER, properties) } export function servedApp(app: App) { const properties: AppServedEvent = {} - processEvent(Event.SERVED_APP, properties) + publishEvent(Event.SERVED_APP, properties) } export function servedAppPreview(app: App) { const properties: AppPreviewServedEvent = {} - processEvent(Event.SERVED_APP_PREVIEW, properties) + publishEvent(Event.SERVED_APP_PREVIEW, properties) } diff --git a/packages/backend-core/src/events/handlers/table.ts b/packages/backend-core/src/events/publishers/table.ts similarity index 69% rename from packages/backend-core/src/events/handlers/table.ts rename to packages/backend-core/src/events/publishers/table.ts index ca0109a8ea..fca966873c 100644 --- a/packages/backend-core/src/events/handlers/table.ts +++ b/packages/backend-core/src/events/publishers/table.ts @@ -1,4 +1,4 @@ -import { processEvent } from "../events" +import { publishEvent } from "../events" import { Event, TableExportFormat, @@ -15,31 +15,31 @@ import { export function created(table: Table) { const properties: TableCreatedEvent = {} - processEvent(Event.TABLE_CREATED, properties) + publishEvent(Event.TABLE_CREATED, properties) } export function updated(table: Table) { const properties: TableUpdatedEvent = {} - processEvent(Event.TABLE_UPDATED, properties) + publishEvent(Event.TABLE_UPDATED, properties) } export function deleted(table: Table) { const properties: TableDeletedEvent = {} - processEvent(Event.TABLE_DELETED, properties) + publishEvent(Event.TABLE_DELETED, properties) } export function exported(table: Table, format: TableExportFormat) { const properties: TableExportedEvent = {} - processEvent(Event.TABLE_EXPORTED, properties) + publishEvent(Event.TABLE_EXPORTED, properties) } export function imported(table: Table, format: TableImportFormat) { const properties: TableImportedEvent = {} - processEvent(Event.TABLE_IMPORTED, properties) + publishEvent(Event.TABLE_IMPORTED, properties) } // TODO export function permissionUpdated() { const properties = {} - processEvent(Event.TABLE_PERMISSION_UPDATED, properties) + publishEvent(Event.TABLE_PERMISSION_UPDATED, properties) } diff --git a/packages/backend-core/src/events/handlers/user.ts b/packages/backend-core/src/events/publishers/user.ts similarity index 68% rename from packages/backend-core/src/events/handlers/user.ts rename to packages/backend-core/src/events/publishers/user.ts index 48ae7b698a..a5da10d3a7 100644 --- a/packages/backend-core/src/events/handlers/user.ts +++ b/packages/backend-core/src/events/publishers/user.ts @@ -1,4 +1,4 @@ -import { processEvent } from "../events" +import { publishEvent } from "../events" import { Event, User, @@ -19,71 +19,71 @@ import { export function created(user: User) { const properties: UserCreatedEvent = {} - processEvent(Event.USER_CREATED, properties) + publishEvent(Event.USER_CREATED, properties) } export function updated(user: User) { const properties: UserUpdatedEvent = {} - processEvent(Event.USER_UPDATED, properties) + publishEvent(Event.USER_UPDATED, properties) } export function deleted(user: User) { const properties: UserDeletedEvent = {} - processEvent(Event.USER_DELETED, properties) + publishEvent(Event.USER_DELETED, properties) } // PERMISSIONS export function permissionAdminAssigned(user: User) { const properties: UserPermissionAssignedEvent = {} - processEvent(Event.USER_PERMISSION_ADMIN_ASSIGNED, properties) + publishEvent(Event.USER_PERMISSION_ADMIN_ASSIGNED, properties) } export function permissionAdminRemoved(user: User) { const properties: UserPermissionRemovedEvent = {} - processEvent(Event.USER_PERMISSION_ADMIN_REMOVED, properties) + publishEvent(Event.USER_PERMISSION_ADMIN_REMOVED, properties) } export function permissionBuilderAssigned(user: User) { const properties: UserPermissionAssignedEvent = {} - processEvent(Event.USER_PERMISSION_BUILDER_ASSIGNED, properties) + publishEvent(Event.USER_PERMISSION_BUILDER_ASSIGNED, properties) } export function permissionBuilderRemoved(user: User) { const properties: UserPermissionRemovedEvent = {} - processEvent(Event.USER_PERMISSION_BUILDER_REMOVED, properties) + publishEvent(Event.USER_PERMISSION_BUILDER_REMOVED, properties) } // INVITE export function invited(userInfo: any) { const properties: UserInvitedEvent = {} - processEvent(Event.USER_INVITED, properties) + publishEvent(Event.USER_INVITED, properties) } export function inviteAccepted(user: User) { const properties: UserInviteAcceptedEvent = {} - processEvent(Event.USER_INVITED_ACCEPTED, properties) + publishEvent(Event.USER_INVITED_ACCEPTED, properties) } // PASSWORD export function passwordForceReset(user: User) { const properties: UserPasswordForceResetEvent = {} - processEvent(Event.USER_PASSWORD_FORCE_RESET, properties) + publishEvent(Event.USER_PASSWORD_FORCE_RESET, properties) } export function passwordUpdated(user: User) { const properties: UserPasswordUpdatedEvent = {} - processEvent(Event.USER_PASSWORD_UPDATED, properties) + publishEvent(Event.USER_PASSWORD_UPDATED, properties) } export function passwordResetRequested(user: User) { const properties: UserPasswordResetRequestedEvent = {} - processEvent(Event.USER_PASSWORD_RESET_REQUESTED, properties) + publishEvent(Event.USER_PASSWORD_RESET_REQUESTED, properties) } export function passwordReset(user: User) { const properties: UserPasswordResetEvent = {} - processEvent(Event.USER_PASSWORD_RESET, properties) + publishEvent(Event.USER_PASSWORD_RESET, properties) } diff --git a/packages/backend-core/src/events/handlers/view.ts b/packages/backend-core/src/events/publishers/view.ts similarity index 68% rename from packages/backend-core/src/events/handlers/view.ts rename to packages/backend-core/src/events/publishers/view.ts index 9fd3370f57..4091fce18e 100644 --- a/packages/backend-core/src/events/handlers/view.ts +++ b/packages/backend-core/src/events/publishers/view.ts @@ -1,4 +1,4 @@ -import { processEvent } from "../events" +import { publishEvent } from "../events" import { Event, ViewCalculationCreatedEvent, @@ -20,50 +20,50 @@ import { export function created(view: View) { const properties: ViewCreatedEvent = {} - processEvent(Event.VIEW_CREATED, properties) + publishEvent(Event.VIEW_CREATED, properties) } export function updated(view: View) { const properties: ViewUpdatedEvent = {} - processEvent(Event.VIEW_UPDATED, properties) + publishEvent(Event.VIEW_UPDATED, properties) } export function deleted() { const properties: ViewDeletedEvent = {} - processEvent(Event.VIEW_DELETED, properties) + publishEvent(Event.VIEW_DELETED, properties) } export function exported(table: Table, format: TableExportFormat) { const properties: ViewExportedEvent = {} - processEvent(Event.VIEW_EXPORTED, properties) + publishEvent(Event.VIEW_EXPORTED, properties) } export function filterCreated() { const properties: ViewFilterCreatedEvent = {} - processEvent(Event.VIEW_FILTER_CREATED, properties) + publishEvent(Event.VIEW_FILTER_CREATED, properties) } export function filterUpdated() { const properties: ViewFilterUpdatedEvent = {} - processEvent(Event.VIEW_FILTER_UPDATED, properties) + publishEvent(Event.VIEW_FILTER_UPDATED, properties) } export function filterDeleted() { const properties: ViewFilterDeletedEvent = {} - processEvent(Event.VIEW_FILTER_DELETED, properties) + publishEvent(Event.VIEW_FILTER_DELETED, properties) } export function calculationCreated() { const properties: ViewCalculationCreatedEvent = {} - processEvent(Event.VIEW_CALCULATION_CREATED, properties) + publishEvent(Event.VIEW_CALCULATION_CREATED, properties) } export function calculationUpdated() { const properties: ViewCalculationUpdatedEvent = {} - processEvent(Event.VIEW_CALCULATION_UPDATED, properties) + publishEvent(Event.VIEW_CALCULATION_UPDATED, properties) } export function calculationDeleted() { const properties: ViewCalculationDeletedEvent = {} - processEvent(Event.VIEW_CALCULATION_DELETED, properties) + publishEvent(Event.VIEW_CALCULATION_DELETED, properties) } diff --git a/packages/backend-core/src/index.ts b/packages/backend-core/src/index.ts index 3638c8fd0d..c2a6eeea2e 100644 --- a/packages/backend-core/src/index.ts +++ b/packages/backend-core/src/index.ts @@ -7,7 +7,6 @@ import env from "./environment" import accounts from "./cloud/accounts" import tenancy from "./tenancy" import featureFlags from "./featureFlags" -import analytics from "./analytics" import sessions from "./security/sessions" import deprovisioning from "./context/deprovision" @@ -43,7 +42,6 @@ export = { tenancy, featureFlags, events, - analytics, sessions, deprovisioning, } diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index d79ccbc46a..a9679f959a 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -258,7 +258,7 @@ const performAppCreate = async (ctx: any) => { } catch (err) { // nothing to do } - const newApplication = { + const newApplication: App = { _id: DocumentTypes.APP_METADATA, _rev, appId: instance._id, diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts index cdd087d68d..bc9910b370 100644 --- a/packages/server/src/app.ts +++ b/packages/server/src/app.ts @@ -16,7 +16,7 @@ const fileSystem = require("./utilities/fileSystem") const bullboard = require("./automations/bullboard") import redis from "./utilities/redis" import * as migrations from "./migrations" -import { analytics } from "@budibase/backend-core" +import { events } from "@budibase/backend-core" const app = new Koa() @@ -74,7 +74,7 @@ server.on("close", async () => { console.log("Server Closed") } await redis.shutdown() - await analytics.shutdown() + await events.shutdown() }) module.exports = server.listen(env.PORT || 0, async () => { diff --git a/packages/server/src/migrations/functions/backfill/app/app.ts b/packages/server/src/migrations/functions/backfill/app/app.ts index ab195636cc..0099f04290 100644 --- a/packages/server/src/migrations/functions/backfill/app/app.ts +++ b/packages/server/src/migrations/functions/backfill/app/app.ts @@ -3,9 +3,11 @@ import { App } from "@budibase/types" export const backfill = async (appDb: any) => { const app: App = await appDb.get(db.DocumentTypes.APP_METADATA) + if (db.isDevAppID(app.appId)) { events.app.created(app) } + if (db.isProdAppID(app.appId)) { events.app.published(app) } diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index ca0a58bcf9..828972a5da 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -1021,10 +1021,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@1.0.138": - version "1.0.138" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.138.tgz#5297d6cf5b9ec8c15f0a6df4c7d8273b8ac900f0" - integrity sha512-1qN/5urKX8bBXwEz266Z94rco8dTI7VqIh75m8ZcqrAfoUpjvZJS76gZxfc5U/QWPwrgVFnLtYvnEjaLbGEflg== +"@budibase/backend-core@1.0.142": + version "1.0.142" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.142.tgz#cbfa426dbf064dff0ea5f9840baae595c0f5343c" + integrity sha512-gd52tOm4mytHa3OQQVcEiwnVCvq0pC0q3YN+5D1uoGt9WaAZoshB5AhXnUPZohvpo5j8isO07qXvF+5YffPVaw== dependencies: "@techpass/passport-openidconnect" "^0.3.0" aws-sdk "^2.901.0" @@ -1098,12 +1098,12 @@ svelte-flatpickr "^3.2.3" svelte-portal "^1.0.0" -"@budibase/pro@1.0.138": - version "1.0.138" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.138.tgz#cacbebe5ce93eb533af62a794a638944c2c61544" - integrity sha512-4ABlUZvl2h8sd8awJATf3KJeoFWV/8SoqdbKiH1ICdUcM/6dad7nhbJ15QqJL+Uuh/+mN2yEbr8V6Un2+yF+CA== +"@budibase/pro@1.0.142": + version "1.0.142" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.142.tgz#17cd53d67ce80e0cce2179caee0c03e6a0d7084e" + integrity sha512-c1SaRoVik/AVn4zNNsDciFeIr+P3J6Sc8hsE5M3pb0Zj/NzSEHXTesxcBVLlD2PYlwnvWPSoMvdDJKrEFHvOYQ== dependencies: - "@budibase/backend-core" "1.0.138" + "@budibase/backend-core" "1.0.142" node-fetch "^2.6.1" "@budibase/standard-components@^0.9.139": @@ -1124,18 +1124,6 @@ svelte-apexcharts "^1.0.2" svelte-flatpickr "^3.1.0" -"@budibase/string-templates@^1.0.130-alpha.1", "@budibase/string-templates@^1.0.134": - version "1.0.134" - resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-1.0.134.tgz#2e21c533e310b55c1f58b8aceb82367d38ad8846" - integrity sha512-W++/k1zAIl/yLX+8KMz3Fo8hi5n6PNK7x/kxAXClWanhdMozLSrS7AIFbrYDhHlsjpteK+3R6wDf5K/JfpYj6g== - dependencies: - "@budibase/handlebars-helpers" "^0.11.8" - dayjs "^1.10.4" - handlebars "^4.7.6" - handlebars-utils "^1.0.6" - lodash "^4.17.20" - vm2 "^3.9.4" - "@bull-board/api@3.9.4": version "3.9.4" resolved "https://registry.yarnpkg.com/@bull-board/api/-/api-3.9.4.tgz#984f25e6d5501d97152d81184968ff135757b57a" @@ -2019,24 +2007,6 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= -"@rollup/plugin-inject@^4.0.0": - version "4.0.4" - resolved "https://registry.yarnpkg.com/@rollup/plugin-inject/-/plugin-inject-4.0.4.tgz#fbeee66e9a700782c4f65c8b0edbafe58678fbc2" - integrity sha512-4pbcU4J/nS+zuHk+c+OL3WtmEQhqxlZ9uqfjQMQDOHOPld7PsCd8k5LWs8h5wjwJN7MgnAn768F2sDxEP4eNFQ== - dependencies: - "@rollup/pluginutils" "^3.1.0" - estree-walker "^2.0.1" - magic-string "^0.25.7" - -"@rollup/pluginutils@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" - integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== - dependencies: - "@types/estree" "0.0.39" - estree-walker "^1.0.1" - picomatch "^2.2.2" - "@sendgrid/client@^7.1.1": version "7.6.1" resolved "https://registry.yarnpkg.com/@sendgrid/client/-/client-7.6.1.tgz#de17fe9f04af3bdb69aca44fc407316de87cea3b" @@ -2224,11 +2194,6 @@ resolved "https://registry.yarnpkg.com/@spectrum-css/illustratedmessage/-/illustratedmessage-3.0.8.tgz#69ef0c935bcc5027f233a78de5aeb0064bf033cb" integrity sha512-HvC4dywDi11GdrXQDCvKQ0vFlrXLTyJuc9UKf7meQLCGoJbGYDBwe+tHXNK1c6gPMD9BoL6pPMP1K/vRzR4EBQ== -"@spectrum-css/inlinealert@^2.0.1": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@spectrum-css/inlinealert/-/inlinealert-2.0.6.tgz#4c5e923a1f56a96cc1adb30ef1f06ae04f2c6376" - integrity sha512-OpvvoWP02wWyCnF4IgG8SOPkXymovkC9cGtgMS1FdDubnG3tJZB/JeKTsRR9C9Vt3WBaOmISRdSKlZ4lC9CFzA== - "@spectrum-css/inputgroup@^3.0.2": version "3.0.8" resolved "https://registry.yarnpkg.com/@spectrum-css/inputgroup/-/inputgroup-3.0.8.tgz#fc23afc8a73c24d17249c9d2337e8b42085b298b" @@ -2326,11 +2291,6 @@ resolved "https://registry.yarnpkg.com/@spectrum-css/tabs/-/tabs-3.1.5.tgz#cc82e69c1fc721902345178231fb95d05938b983" integrity sha512-UtfW8bA1quYnJM6v/lp6AVYGnQFkiUix2FHAf/4VHVrk4mh7ydtLiXS0IR3Kx+t/S8FWdSdSQHDZ8tHbY1ZLZg== -"@spectrum-css/tag@^3.1.4": - version "3.3.11" - resolved "https://registry.yarnpkg.com/@spectrum-css/tag/-/tag-3.3.11.tgz#66b5f91a845df2ad232fae702ae53b3fa46cf745" - integrity sha512-dyDUwG4fbsScMLaVOKQgKuUvYshGEIjTS9lVNhOHCz7klZ800UIMoCzDQXieHf+0nSdiR1Wy1oHBObHMMB8sxA== - "@spectrum-css/tags@^3.0.2": version "3.0.3" resolved "https://registry.yarnpkg.com/@spectrum-css/tags/-/tags-3.0.3.tgz#fc76d2735cdc442de91b7eb3bee49a928c0767ac" @@ -2490,13 +2450,6 @@ resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8" integrity sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w== -"@types/codemirror@^5.60.4": - version "5.60.5" - resolved "https://registry.yarnpkg.com/@types/codemirror/-/codemirror-5.60.5.tgz#5b989a3b4bbe657458cf372c92b6bfda6061a2b7" - integrity sha512-TiECZmm8St5YxjFUp64LK0c8WU5bxMDt9YaAek1UqUb9swrSCoJhh92fWu1p3mTEqlHjhB5sY7OFBhWroJXZVg== - dependencies: - "@types/tern" "*" - "@types/connect@*": version "3.4.35" resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" @@ -2545,11 +2498,6 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== -"@types/estree@0.0.39": - version "0.0.39" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" - integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== - "@types/estree@^0.0.51": version "0.0.51" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" @@ -2705,11 +2653,6 @@ resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== -"@types/marked@^4.0.1": - version "4.0.3" - resolved "https://registry.yarnpkg.com/@types/marked/-/marked-4.0.3.tgz#2098f4a77adaba9ce881c9e0b6baf29116e5acc4" - integrity sha512-HnMWQkLJEf/PnxZIfbm0yGJRRZYYMhb++O9M36UCTA9z53uPvVoSlAwJr3XOpDEryb7Hwl1qAx/MV6YIW1RXxg== - "@types/mime@^1": version "1.3.2" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" @@ -2821,13 +2764,6 @@ "@types/cookiejar" "*" "@types/node" "*" -"@types/tern@*": - version "0.23.4" - resolved "https://registry.yarnpkg.com/@types/tern/-/tern-0.23.4.tgz#03926eb13dbeaf3ae0d390caf706b2643a0127fb" - integrity sha512-JAUw1iXGO1qaWwEOzxTKJZ/5JxVeON9kvGZ/osgZaJImBnyjyn0cjovPsf6FNLmyGY8Vw9DoXZCMlfMkMwHRWg== - dependencies: - "@types/estree" "*" - "@types/tough-cookie@*": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.1.tgz#8f80dd965ad81f3e1bc26d6f5c727e132721ff40" @@ -3392,7 +3328,7 @@ arg@^4.1.0: resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== -argparse@^1.0.10, argparse@^1.0.7: +argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== @@ -3439,15 +3375,6 @@ array-equal@^1.0.0: resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= -array-sort@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-sort/-/array-sort-1.0.0.tgz#e4c05356453f56f53512a7d1d6123f2c54c0a88a" - integrity sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg== - dependencies: - default-compare "^1.0.0" - get-value "^2.0.6" - kind-of "^5.0.2" - array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" @@ -3551,13 +3478,6 @@ atomic-sleep@^1.0.0: resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== -autolinker@~0.28.0: - version "0.28.1" - resolved "https://registry.yarnpkg.com/autolinker/-/autolinker-0.28.1.tgz#0652b491881879f0775dace0cdca3233942a4e47" - integrity sha1-BlK0kYgYefB3XazgzcoyM5QqTkc= - dependencies: - gulp-header "^1.7.1" - aws-sdk@^2.767.0: version "2.1030.0" resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1030.0.tgz#24a856af3d2b8b37c14a8f59974993661c66fd82" @@ -4338,18 +4258,6 @@ co@^4.6.0: resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= -codemirror-spell-checker@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/codemirror-spell-checker/-/codemirror-spell-checker-1.1.2.tgz#1c660f9089483ccb5113b9ba9ca19c3f4993371e" - integrity sha1-HGYPkIlIPMtRE7m6nKGcP0mTNx4= - dependencies: - typo-js "*" - -codemirror@^5.63.1: - version "5.65.3" - resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.65.3.tgz#2d029930d5a293bc5fb96ceea64654803c0d4ac7" - integrity sha512-kCC0iwGZOVZXHEKW3NDTObvM7pTIyowjty4BUqeREROc/3I6bWbgZDA3fGDwlA+rbgRjvnRnfqs9SfXynel1AQ== - collect-v8-coverage@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" @@ -4490,13 +4398,6 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-with-sourcemaps@*: - version "1.1.0" - resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz#d4ea93f05ae25790951b99e7b3b09e3908a4082e" - integrity sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg== - dependencies: - source-map "^0.6.1" - condense-newlines@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/condense-newlines/-/condense-newlines-0.2.1.tgz#3de985553139475d32502c83b02f60684d24c55f" @@ -4898,13 +4799,6 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== -default-compare@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/default-compare/-/default-compare-1.0.0.tgz#cb61131844ad84d84788fb68fd01681ca7781a2f" - integrity sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ== - dependencies: - kind-of "^5.0.2" - default-shell@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/default-shell/-/default-shell-1.0.1.tgz#752304bddc6174f49eb29cb988feea0b8813c8bc" @@ -5066,25 +4960,11 @@ doctrine@3.0.0, doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-serializer@^1.0.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" - integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.2.0" - entities "^2.0.0" - dom-walk@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== -domelementtype@^2.0.1, domelementtype@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" - integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== - domexception@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" @@ -5099,22 +4979,6 @@ domexception@^2.0.1: dependencies: webidl-conversions "^5.0.0" -domhandler@^4.0.0, domhandler@^4.2.0: - version "4.3.1" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" - integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== - dependencies: - domelementtype "^2.2.0" - -domutils@^2.5.2: - version "2.8.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" - integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== - dependencies: - dom-serializer "^1.0.1" - domelementtype "^2.2.0" - domhandler "^4.2.0" - dot-prop@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" @@ -5154,11 +5018,6 @@ download@8.0.0: p-event "^2.1.0" pify "^4.0.1" -downloadjs@1.4.7: - version "1.4.7" - resolved "https://registry.yarnpkg.com/downloadjs/-/downloadjs-1.4.7.tgz#f69f96f940e0d0553dac291139865a3cd0101e3c" - integrity sha1-9p+W+UDg0FU9rCkROYZaPNAQHjw= - duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" @@ -5174,17 +5033,6 @@ duplexify@^4.0.0: readable-stream "^3.1.1" stream-shift "^1.0.0" -easymde@^2.16.1: - version "2.16.1" - resolved "https://registry.yarnpkg.com/easymde/-/easymde-2.16.1.tgz#f4c2380312615cb33826f1a1fecfaa4022ff551a" - integrity sha512-FihYgjRsKfhGNk89SHSqxKLC4aJ1kfybPWW6iAmtb5GnXu+tnFPSzSaGBmk1RRlCuhFSjhF0SnIMGVPjEzkr6g== - dependencies: - "@types/codemirror" "^5.60.4" - "@types/marked" "^4.0.1" - codemirror "^5.63.1" - codemirror-spell-checker "1.1.2" - marked "^4.0.10" - ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -5296,16 +5144,6 @@ enhanced-resolve@^5.9.0: graceful-fs "^4.2.4" tapable "^2.2.0" -ent@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" - integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0= - -entities@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" - integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== - entities@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" @@ -5725,16 +5563,6 @@ estraverse@^5.1.0, estraverse@^5.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== -estree-walker@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" - integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== - -estree-walker@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" - integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== - esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -6282,11 +6110,6 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-exists-sync@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" - integrity sha1-mC1ok6+RjnLQjeyehnP/K1qNat0= - fs-extra@8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -6393,14 +6216,6 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: has "^1.0.3" has-symbols "^1.0.1" -get-object@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/get-object/-/get-object-0.2.0.tgz#d92ff7d5190c64530cda0543dac63a3d47fe8c0c" - integrity sha1-2S/31RkMZFMM2gVD2sY6PUf+jAw= - dependencies: - is-number "^2.0.2" - isobject "^0.2.0" - get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -6463,13 +6278,6 @@ get-value@^2.0.3, get-value@^2.0.6: resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= -get-value@^3.0.0, get-value@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/get-value/-/get-value-3.0.1.tgz#5efd2a157f1d6a516d7524e124ac52d0a39ef5a8" - integrity sha512-mKZj9JLQrwMBtj5wxi6MH8Z5eSKaERpAwjg43dPtlGI1ZVEgH/qC7T8/6R2OBSUA+zzHBZgICsVJaEIV2tKTDA== - dependencies: - isobject "^3.0.1" - getopts@2.2.5: version "2.2.5" resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.2.5.tgz#67a0fe471cacb9c687d817cab6450b96dde8313b" @@ -6775,24 +6583,7 @@ gtoken@^5.0.4: google-p12-pem "^3.1.3" jws "^4.0.0" -gulp-header@^1.7.1: - version "1.8.12" - resolved "https://registry.yarnpkg.com/gulp-header/-/gulp-header-1.8.12.tgz#ad306be0066599127281c4f8786660e705080a84" - integrity sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ== - dependencies: - concat-with-sourcemaps "*" - lodash.template "^4.4.0" - through2 "^2.0.0" - -handlebars-utils@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/handlebars-utils/-/handlebars-utils-1.0.6.tgz#cb9db43362479054782d86ffe10f47abc76357f9" - integrity sha512-d5mmoQXdeEqSKMtQQZ9WkiUcO1E3tPbWxluCK9hVgIDPzQa9WsKo3Lbe/sGflTe7TomHEeZaOgwIkyIr1kfzkw== - dependencies: - kind-of "^6.0.0" - typeof-article "^0.1.1" - -handlebars@^4.7.6, handlebars@^4.7.7: +handlebars@^4.7.7: version "4.7.7" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== @@ -6874,14 +6665,6 @@ has-value@^1.0.0: has-values "^1.0.0" isobject "^3.0.0" -has-value@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-2.0.2.tgz#d0f12e8780ba8e90e66ad1a21c707fdb67c25658" - integrity sha512-ybKOlcRsK2MqrM3Hmz/lQxXHZ6ejzSPzpNabKB45jb5qDgJvKPa3SdapTsTLwEb9WltgWpOmNax7i+DzNOk4TA== - dependencies: - get-value "^3.0.0" - has-values "^2.0.1" - has-values@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" @@ -6895,13 +6678,6 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" -has-values@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-2.0.1.tgz#3876200ff86d8a8546a9264a952c17d5fc17579d" - integrity sha512-+QdH3jOmq9P8GfdjFg0eJudqx1FqU62NQJ4P16rOEHeRdl7ckgwn6uqQjzYE0ZoHVV/e5E2esuJ5Gl5+HUW19w== - dependencies: - kind-of "^6.0.2" - has-yarn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" @@ -6914,16 +6690,6 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" -helper-md@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/helper-md/-/helper-md-0.2.2.tgz#c1f59d7e55bbae23362fd8a0e971607aec69d41f" - integrity sha1-wfWdflW7riM2L9ig6XFgeuxp1B8= - dependencies: - ent "^2.2.0" - extend-shallow "^2.0.1" - fs-exists-sync "^0.1.0" - remarkable "^1.6.2" - hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" @@ -6953,24 +6719,6 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -html-tag@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/html-tag/-/html-tag-2.0.0.tgz#36c3bc8d816fd30b570d5764a497a641640c2fed" - integrity sha512-XxzooSo6oBoxBEUazgjdXj7VwTn/iSTSZzTYKzYY6I916tkaYzypHxy+pbVU1h+0UQ9JlVf5XkNQyxOAiiQO1g== - dependencies: - is-self-closing "^1.0.1" - kind-of "^6.0.0" - -htmlparser2@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" - integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.0.0" - domutils "^2.5.2" - entities "^2.0.0" - http-assert@^1.3.0: version "1.5.0" resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.5.0.tgz#c389ccd87ac16ed2dfa6246fd73b926aa00e6b8f" @@ -7390,13 +7138,6 @@ is-docker@^2.0.0, is-docker@^2.1.1: resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== -is-even@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-even/-/is-even-1.0.0.tgz#76b5055fbad8d294a86b6a949015e1c97b717c06" - integrity sha1-drUFX7rY0pSoa2qUkBXhyXtxfAY= - dependencies: - is-odd "^0.1.2" - is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -7486,13 +7227,6 @@ is-number-object@^1.0.4: dependencies: has-tostringtag "^1.0.0" -is-number@^2.0.2: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" - integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= - dependencies: - kind-of "^3.0.2" - is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -7515,13 +7249,6 @@ is-object@^1.0.1: resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== -is-odd@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-0.1.2.tgz#bc573b5ce371ef2aad6e6f49799b72bef13978a7" - integrity sha1-vFc7XONx7yqtbm9JeZtyvvE5eKc= - dependencies: - is-number "^3.0.0" - is-path-inside@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" @@ -7539,11 +7266,6 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" -is-plain-object@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" - integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== - is-potential-custom-element-name@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" @@ -7572,13 +7294,6 @@ is-retry-allowed@^2.2.0: resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz#88f34cbd236e043e71b6932d09b0c65fb7b4d71d" integrity sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg== -is-self-closing@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-self-closing/-/is-self-closing-1.0.1.tgz#5f406b527c7b12610176320338af0fa3896416e4" - integrity sha512-E+60FomW7Blv5GXTlYee2KDrnG6srxF7Xt1SjrhWUGUEsTFIqY/nq2y3DaftCsgUMdh89V07IVfhY9KIJhLezg== - dependencies: - self-closing-tags "^1.0.1" - is-shared-array-buffer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" @@ -7676,11 +7391,6 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= -isobject@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-0.2.0.tgz#a3432192f39b910b5f02cc989487836ec70aa85e" - integrity sha1-o0MhkvObkQtfAsyYlIeDbscKqF4= - isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" @@ -8863,7 +8573,7 @@ keyv@^3.0.0: dependencies: json-buffer "3.0.0" -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.1.0, kind-of@^3.2.0: +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= @@ -8877,12 +8587,12 @@ kind-of@^4.0.0: dependencies: is-buffer "^1.1.5" -kind-of@^5.0.0, kind-of@^5.0.2: +kind-of@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== -kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: +kind-of@^6.0.0, kind-of@^6.0.2: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== @@ -9142,11 +8852,6 @@ lcid@^2.0.0: dependencies: invert-kv "^2.0.0" -leaflet@^1.7.1: - version "1.8.0" - resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.8.0.tgz#4615db4a22a304e8e692cae9270b983b38a2055e" - integrity sha512-gwhMjFCQiYs3x/Sf+d49f10ERXaEFCPr+nVTryhAW8DWbMGqJqt9G4XuIaHmFW08zYvhgdzqXGr8AlW8v8dQkA== - left-pad@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" @@ -9355,11 +9060,6 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -lodash._reinterpolate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" - integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= - lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" @@ -9470,21 +9170,6 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= -lodash.template@^4.4.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" - integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.templatesettings "^4.0.0" - -lodash.templatesettings@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" - integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.without@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac" @@ -9495,7 +9180,7 @@ lodash.xor@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.xor/-/lodash.xor-4.5.0.tgz#4d48ed7e98095b0632582ba714d3ff8ae8fb1db6" integrity sha1-TUjtfpgJWwYyWCunFNP/iuj7HbY= -lodash@4.17.21, lodash@^4.14.0, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.7.0: +lodash@4.17.21, lodash@^4.14.0, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -9563,13 +9248,6 @@ ltgt@2.2.1, ltgt@^2.1.2, ltgt@~2.2.0: resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU= -magic-string@^0.25.7: - version "0.25.9" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" - integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== - dependencies: - sourcemap-codec "^1.4.8" - make-dir@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" @@ -9634,11 +9312,6 @@ markdown-it@^12.2.0: mdurl "^1.0.1" uc.micro "^1.0.5" -marked@^4.0.10: - version "4.0.15" - resolved "https://registry.yarnpkg.com/marked/-/marked-4.0.15.tgz#0216b7c9d5fcf6ac5042343c41d81a8b1b5e1b4a" - integrity sha512-esX5lPdTfG4p8LDkv+obbRCyOKzB+820ZZyMOXJZygZBHrH9b3xXR64X4kT3sPe9Nx8qQXbmcz6kFSMt4Nfk6Q== - matcher@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca" @@ -9711,7 +9384,7 @@ methods@^1.0.1, methods@^1.1.1, methods@^1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= -micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.5: +micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -9936,16 +9609,6 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== -nanoid@^2.1.0: - version "2.1.11" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280" - integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA== - -nanoid@^3.3.3: - version "3.3.4" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" - integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== - nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -10498,11 +10161,6 @@ parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" -parse-srcset@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1" - integrity sha1-8r0iH2zJcKk42IVWq8WJyqqiveE= - parse5@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" @@ -10744,11 +10402,6 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== -picomatch@^2.2.2: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -10859,15 +10512,6 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= -postcss@^8.3.11: - version "8.4.13" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.13.tgz#7c87bc268e79f7f86524235821dfdf9f73e5d575" - integrity sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA== - dependencies: - nanoid "^3.3.3" - picocolors "^1.0.0" - source-map-js "^1.0.2" - postgres-array@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" @@ -11588,16 +11232,6 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexparam@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/regexparam/-/regexparam-2.0.0.tgz#059476767d5f5f87f735fc7922d133fd1a118c8c" - integrity sha512-gJKwd2MVPWHAIFLsaYDZfyKzHNS4o7E/v8YmNf44vmeV2e4YfVoDToTOKTvE7ab68cRJ++kLuEXJBaEeJVt5ow== - -regexparam@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/regexparam/-/regexparam-1.3.0.tgz#2fe42c93e32a40eff6235d635e0ffa344b92965f" - integrity sha512-6IQpFBv6e5vz1QAqI+V4k8P2e/3gRrqfCJ9FI+O1FLQTO+Uz6RXZEZOPmTJ6hlGj7gkERzY5BRCv09whKP96/g== - regexpp@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" @@ -11641,21 +11275,6 @@ regjsparser@^0.8.2: dependencies: jsesc "~0.5.0" -relative@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/relative/-/relative-3.0.2.tgz#0dcd8ec54a5d35a3c15e104503d65375b5a5367f" - integrity sha1-Dc2OxUpdNaPBXhBFA9ZTdbWlNn8= - dependencies: - isobject "^2.0.0" - -remarkable@^1.6.2: - version "1.7.4" - resolved "https://registry.yarnpkg.com/remarkable/-/remarkable-1.7.4.tgz#19073cb960398c87a7d6546eaa5e50d2022fcd00" - integrity sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg== - dependencies: - argparse "^1.0.10" - autolinker "~0.28.0" - remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -11859,13 +11478,6 @@ roarr@^2.15.3: semver-compare "^1.0.0" sprintf-js "^1.1.2" -rollup-plugin-polyfill-node@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-polyfill-node/-/rollup-plugin-polyfill-node-0.8.0.tgz#859c070822f5e38d221e5b4238cb34aa894c2b19" - integrity sha512-C4UeKedOmOBkB3FgR+z/v9kzRwV1Q/H8xWs1u1+CNe4XOV6hINfOrcO+TredKxYvopCmr+WKUSNsFUnD1RLHgQ== - dependencies: - "@rollup/plugin-inject" "^4.0.0" - rsvp@^4.8.4: version "4.8.5" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" @@ -11932,18 +11544,6 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" -sanitize-html@^2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.7.0.tgz#e106205b468aca932e2f9baf241f24660d34e279" - integrity sha512-jfQelabOn5voO7FAfnQF7v+jsA6z9zC/O4ec0z3E35XPEtHYJT/OdUziVWlKW4irCr2kXaQAyXTXDHWAibg1tA== - dependencies: - deepmerge "^4.2.2" - escape-string-regexp "^4.0.0" - htmlparser2 "^6.0.0" - is-plain-object "^5.0.0" - parse-srcset "^1.0.2" - postcss "^8.3.11" - sanitize-s3-objectkey@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/sanitize-s3-objectkey/-/sanitize-s3-objectkey-0.0.1.tgz#efa9887cd45275b40234fb4bb12fc5754fe64e7e" @@ -11982,11 +11582,6 @@ schema-utils@^3.1.0, schema-utils@^3.1.1: ajv "^6.12.5" ajv-keywords "^3.5.2" -screenfull@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-6.0.1.tgz#3b71e6f06b72d817a8d3be73c45ebe71fa8da1ce" - integrity sha512-yzQW+j4zMUBQC51xxWaoDYjxOtl8Kn+xvue3p6v/fv2pIi1jH4AldgVLU8TBfFVgH2x3VXlf3+YiA/AYIPlaew== - search-params@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/search-params/-/search-params-3.0.0.tgz#dbc7c243058e5a33ae1e9870be91f5aced4100d8" @@ -12004,11 +11599,6 @@ seek-bzip@^1.0.5: dependencies: commander "^2.8.1" -self-closing-tags@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/self-closing-tags/-/self-closing-tags-1.0.1.tgz#6c5fa497994bb826b484216916371accee490a5d" - integrity sha512-7t6hNbYMxM+VHXTgJmxwgZgLGktuXtVVD5AivWzNTdJBM4DBjnDKDzkf2SrNjihaArpeJYNjxkELBu1evI4lQA== - semver-compare@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" @@ -12144,13 +11734,6 @@ shimmer@^1.2.0: resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== -shortid@^2.2.15: - version "2.2.16" - resolved "https://registry.yarnpkg.com/shortid/-/shortid-2.2.16.tgz#b742b8f0cb96406fd391c76bfc18a67a57fe5608" - integrity sha512-Ugt+GIZqvGXCIItnsL+lvFJOiN7RYqlGy7QE41O3YC1xbNSeDGIRO7xg2JJXIAj1cAGnOeC1r7/T9pgrtQbv4g== - dependencies: - nanoid "^2.1.0" - side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -12265,11 +11848,6 @@ source-list-map@^2.0.1: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -source-map-js@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" - integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== - source-map-resolve@^0.5.0: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -12323,11 +11901,6 @@ source-map@^0.7.3, source-map@~0.7.2: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== -sourcemap-codec@^1.4.8: - version "1.4.8" - resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" - integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== - spark-md5@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/spark-md5/-/spark-md5-3.0.1.tgz#83a0e255734f2ab4e5c466e5a2cfc9ba2aa2124d" @@ -12650,11 +12223,6 @@ strip-outer@^1.0.0: dependencies: escape-string-regexp "^1.0.2" -striptags@^3.1.1: - version "3.2.0" - resolved "https://registry.yarnpkg.com/striptags/-/striptags-3.2.0.tgz#cc74a137db2de8b0b9a370006334161f7dd67052" - integrity sha512-g45ZOGzHDMe2bdYMdIvdAfCQkCTDMGBazSw1ypMowwGIee7ZQ5dU0rBJ8Jqgl+jAKIv4dbeE1jscZq9wid1Tkw== - style-loader@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.1.tgz#057dfa6b3d4d7c7064462830f9113ed417d38575" @@ -12754,23 +12322,11 @@ svelte-portal@^1.0.0: resolved "https://registry.yarnpkg.com/svelte-portal/-/svelte-portal-1.0.0.tgz#36a47c5578b1a4d9b4dc60fa32a904640ec4cdd3" integrity sha512-nHf+DS/jZ6jjnZSleBMSaZua9JlG5rZv9lOGKgJuaZStfevtjIlUJrkLc3vbV8QdBvPPVmvcjTlazAzfKu0v3Q== -svelte-spa-router@^3.0.5: - version "3.2.0" - resolved "https://registry.yarnpkg.com/svelte-spa-router/-/svelte-spa-router-3.2.0.tgz#fae3311d292451236cb57131262406cf312b15ee" - integrity sha512-igemo5Vs82TGBBw+DjWt6qKameXYzNs6aDXcTxou5XbEvOjiRcAM6MLkdVRCatn6u8r42dE99bt/br7T4qe/AQ== - dependencies: - regexparam "2.0.0" - svelte@^3.38.2: version "3.44.1" resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.44.1.tgz#5cc772a8340f4519a4ecd1ac1a842325466b1a63" integrity sha512-4DrCEJoBvdR689efHNSxIQn2pnFwB7E7j2yLEJtHE/P8hxwZWIphCtJ8are7bjl/iVMlcEf5uh5pJ68IwR09vQ== -svelte@^3.46.2: - version "3.48.0" - resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.48.0.tgz#f98c866d45e155bad8e1e88f15f9c03cd28753d3" - integrity sha512-fN2YRm/bGumvjUpu6yI3BpvZnpIm9I6A7HR4oUNYd7ggYyIwSA/BX7DJ+UXXffLp6XNcUijyLvttbPVCYa/3xQ== - svg.draggable.js@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz#c514a2f1405efb6f0263e7958f5b68fce50603ba" @@ -13103,11 +12659,6 @@ to-fast-properties@^2.0.0: resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= -to-gfm-code-block@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/to-gfm-code-block/-/to-gfm-code-block-0.1.1.tgz#25d045a5fae553189e9637b590900da732d8aa82" - integrity sha1-JdBFpfrlUxielje1kJANpzLYqoI= - to-json-schema@0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/to-json-schema/-/to-json-schema-0.2.5.tgz#ef3c3f11ad64460dcfbdbafd0fd525d69d62a98f" @@ -13345,13 +12896,6 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typeof-article@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/typeof-article/-/typeof-article-0.1.1.tgz#9f07e733c3fbb646ffa9e61c08debacd460e06af" - integrity sha1-nwfnM8P7tkb/qeYcCN66zUYOBq8= - dependencies: - kind-of "^3.1.0" - typeof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/typeof/-/typeof-1.0.0.tgz#9c84403f2323ae5399167275497638ea1d2f2440" @@ -13362,11 +12906,6 @@ typescript@^4.5.5: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.2.tgz#fe12d2727b708f4eef40f51598b3398baa9611d4" integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg== -typo-js@*: - version "1.2.1" - resolved "https://registry.yarnpkg.com/typo-js/-/typo-js-1.2.1.tgz#334a0d8c3f6c56f2f1e15fdf6c31677793cbbe9b" - integrity sha512-bTGLjbD3WqZDR3CgEFkyi9Q/SS2oM29ipXrWfDb4M74ea69QwKAECVceYpaBu0GfdnASMg9Qfl67ttB23nePHg== - uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" @@ -13669,14 +13208,6 @@ vm2@^3.9.3: acorn "^8.7.0" acorn-walk "^8.2.0" -vm2@^3.9.4: - version "3.9.9" - resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.9.tgz#c0507bc5fbb99388fad837d228badaaeb499ddc5" - integrity sha512-xwTm7NLh/uOjARRBs8/95H0e8fT3Ukw5D/JJWhxMbhKzNh1Nu981jQKvkep9iKYNxzlVrdzD0mlBGkDKZWprlw== - dependencies: - acorn "^8.7.0" - acorn-walk "^8.2.0" - vuvuzela@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/vuvuzela/-/vuvuzela-1.0.3.tgz#3be145e58271c73ca55279dd851f12a682114b0b" @@ -14195,11 +13726,6 @@ yauzl@^2.4.2: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" -year@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/year/-/year-0.2.1.tgz#4083ae520a318b23ec86037f3000cb892bdf9bb0" - integrity sha1-QIOuUgoxiyPshgN/MADLiSvfm7A= - ylru@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/ylru/-/ylru-1.2.1.tgz#f576b63341547989c1de7ba288760923b27fe84f" diff --git a/packages/types/src/documents/app/app.ts b/packages/types/src/documents/app/app.ts index d1cb9a1ab3..358c2afbf1 100644 --- a/packages/types/src/documents/app/app.ts +++ b/packages/types/src/documents/app/app.ts @@ -1,3 +1,18 @@ -export interface App { +import { Document } from "./document" + +export interface App extends Document { appId: string + type: string + version: string + componentLibraries: string[] + name: string + url: string | undefined + template: string | undefined + instance: AppInstance + tenantId: string + status: string +} + +export interface AppInstance { + _id: string } diff --git a/packages/types/src/documents/app/document.ts b/packages/types/src/documents/app/document.ts new file mode 100644 index 0000000000..5808bd91e6 --- /dev/null +++ b/packages/types/src/documents/app/document.ts @@ -0,0 +1,6 @@ +export interface Document { + _id: string + _rev?: string + createdAt: string + updatedAt: string +} diff --git a/packages/types/src/documents/app/index.ts b/packages/types/src/documents/app/index.ts index 9db35940bd..d655d99033 100644 --- a/packages/types/src/documents/app/index.ts +++ b/packages/types/src/documents/app/index.ts @@ -7,3 +7,4 @@ export * from "./role" export * from "./table" export * from "./screen" export * from "./view" +export * from "./document" diff --git a/packages/worker/src/index.ts b/packages/worker/src/index.ts index 1cec2868c6..1f935e0dfc 100644 --- a/packages/worker/src/index.ts +++ b/packages/worker/src/index.ts @@ -17,6 +17,7 @@ const http = require("http") const api = require("./api") const redis = require("./utilities/redis") const Sentry = require("@sentry/node") +import { events } from "@budibase/backend-core" // this will setup http and https proxies form env variables bootstrap() @@ -67,6 +68,7 @@ server.on("close", async () => { console.log("Server Closed") } await redis.shutdown() + await events.shutdown() }) module.exports = server.listen(parseInt(env.PORT || 4002), async () => { diff --git a/packages/worker/yarn.lock b/packages/worker/yarn.lock index 5799e7bac7..4e0af161e3 100644 --- a/packages/worker/yarn.lock +++ b/packages/worker/yarn.lock @@ -293,10 +293,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@1.0.138": - version "1.0.138" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.138.tgz#5297d6cf5b9ec8c15f0a6df4c7d8273b8ac900f0" - integrity sha512-1qN/5urKX8bBXwEz266Z94rco8dTI7VqIh75m8ZcqrAfoUpjvZJS76gZxfc5U/QWPwrgVFnLtYvnEjaLbGEflg== +"@budibase/backend-core@1.0.142": + version "1.0.142" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.142.tgz#cbfa426dbf064dff0ea5f9840baae595c0f5343c" + integrity sha512-gd52tOm4mytHa3OQQVcEiwnVCvq0pC0q3YN+5D1uoGt9WaAZoshB5AhXnUPZohvpo5j8isO07qXvF+5YffPVaw== dependencies: "@techpass/passport-openidconnect" "^0.3.0" aws-sdk "^2.901.0" @@ -321,12 +321,12 @@ uuid "^8.3.2" zlib "^1.0.5" -"@budibase/pro@1.0.138": - version "1.0.138" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.138.tgz#cacbebe5ce93eb533af62a794a638944c2c61544" - integrity sha512-4ABlUZvl2h8sd8awJATf3KJeoFWV/8SoqdbKiH1ICdUcM/6dad7nhbJ15QqJL+Uuh/+mN2yEbr8V6Un2+yF+CA== +"@budibase/pro@1.0.142": + version "1.0.142" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.142.tgz#17cd53d67ce80e0cce2179caee0c03e6a0d7084e" + integrity sha512-c1SaRoVik/AVn4zNNsDciFeIr+P3J6Sc8hsE5M3pb0Zj/NzSEHXTesxcBVLlD2PYlwnvWPSoMvdDJKrEFHvOYQ== dependencies: - "@budibase/backend-core" "1.0.138" + "@budibase/backend-core" "1.0.142" node-fetch "^2.6.1" "@cspotcode/source-map-consumer@0.8.0": From 8da427284cc72d6dcbf0f7031c9c13c04a944618 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 18 May 2022 10:56:44 +0100 Subject: [PATCH 36/75] Stub out migrations + start tests + add build watching for backend-core --- package.json | 1 + packages/backend-core/package.json | 5 +- .../backend-core/src/events/publishers/app.ts | 8 - .../src/events/publishers/index.ts | 2 +- .../backend-core/src/events/publishers/row.ts | 23 - .../src/events/publishers/rows.ts | 26 + packages/backend-core/yarn.lock | 509 +++++++++++++++++- packages/server/package.json | 1 + .../server/src/api/controllers/table/utils.ts | 2 +- .../functions/backfill/app/account.ts | 16 +- .../functions/backfill/app/automation.ts | 17 +- .../functions/backfill/app/datasource.ts | 11 +- .../functions/backfill/app/layout.ts | 11 +- .../functions/backfill/app/query.ts | 14 +- .../migrations/functions/backfill/app/role.ts | 14 +- .../migrations/functions/backfill/app/row.ts | 9 +- .../functions/backfill/tests/app.spec.ts | 42 ++ .../src/tests/utilities/TestConfiguration.js | 17 +- .../types/src/documents/account/account.ts | 1 + packages/types/src/documents/app/index.ts | 1 + packages/types/src/documents/app/row.ts | 1 + packages/types/src/events/event.ts | 6 +- packages/types/src/events/index.ts | 2 +- packages/types/src/events/row.ts | 1 - packages/types/src/events/rows.ts | 5 + packages/worker/package.json | 1 + 26 files changed, 676 insertions(+), 70 deletions(-) delete mode 100644 packages/backend-core/src/events/publishers/row.ts create mode 100644 packages/backend-core/src/events/publishers/rows.ts create mode 100644 packages/server/src/migrations/functions/backfill/tests/app.spec.ts create mode 100644 packages/types/src/documents/app/row.ts delete mode 100644 packages/types/src/events/row.ts create mode 100644 packages/types/src/events/rows.ts diff --git a/package.json b/package.json index 91560da1b0..f02e9eb8e1 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "setup": "node ./hosting/scripts/setup.js && yarn && yarn bootstrap && yarn build && yarn dev", "bootstrap": "lerna link && lerna bootstrap && ./scripts/link-dependencies.sh", "build": "lerna run build", + "build:watch": "lerna run build:watch --ignore @budibase/backend-core --stream --parallel", "release": "lerna publish patch --yes --force-publish && yarn release:pro", "release:develop": "lerna publish prerelease --yes --force-publish --dist-tag develop && yarn release:pro:develop", "release:pro": "bash scripts/pro/release.sh", diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 93ac74831b..cd6586a49c 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -8,7 +8,7 @@ "license": "GPL-3.0", "scripts": { "build": "rimraf dist/ && tsc -p tsconfig.build.json", - "dev:builder": "rimraf dist/ && tsc -p tsconfig.build.json --watch --preserveWatchOutput", + "build:watch": "rimraf dist/ && tsc -p tsconfig.build.json --watch --preserveWatchOutput", "test": "jest", "test:watch": "jest --watchAll" }, @@ -59,7 +59,8 @@ "pouchdb-all-dbs": "^1.0.2", "timekeeper": "^2.2.0", "ts-jest": "^27.0.3", - "typescript": "^4.5.5" + "typescript": "^4.5.5", + "nodemon": "^2.0.7" }, "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc" } diff --git a/packages/backend-core/src/events/publishers/app.ts b/packages/backend-core/src/events/publishers/app.ts index 2bce8f81c1..dc8ca165fd 100644 --- a/packages/backend-core/src/events/publishers/app.ts +++ b/packages/backend-core/src/events/publishers/app.ts @@ -1,5 +1,4 @@ import { publishEvent } from "../events" -import { getAppDB } from "../../context" import { Event, App, @@ -16,13 +15,6 @@ import { AppExportedEvent, } from "@budibase/types" -const saveApp = async (app: App): Promise => { - const db = getAppDB() - const response = await db.save(app) - app._rev = response.rev - return app -} - export const created = async (app: App) => { const properties: AppCreatedEvent = {} await publishEvent(Event.APP_CREATED, properties) diff --git a/packages/backend-core/src/events/publishers/index.ts b/packages/backend-core/src/events/publishers/index.ts index b8381622b7..fd60b9dc4f 100644 --- a/packages/backend-core/src/events/publishers/index.ts +++ b/packages/backend-core/src/events/publishers/index.ts @@ -10,7 +10,7 @@ export * as org from "./org" export * as query from "./query" export * as role from "./role" export * as screen from "./screen" -export * as row from "./row" +export * as rows from "./rows" export * as table from "./table" export * as serve from "./serve" export * as user from "./user" diff --git a/packages/backend-core/src/events/publishers/row.ts b/packages/backend-core/src/events/publishers/row.ts deleted file mode 100644 index e233c78a2b..0000000000 --- a/packages/backend-core/src/events/publishers/row.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { publishEvent } from "../events" -import { - Event, - RowImportedEvent, - RowImportFormat, - Table, -} from "@budibase/types" - -/* eslint-disable */ - -// exports.created = () => { -// const properties = {} -// events.processEvent(Events.ROW_CREATED, properties) -// } - -export const imported = ( - table: Table, - format: RowImportFormat, - count: number -) => { - const properties: RowImportedEvent = {} - publishEvent(Event.ROW_IMPORT, properties) -} diff --git a/packages/backend-core/src/events/publishers/rows.ts b/packages/backend-core/src/events/publishers/rows.ts new file mode 100644 index 0000000000..450a5a6495 --- /dev/null +++ b/packages/backend-core/src/events/publishers/rows.ts @@ -0,0 +1,26 @@ +import { publishEvent } from "../events" +import { + Event, + RowsImportedEvent, + RowsCreatedEvent, + RowImportFormat, + Table, +} from "@budibase/types" + +/* eslint-disable */ + +export const created = (count: number) => { + const properties: RowsCreatedEvent = { + count, + } + publishEvent(Event.ROWS_CREATED, properties) +} + +export const imported = ( + table: Table, + format: RowImportFormat, + count: number +) => { + const properties: RowsImportedEvent = {} + publishEvent(Event.ROWS_IMPORTED, properties) +} diff --git a/packages/backend-core/yarn.lock b/packages/backend-core/yarn.lock index c28a591a0c..96f2520ce7 100644 --- a/packages/backend-core/yarn.lock +++ b/packages/backend-core/yarn.lock @@ -721,6 +721,11 @@ koa "^2.13.4" node-mocks-http "^1.5.8" +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" @@ -735,6 +740,13 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + "@techpass/passport-openidconnect@^0.3.0": version "0.3.2" resolved "https://registry.yarnpkg.com/@techpass/passport-openidconnect/-/passport-openidconnect-0.3.2.tgz#f8fd5d97256286665dbf26dac92431f977ab1e63" @@ -1009,6 +1021,11 @@ abab@^2.0.3, abab@^2.0.5: resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + abort-controller@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -1100,6 +1117,13 @@ amdefine@>=0.0.4: resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= +ansi-align@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== + dependencies: + string-width "^4.1.0" + ansi-escapes@^4.2.1: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" @@ -1136,7 +1160,7 @@ any-promise@^1.1.0: resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= -anymatch@^3.0.3: +anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== @@ -1343,6 +1367,11 @@ bcryptjs@^2.4.3: resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb" integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms= +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + bl@^4.0.3: version "4.1.0" resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" @@ -1359,6 +1388,20 @@ boom@2.x.x: dependencies: hoek "2.x.x" +boxen@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" + integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^6.2.0" + chalk "^4.1.0" + cli-boxes "^2.2.1" + string-width "^4.2.2" + type-fest "^0.20.2" + widest-line "^3.1.0" + wrap-ansi "^7.0.0" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1367,7 +1410,7 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^3.0.2: +braces@^3.0.2, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -1455,6 +1498,19 @@ cache-content-type@^1.0.0: mime-types "^2.1.18" ylru "^1.2.0" +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -1494,7 +1550,7 @@ chalk@^2.0.0: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0: +chalk@^4.0.0, chalk@^4.1.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -1512,11 +1568,31 @@ charenc@0.0.2: resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= +chokidar@^3.5.2: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + chownr@^1.1.1: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + ci-info@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" @@ -1527,6 +1603,11 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== +cli-boxes@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -1541,6 +1622,13 @@ clone-buffer@1.0.0: resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + cls-hooked@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908" @@ -1633,6 +1721,18 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +configstore@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" + integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== + dependencies: + dot-prop "^5.2.0" + graceful-fs "^4.1.2" + make-dir "^3.0.0" + unique-string "^2.0.0" + write-file-atomic "^3.0.0" + xdg-basedir "^4.0.0" + content-disposition@^0.5.3, content-disposition@~0.5.2: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" @@ -1694,6 +1794,11 @@ cryptiles@2.x.x: dependencies: boom "2.x.x" +crypto-random-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" + integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== + cssom@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" @@ -1734,6 +1839,13 @@ debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: dependencies: ms "2.1.2" +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + debug@^4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" @@ -1753,6 +1865,13 @@ decimal.js@^10.2.1: resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + dedent@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" @@ -1763,6 +1882,11 @@ deep-equal@~1.0.1: resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + deep-is@~0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -1773,6 +1897,11 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + deferred-leveldown@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz#27a997ad95408b61161aa69bd489b86c71b78058" @@ -1841,11 +1970,23 @@ domexception@^2.0.1: dependencies: webidl-conversions "^5.0.0" +dot-prop@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" + integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== + dependencies: + is-obj "^2.0.0" + double-ended-queue@2.1.0-0: version "2.1.0-0" resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c" integrity sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw= +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + ecc-jsbn@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" @@ -1962,6 +2103,11 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +escape-goat@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" + integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== + escape-html@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -2191,7 +2337,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^2.3.2: +fsevents@^2.3.2, fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -2221,6 +2367,20 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + get-stream@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" @@ -2233,6 +2393,13 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + glob@^5.0.15: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" @@ -2256,6 +2423,13 @@ glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" +global-dirs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" + integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== + dependencies: + ini "2.0.0" + globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -2287,6 +2461,23 @@ googleapis@^16.0.0: google-auth-library "~0.10.0" string-template "~1.0.0" +got@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + graceful-fs@^4.1.2: version "4.2.8" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" @@ -2355,6 +2546,11 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" +has-yarn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" + integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== + has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -2397,6 +2593,11 @@ http-assert@^1.3.0: deep-equal "~1.0.1" http-errors "~1.8.0" +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + http-errors@^1.6.3, http-errors@~1.8.0: version "1.8.1" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" @@ -2460,6 +2661,11 @@ ieee754@1.1.13, ieee754@^1.1.13, ieee754@^1.1.4: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== +ignore-by-default@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" + integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk= + immediate@3.3.0, immediate@^3.2.3: version "3.3.0" resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" @@ -2470,6 +2676,11 @@ immediate@~3.0.5: resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= +import-lazy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" + integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= + import-local@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.3.tgz#4d51c2c495ca9393da259ec66b62e022920211e0" @@ -2496,6 +2707,16 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, i resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +ini@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + ioredis-mock@^5.5.5: version "5.8.0" resolved "https://registry.yarnpkg.com/ioredis-mock/-/ioredis-mock-5.8.0.tgz#99d3f0fe58419c2ff15c81cb4e13709aff8f2a44" @@ -2528,11 +2749,25 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-buffer@~1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + is-core-module@^2.8.1: version "2.9.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" @@ -2540,6 +2775,11 @@ is-core-module@^2.8.1: dependencies: has "^1.0.3" +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -2557,11 +2797,41 @@ is-generator-function@^1.0.7: dependencies: has-tostringtag "^1.0.0" +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-installed-globally@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" + integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== + dependencies: + global-dirs "^3.0.0" + is-path-inside "^3.0.2" + +is-npm@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" + integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + +is-path-inside@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-potential-custom-element-name@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" @@ -2582,6 +2852,11 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-yarn-global@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" + integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== + isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -3133,6 +3408,11 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + json-parse-even-better-errors@^2.3.0: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" @@ -3262,6 +3542,13 @@ keygrip@~1.1.0: dependencies: tsscmp "1.0.6" +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -3366,6 +3653,13 @@ koa@^2.13.4: type-is "^1.6.16" vary "^1.1.2" +latest-version@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" + integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== + dependencies: + package-json "^6.3.0" + level-codec@9.0.2, level-codec@^9.0.0: version "9.0.2" resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-9.0.2.tgz#fd60df8c64786a80d44e63423096ffead63d8cbc" @@ -3562,6 +3856,16 @@ lodash@^4.17.21, lodash@^4.7.0: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -3693,6 +3997,11 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + "minimatch@2 || 3", minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -3810,11 +4119,39 @@ node-releases@^2.0.3: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.4.tgz#f38252370c43854dc48aa431c766c6c398f40476" integrity sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ== -normalize-path@^3.0.0: +nodemon@^2.0.7: + version "2.0.16" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.16.tgz#d71b31bfdb226c25de34afea53486c8ef225fdef" + integrity sha512-zsrcaOfTWRuUzBn3P44RDliLlp263Z/76FPoHFr3cFFkOz0lTPAcIw8dCzfdVIx/t3AtDYCZRCDkoCojJqaG3w== + dependencies: + chokidar "^3.5.2" + debug "^3.2.7" + ignore-by-default "^1.0.1" + minimatch "^3.0.4" + pstree.remy "^1.1.8" + semver "^5.7.1" + supports-color "^5.5.0" + touch "^3.1.0" + undefsafe "^2.0.5" + update-notifier "^5.1.0" + +nopt@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4= + dependencies: + abbrev "1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +normalize-url@^4.1.0: + version "4.5.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== + npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" @@ -3890,6 +4227,11 @@ os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -3914,6 +4256,16 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +package-json@^6.3.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" + integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== + dependencies: + got "^9.6.0" + registry-auth-token "^4.0.0" + registry-url "^5.0.0" + semver "^6.2.0" + parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" @@ -4057,7 +4409,7 @@ picomatch@^2.0.4, picomatch@^2.2.3: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== -picomatch@^2.3.1: +picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -4311,6 +4663,11 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + pretty-format@^27.0.0, pretty-format@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" @@ -4348,6 +4705,11 @@ psl@^1.1.28, psl@^1.1.33: resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== +pstree.remy@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" + integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== + pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -4371,6 +4733,13 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +pupa@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" + integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== + dependencies: + escape-goat "^2.0.0" + q@^1.1.2: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -4396,6 +4765,16 @@ range-parser@^1.2.0: resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== +rc@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + react-is@^17.0.1: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" @@ -4438,6 +4817,13 @@ readable-stream@~2.3.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + readline-sync@^1.4.9: version "1.4.10" resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.10.tgz#41df7fbb4b6312d673011594145705bf56d8873b" @@ -4475,6 +4861,20 @@ regenerator-runtime@^0.13.4: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== +registry-auth-token@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250" + integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== + dependencies: + rc "^1.2.8" + +registry-url@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" + integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== + dependencies: + rc "^1.2.8" + remove-trailing-slash@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz#be2285a59f39c74d1bce4f825950061915e3780d" @@ -4565,6 +4965,13 @@ resolve@^1.20.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + rimraf@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -4609,19 +5016,26 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -semver@7.x: +semver-diff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" + integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== + dependencies: + semver "^6.3.0" + +semver@7.x, semver@^7.3.4: version "7.3.7" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== dependencies: lru-cache "^6.0.0" -semver@^5.4.1, semver@^5.6.0: +semver@^5.4.1, semver@^5.6.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.0.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -4795,7 +5209,7 @@ string-template@~1.0.0: resolved "https://registry.yarnpkg.com/string-template/-/string-template-1.0.0.tgz#9e9f2233dc00f218718ec379a28a5673ecca8b96" integrity sha1-np8iM9wA8hhxjsN5oopWc+zKi5Y= -string-width@^4.1.0, string-width@^4.2.0: +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -4850,6 +5264,11 @@ strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + sublevel-pouchdb@7.2.2: version "7.2.2" resolved "https://registry.yarnpkg.com/sublevel-pouchdb/-/sublevel-pouchdb-7.2.2.tgz#49e46cd37883bf7ff5006d7c5b9bcc7bcc1f422f" @@ -4860,7 +5279,7 @@ sublevel-pouchdb@7.2.2: ltgt "2.2.1" readable-stream "1.1.14" -supports-color@^5.3.0: +supports-color@^5.3.0, supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== @@ -4990,6 +5409,11 @@ to-fast-properties@^2.0.0: resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -5002,6 +5426,13 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +touch@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" + integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== + dependencies: + nopt "~1.0.10" + "tough-cookie@^2.3.3 || ^3.0.1 || ^4.0.0", tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -5081,6 +5512,11 @@ type-detect@4.0.8: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + type-fest@^0.21.3: version "0.21.3" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" @@ -5111,11 +5547,43 @@ uid2@0.0.x: resolved "https://registry.yarnpkg.com/uid2/-/uid2-0.0.4.tgz#033f3b1d5d32505f5ce5f888b9f3b667123c0a44" integrity sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA== +undefsafe@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" + integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== + +unique-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" + integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== + dependencies: + crypto-random-string "^2.0.0" + universalify@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +update-notifier@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9" + integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw== + dependencies: + boxen "^5.0.0" + chalk "^4.1.0" + configstore "^5.0.1" + has-yarn "^2.1.0" + import-lazy "^2.1.0" + is-ci "^2.0.0" + is-installed-globally "^0.4.0" + is-npm "^5.0.0" + is-yarn-global "^0.3.0" + latest-version "^5.1.0" + pupa "^2.1.1" + semver "^7.3.4" + semver-diff "^3.1.1" + xdg-basedir "^4.0.0" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -5123,6 +5591,13 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + url@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" @@ -5272,6 +5747,13 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" @@ -5313,6 +5795,11 @@ ws@^7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881" integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== +xdg-basedir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" + integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== + xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" diff --git a/packages/server/package.json b/packages/server/package.json index 4d3aaec8d7..04ac61ee32 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -10,6 +10,7 @@ }, "scripts": { "build": "rimraf dist/ && tsc -p tsconfig.build.json && mv dist/src/* dist/ && rimraf dist/src/ && yarn postbuild", + "build:watch": "rimraf dist/ && tsc -p tsconfig.build.json --watch --preserveWatchOutput", "postbuild": "copyfiles -u 1 src/**/*.svelte dist/ && copyfiles -u 1 src/**/*.hbs dist/ && copyfiles -u 1 src/**/*.json dist/", "test": "jest --coverage --maxWorkers=2", "test:watch": "jest --watch", diff --git a/packages/server/src/api/controllers/table/utils.ts b/packages/server/src/api/controllers/table/utils.ts index f598b757d2..31fd1d4220 100644 --- a/packages/server/src/api/controllers/table/utils.ts +++ b/packages/server/src/api/controllers/table/utils.ts @@ -149,7 +149,7 @@ export async function handleDataImport(user: any, table: any, dataImport: any) { } await quotas.addRows(finalData.length, () => db.bulkDocs(finalData)) - events.row.imported(table, "csv", finalData.length) + events.rows.imported(table, "csv", finalData.length) return table } diff --git a/packages/server/src/migrations/functions/backfill/app/account.ts b/packages/server/src/migrations/functions/backfill/app/account.ts index 2fe7003437..a0f10d218f 100644 --- a/packages/server/src/migrations/functions/backfill/app/account.ts +++ b/packages/server/src/migrations/functions/backfill/app/account.ts @@ -1,4 +1,16 @@ // TODO: Add migrations to account portal -// ACCOUNT_CREATED = "account:created", -// ACCOUNT_VERIFIED = "account:verified", +import { events, db } from "@budibase/backend-core" +import { Account } from "@budibase/types" + +export const backfill = async (appDb: any) => { + const accounts: Account[] = [] + + for (const account of accounts) { + events.account.created(account) + + if (account.verified) { + events.account.verified(account) + } + } +} diff --git a/packages/server/src/migrations/functions/backfill/app/automation.ts b/packages/server/src/migrations/functions/backfill/app/automation.ts index a1f908b8b5..86792d072d 100644 --- a/packages/server/src/migrations/functions/backfill/app/automation.ts +++ b/packages/server/src/migrations/functions/backfill/app/automation.ts @@ -1,2 +1,15 @@ -// AUTOMATION_CREATED = "automation:created", -// AUTOMATION_STEP_CREATED = "automation:step:created", +import { events, db } from "@budibase/backend-core" +import { Automation, AutomationStep } from "@budibase/types" + +export const backfill = async (appDb: any) => { + const automations: Automation[] = [] + + for (const automation of automations) { + events.automation.created(automation) + + const steps: AutomationStep[] = [] + for (const step of steps) { + events.automation.stepCreated(automation, step) + } + } +} diff --git a/packages/server/src/migrations/functions/backfill/app/datasource.ts b/packages/server/src/migrations/functions/backfill/app/datasource.ts index 1830d54924..fa01c2e3ad 100644 --- a/packages/server/src/migrations/functions/backfill/app/datasource.ts +++ b/packages/server/src/migrations/functions/backfill/app/datasource.ts @@ -1 +1,10 @@ -// DATASOURCE_CREATED = "datasource:created", +import { events, db } from "@budibase/backend-core" +import { Datasource } from "@budibase/types" + +export const backfill = async (appDb: any) => { + const datasources: Datasource[] = [] + + for (const datasource of datasources) { + events.datasource.created(datasource) + } +} diff --git a/packages/server/src/migrations/functions/backfill/app/layout.ts b/packages/server/src/migrations/functions/backfill/app/layout.ts index e69d79238b..c2e4aa0faf 100644 --- a/packages/server/src/migrations/functions/backfill/app/layout.ts +++ b/packages/server/src/migrations/functions/backfill/app/layout.ts @@ -1 +1,10 @@ -// LAYOUT_CREATED = "layout:created", +import { events, db } from "@budibase/backend-core" +import { Layout } from "@budibase/types" + +export const backfill = async (appDb: any) => { + const layouts: Layout[] = [] + + for (const layout of layouts) { + events.layout.created(layout) + } +} diff --git a/packages/server/src/migrations/functions/backfill/app/query.ts b/packages/server/src/migrations/functions/backfill/app/query.ts index 79b56466e3..7031db048d 100644 --- a/packages/server/src/migrations/functions/backfill/app/query.ts +++ b/packages/server/src/migrations/functions/backfill/app/query.ts @@ -1,3 +1,11 @@ -// QUERY_CREATED = "query:created", -// -// QUERY_IMPORT = "query:import", +import { events, db } from "@budibase/backend-core" +import { Query, Datasource } from "@budibase/types" + +export const backfill = async (appDb: any) => { + const queries: Query[] = [] + + for (const query of queries) { + const datasource: Datasource = {} + events.query.created(datasource, query) + } +} diff --git a/packages/server/src/migrations/functions/backfill/app/role.ts b/packages/server/src/migrations/functions/backfill/app/role.ts index 34cbd19569..9162d9a546 100644 --- a/packages/server/src/migrations/functions/backfill/app/role.ts +++ b/packages/server/src/migrations/functions/backfill/app/role.ts @@ -1,2 +1,12 @@ -// ROLE_CREATED = "role:created", -// ROLE_ASSIGNED = "role:assigned", +import { events, db } from "@budibase/backend-core" +import { Role, User } from "@budibase/types" + +export const backfill = async (appDb: any) => { + const roles: Role[] = [] + + for (const role of roles) { + events.role.created(role) + const user: User = {} + events.role.assigned(user, role) + } +} diff --git a/packages/server/src/migrations/functions/backfill/app/row.ts b/packages/server/src/migrations/functions/backfill/app/row.ts index 52b0664231..c4eb6f2972 100644 --- a/packages/server/src/migrations/functions/backfill/app/row.ts +++ b/packages/server/src/migrations/functions/backfill/app/row.ts @@ -1 +1,8 @@ -// ROW_IMPORT = "row:import", +import { events } from "@budibase/backend-core" +import { Row } from "@budibase/types" + +export const backfill = async (appDb: any) => { + const rows: Row[] = [] + const count = rows.length + events.rows.created(count) +} diff --git a/packages/server/src/migrations/functions/backfill/tests/app.spec.ts b/packages/server/src/migrations/functions/backfill/tests/app.spec.ts new file mode 100644 index 0000000000..a8caebff1c --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/tests/app.spec.ts @@ -0,0 +1,42 @@ +import { tenancy, events } from "@budibase/backend-core" +import TestConfig from "../../../../tests/utilities/TestConfiguration" +import { backfill } from "../app/app" + +describe("app backfill", () => { + const config = new TestConfig() + + beforeEach(async () => { + await config.init() + jest.clearAllMocks() + }) + + afterEach(() => { + config.end() + }) + + it("should backfill dev app", async () => { + await config.doInContext(null, async () => { + const db = tenancy.getAppDB({}) + + await backfill(db) + + expect(events.app.created).toBeCalledTimes(1) + expect(events.app.published).toBeCalledTimes(0) + }) + }) + + it("should backfill prod app", async () => { + await config.doInContext( + null, + async () => { + const db = tenancy.getAppDB({}) + + await backfill(db) + + // expect(events.app.created).toBeCalledTimes(0) + expect(events.app.published).toBeCalledTimes(1) + }, + { prod: true } + ) + }) +}) diff --git a/packages/server/src/tests/utilities/TestConfiguration.js b/packages/server/src/tests/utilities/TestConfiguration.js index 2cdcc2854a..be883fb7f6 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.js +++ b/packages/server/src/tests/utilities/TestConfiguration.js @@ -60,13 +60,14 @@ class TestConfiguration { return this.prodAppId } - async doInContext(appId, task) { + async doInContext(appId, task, opts = { prod: false }) { if (!appId) { - appId = this.appId + appId = opts.prod ? this.prodAppId : this.appId } return doInTenant(TENANT_ID, () => { // check if already in a context - if (context.getAppId() == null && appId !== null) { + const contextId = context.getAppId() + if (appId !== null && (contextId === null || contextId !== appId)) { return context.doInAppContext(appId, async () => { return task() }) @@ -276,7 +277,7 @@ class TestConfiguration { // APP - async createApp(appName) { + async createApp(appName, opts = { deploy: true }) { // create dev app // clear any old app this.appId = null @@ -286,10 +287,12 @@ class TestConfiguration { await context.updateAppId(this.appId) // create production app - this.prodApp = await this.deploy() - this.prodAppId = this.prodApp.appId + if (opts.deploy) { + this.prodApp = await this.deploy() + this.prodAppId = this.prodApp.appId + this.allApps.push(this.prodApp) + } - this.allApps.push(this.prodApp) this.allApps.push(this.app) return this.app diff --git a/packages/types/src/documents/account/account.ts b/packages/types/src/documents/account/account.ts index 3822d63d58..98d6235f03 100644 --- a/packages/types/src/documents/account/account.ts +++ b/packages/types/src/documents/account/account.ts @@ -3,4 +3,5 @@ import { Hosting } from "../../core" export interface Account { accountId: string hosting: Hosting + verified: boolean } diff --git a/packages/types/src/documents/app/index.ts b/packages/types/src/documents/app/index.ts index d655d99033..c4d55173dc 100644 --- a/packages/types/src/documents/app/index.ts +++ b/packages/types/src/documents/app/index.ts @@ -8,3 +8,4 @@ export * from "./table" export * from "./screen" export * from "./view" export * from "./document" +export * from "./row" diff --git a/packages/types/src/documents/app/row.ts b/packages/types/src/documents/app/row.ts new file mode 100644 index 0000000000..3fb0cb00a4 --- /dev/null +++ b/packages/types/src/documents/app/row.ts @@ -0,0 +1 @@ +export interface Row {} diff --git a/packages/types/src/events/event.ts b/packages/types/src/events/event.ts index 2e1f052297..7838f0e45e 100644 --- a/packages/types/src/events/event.ts +++ b/packages/types/src/events/event.ts @@ -102,9 +102,9 @@ export enum Event { VIEW_CALCULATION_UPDATED = "view:calculation:updated", VIEW_CALCULATION_DELETED = "view:calculation:deleted", - // ROW - // ROW_CREATED = "row:created", - ROW_IMPORT = "row:import", + // ROWS + ROWS_CREATED = "rows:created", + ROWS_IMPORTED = "rows:imported", // COMPONENT COMPONENT_CREATED = "component:created", diff --git a/packages/types/src/events/index.ts b/packages/types/src/events/index.ts index a47ab8104e..e2c4ae8a6d 100644 --- a/packages/types/src/events/index.ts +++ b/packages/types/src/events/index.ts @@ -9,7 +9,7 @@ export * from "./license" export * from "./org" export * from "./query" export * from "./role" -export * from "./row" +export * from "./rows" export * from "./screen" export * from "./serve" export * from "./table" diff --git a/packages/types/src/events/row.ts b/packages/types/src/events/row.ts deleted file mode 100644 index 3d4d093e80..0000000000 --- a/packages/types/src/events/row.ts +++ /dev/null @@ -1 +0,0 @@ -export interface RowImportedEvent {} diff --git a/packages/types/src/events/rows.ts b/packages/types/src/events/rows.ts new file mode 100644 index 0000000000..bf7c021eb0 --- /dev/null +++ b/packages/types/src/events/rows.ts @@ -0,0 +1,5 @@ +export interface RowsImportedEvent {} + +export interface RowsCreatedEvent { + count: number +} diff --git a/packages/worker/package.json b/packages/worker/package.json index de38e8182a..02609f081e 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -13,6 +13,7 @@ ], "scripts": { "build": "rimraf dist/ && tsc", + "build:watch": "rimraf dist/ && tsc --watch --preserveWatchOutput", "postbuild": "copyfiles -u 1 src/**/*.hbs dist/", "run:docker": "node dist/index.js", "build:docker": "docker build . -t worker-service --label version=$BUDIBASE_RELEASE_VERSION", From 498c130e71fe173e3b818e93eb70e778bc843957 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Thu, 19 May 2022 08:19:25 +0100 Subject: [PATCH 37/75] Migration for apps, automations, datasources, layouts, queries, roles, tables --- .../src/events/publishers/role.ts | 4 +- packages/backend-core/src/migrations/index.js | 4 +- .../src/migrations/functions/backfill/app.ts | 16 +++++- .../backfill/app/{app.ts => apps.ts} | 0 .../functions/backfill/app/automation.ts | 15 ------ .../functions/backfill/app/automations.ts | 26 ++++++++++ .../functions/backfill/app/datasource.ts | 10 ---- .../functions/backfill/app/datasources.ts | 22 ++++++++ .../functions/backfill/app/layout.ts | 10 ---- .../functions/backfill/app/layouts.ts | 22 ++++++++ .../functions/backfill/app/queries.ts | 33 ++++++++++++ .../functions/backfill/app/query.ts | 11 ---- .../migrations/functions/backfill/app/role.ts | 12 ----- .../functions/backfill/app/roles.ts | 22 ++++++++ .../backfill/app/{row.ts => rows.ts} | 0 .../backfill/app/{screen.ts => screens.ts} | 0 .../functions/backfill/app/table.ts | 3 -- .../functions/backfill/app/tables.ts | 22 ++++++++ .../functions/backfill/tests/app.spec.ts | 42 --------------- .../server/src/migrations/tests/index.spec.ts | 52 +++++++++++++++++++ .../types/src/documents/app/automation.ts | 7 ++- packages/types/src/documents/app/index.ts | 1 + packages/types/src/documents/app/query.ts | 4 +- packages/types/src/documents/app/role.ts | 4 +- packages/types/src/documents/app/user.ts | 3 ++ packages/types/src/documents/global/user.ts | 8 ++- packages/worker/package.json | 1 + packages/worker/src/sdk/users/events.ts | 23 +++++--- 28 files changed, 257 insertions(+), 120 deletions(-) rename packages/server/src/migrations/functions/backfill/app/{app.ts => apps.ts} (100%) delete mode 100644 packages/server/src/migrations/functions/backfill/app/automation.ts create mode 100644 packages/server/src/migrations/functions/backfill/app/automations.ts delete mode 100644 packages/server/src/migrations/functions/backfill/app/datasource.ts create mode 100644 packages/server/src/migrations/functions/backfill/app/datasources.ts delete mode 100644 packages/server/src/migrations/functions/backfill/app/layout.ts create mode 100644 packages/server/src/migrations/functions/backfill/app/layouts.ts create mode 100644 packages/server/src/migrations/functions/backfill/app/queries.ts delete mode 100644 packages/server/src/migrations/functions/backfill/app/query.ts delete mode 100644 packages/server/src/migrations/functions/backfill/app/role.ts create mode 100644 packages/server/src/migrations/functions/backfill/app/roles.ts rename packages/server/src/migrations/functions/backfill/app/{row.ts => rows.ts} (100%) rename packages/server/src/migrations/functions/backfill/app/{screen.ts => screens.ts} (100%) delete mode 100644 packages/server/src/migrations/functions/backfill/app/table.ts create mode 100644 packages/server/src/migrations/functions/backfill/app/tables.ts delete mode 100644 packages/server/src/migrations/functions/backfill/tests/app.spec.ts create mode 100644 packages/server/src/migrations/tests/index.spec.ts create mode 100644 packages/types/src/documents/app/user.ts diff --git a/packages/backend-core/src/events/publishers/role.ts b/packages/backend-core/src/events/publishers/role.ts index a7f7e60a32..b08d001c77 100644 --- a/packages/backend-core/src/events/publishers/role.ts +++ b/packages/backend-core/src/events/publishers/role.ts @@ -27,12 +27,12 @@ export function deleted(role: Role) { publishEvent(Event.ROLE_DELETED, properties) } -export function assigned(user: User, role: Role) { +export function assigned(user: User, role: string) { const properties: RoleAssignedEvent = {} publishEvent(Event.ROLE_ASSIGNED, properties) } -export function unassigned(user: User, role: Role) { +export function unassigned(user: User, role: string) { const properties: RoleUnassignedEvent = {} publishEvent(Event.ROLE_UNASSIGNED, properties) } diff --git a/packages/backend-core/src/migrations/index.js b/packages/backend-core/src/migrations/index.js index ada1478ace..eb51613e60 100644 --- a/packages/backend-core/src/migrations/index.js +++ b/packages/backend-core/src/migrations/index.js @@ -27,7 +27,7 @@ exports.getMigrationsDoc = async db => { } } -const runMigration = async (migration, options = {}) => { +exports.runMigration = async (migration, options = {}) => { const tenantId = getTenantId() const migrationType = migration.type const migrationName = migration.name @@ -110,7 +110,7 @@ exports.runMigrations = async (migrations, options = {}) => { // for all migrations for (const migration of migrations) { // run the migration - await doInTenant(tenantId, () => runMigration(migration, options)) + await doInTenant(tenantId, () => exports.runMigration(migration, options)) } } console.log("Migrations complete") diff --git a/packages/server/src/migrations/functions/backfill/app.ts b/packages/server/src/migrations/functions/backfill/app.ts index 6080f75b1f..4b2d20b008 100644 --- a/packages/server/src/migrations/functions/backfill/app.ts +++ b/packages/server/src/migrations/functions/backfill/app.ts @@ -1,4 +1,10 @@ -import * as app from "./app/app" +import * as apps from "./app/apps" +import * as automations from "./app/automations" +import * as datasources from "./app/datasources" +import * as layouts from "./app/layouts" +import * as queries from "./app/queries" +import * as roles from "./app/roles" +import * as tables from "./app/tables" /** * Date: @@ -9,5 +15,11 @@ import * as app from "./app/app" */ export const run = async (appDb: any) => { - await app.backfill(appDb) + await apps.backfill(appDb) + await automations.backfill(appDb) + await datasources.backfill(appDb) + await layouts.backfill(appDb) + await queries.backfill(appDb) + await roles.backfill(appDb) + await tables.backfill(appDb) } diff --git a/packages/server/src/migrations/functions/backfill/app/app.ts b/packages/server/src/migrations/functions/backfill/app/apps.ts similarity index 100% rename from packages/server/src/migrations/functions/backfill/app/app.ts rename to packages/server/src/migrations/functions/backfill/app/apps.ts diff --git a/packages/server/src/migrations/functions/backfill/app/automation.ts b/packages/server/src/migrations/functions/backfill/app/automation.ts deleted file mode 100644 index 86792d072d..0000000000 --- a/packages/server/src/migrations/functions/backfill/app/automation.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { events, db } from "@budibase/backend-core" -import { Automation, AutomationStep } from "@budibase/types" - -export const backfill = async (appDb: any) => { - const automations: Automation[] = [] - - for (const automation of automations) { - events.automation.created(automation) - - const steps: AutomationStep[] = [] - for (const step of steps) { - events.automation.stepCreated(automation, step) - } - } -} diff --git a/packages/server/src/migrations/functions/backfill/app/automations.ts b/packages/server/src/migrations/functions/backfill/app/automations.ts new file mode 100644 index 0000000000..1c1f0e1da4 --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/app/automations.ts @@ -0,0 +1,26 @@ +import { events, db } from "@budibase/backend-core" +import { getAutomationParams } from "../../../../db/utils" +import { Automation } from "@budibase/types" + +const getAutomations = async (appDb: any): Promise => { + const response = await appDb.allDocs( + getAutomationParams(null, { + include_docs: true, + }) + ) + return response.rows.map((row: any) => row.doc) +} + +export const backfill = async (appDb: any) => { + if (db.isDevAppID(appDb.name)) { + const automations = await getAutomations(appDb) + + for (const automation of automations) { + events.automation.created(automation) + + for (const step of automation.definition.steps) { + events.automation.stepCreated(automation, step) + } + } + } +} diff --git a/packages/server/src/migrations/functions/backfill/app/datasource.ts b/packages/server/src/migrations/functions/backfill/app/datasource.ts deleted file mode 100644 index fa01c2e3ad..0000000000 --- a/packages/server/src/migrations/functions/backfill/app/datasource.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { events, db } from "@budibase/backend-core" -import { Datasource } from "@budibase/types" - -export const backfill = async (appDb: any) => { - const datasources: Datasource[] = [] - - for (const datasource of datasources) { - events.datasource.created(datasource) - } -} diff --git a/packages/server/src/migrations/functions/backfill/app/datasources.ts b/packages/server/src/migrations/functions/backfill/app/datasources.ts new file mode 100644 index 0000000000..7ebc92ab7c --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/app/datasources.ts @@ -0,0 +1,22 @@ +import { events, db } from "@budibase/backend-core" +import { getDatasourceParams } from "../../../../db/utils" +import { Datasource } from "@budibase/types" + +const getDatasources = async (appDb: any): Promise => { + const response = await appDb.allDocs( + getDatasourceParams(null, { + include_docs: true, + }) + ) + return response.rows.map((row: any) => row.doc) +} + +export const backfill = async (appDb: any) => { + if (db.isDevAppID(appDb.name)) { + const datasources: Datasource[] = await getDatasources(appDb) + + for (const datasource of datasources) { + events.datasource.created(datasource) + } + } +} diff --git a/packages/server/src/migrations/functions/backfill/app/layout.ts b/packages/server/src/migrations/functions/backfill/app/layout.ts deleted file mode 100644 index c2e4aa0faf..0000000000 --- a/packages/server/src/migrations/functions/backfill/app/layout.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { events, db } from "@budibase/backend-core" -import { Layout } from "@budibase/types" - -export const backfill = async (appDb: any) => { - const layouts: Layout[] = [] - - for (const layout of layouts) { - events.layout.created(layout) - } -} diff --git a/packages/server/src/migrations/functions/backfill/app/layouts.ts b/packages/server/src/migrations/functions/backfill/app/layouts.ts new file mode 100644 index 0000000000..89c3aa9dc2 --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/app/layouts.ts @@ -0,0 +1,22 @@ +import { events, db } from "@budibase/backend-core" +import { getLayoutParams } from "../../../../db/utils" +import { Layout } from "@budibase/types" + +const getLayouts = async (appDb: any): Promise => { + const response = await appDb.allDocs( + getLayoutParams(null, { + include_docs: true, + }) + ) + return response.rows.map((row: any) => row.doc) +} + +export const backfill = async (appDb: any) => { + if (db.isDevAppID(appDb.name)) { + const layouts: Layout[] = await getLayouts(appDb) + + for (const layout of layouts) { + events.layout.created(layout) + } + } +} diff --git a/packages/server/src/migrations/functions/backfill/app/queries.ts b/packages/server/src/migrations/functions/backfill/app/queries.ts new file mode 100644 index 0000000000..6c6ad985f2 --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/app/queries.ts @@ -0,0 +1,33 @@ +import { events, db } from "@budibase/backend-core" +import { getQueryParams } from "../../../../db/utils" +import { Query, Datasource } from "@budibase/types" + +const getQueries = async (appDb: any): Promise => { + const response = await appDb.allDocs( + getQueryParams(null, { + include_docs: true, + }) + ) + return response.rows.map((row: any) => row.doc) +} + +const getDatasource = async ( + appDb: any, + datasourceId: string +): Promise => { + return appDb.get(datasourceId) +} + +export const backfill = async (appDb: any) => { + if (db.isDevAppID(appDb.name)) { + const queries: Query[] = await getQueries(appDb) + + for (const query of queries) { + const datasource: Datasource = await getDatasource( + appDb, + query.datasourceId + ) + events.query.created(datasource, query) + } + } +} diff --git a/packages/server/src/migrations/functions/backfill/app/query.ts b/packages/server/src/migrations/functions/backfill/app/query.ts deleted file mode 100644 index 7031db048d..0000000000 --- a/packages/server/src/migrations/functions/backfill/app/query.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { events, db } from "@budibase/backend-core" -import { Query, Datasource } from "@budibase/types" - -export const backfill = async (appDb: any) => { - const queries: Query[] = [] - - for (const query of queries) { - const datasource: Datasource = {} - events.query.created(datasource, query) - } -} diff --git a/packages/server/src/migrations/functions/backfill/app/role.ts b/packages/server/src/migrations/functions/backfill/app/role.ts deleted file mode 100644 index 9162d9a546..0000000000 --- a/packages/server/src/migrations/functions/backfill/app/role.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { events, db } from "@budibase/backend-core" -import { Role, User } from "@budibase/types" - -export const backfill = async (appDb: any) => { - const roles: Role[] = [] - - for (const role of roles) { - events.role.created(role) - const user: User = {} - events.role.assigned(user, role) - } -} diff --git a/packages/server/src/migrations/functions/backfill/app/roles.ts b/packages/server/src/migrations/functions/backfill/app/roles.ts new file mode 100644 index 0000000000..de2ce9d37b --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/app/roles.ts @@ -0,0 +1,22 @@ +import { events, db } from "@budibase/backend-core" +import { getRoleParams } from "../../../../db/utils" +import { Role } from "@budibase/types" + +const getRoles = async (appDb: any): Promise => { + const response = await appDb.allDocs( + getRoleParams(null, { + include_docs: true, + }) + ) + return response.rows.map((row: any) => row.doc) +} + +export const backfill = async (appDb: any) => { + if (db.isDevAppID(appDb.name)) { + const roles = await getRoles(appDb) + + for (const role of roles) { + events.role.created(role) + } + } +} diff --git a/packages/server/src/migrations/functions/backfill/app/row.ts b/packages/server/src/migrations/functions/backfill/app/rows.ts similarity index 100% rename from packages/server/src/migrations/functions/backfill/app/row.ts rename to packages/server/src/migrations/functions/backfill/app/rows.ts diff --git a/packages/server/src/migrations/functions/backfill/app/screen.ts b/packages/server/src/migrations/functions/backfill/app/screens.ts similarity index 100% rename from packages/server/src/migrations/functions/backfill/app/screen.ts rename to packages/server/src/migrations/functions/backfill/app/screens.ts diff --git a/packages/server/src/migrations/functions/backfill/app/table.ts b/packages/server/src/migrations/functions/backfill/app/table.ts deleted file mode 100644 index 116e7bbbdf..0000000000 --- a/packages/server/src/migrations/functions/backfill/app/table.ts +++ /dev/null @@ -1,3 +0,0 @@ -// TABLE_CREATED = "table:created", -// -// TABLE_DATA_IMPORTED = "table:data:imported", diff --git a/packages/server/src/migrations/functions/backfill/app/tables.ts b/packages/server/src/migrations/functions/backfill/app/tables.ts new file mode 100644 index 0000000000..233474ffbd --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/app/tables.ts @@ -0,0 +1,22 @@ +import { events, db } from "@budibase/backend-core" +import { getTableParams } from "../../../../db/utils" +import { Table } from "@budibase/types" + +const getTables = async (appDb: any): Promise => { + const response = await appDb.allDocs( + getTableParams(null, { + include_docs: true, + }) + ) + return response.rows.map((row: any) => row.doc) +} + +export const backfill = async (appDb: any) => { + if (db.isDevAppID(appDb.name)) { + const tables = await getTables(appDb) + + for (const table of tables) { + events.table.created(table) + } + } +} diff --git a/packages/server/src/migrations/functions/backfill/tests/app.spec.ts b/packages/server/src/migrations/functions/backfill/tests/app.spec.ts deleted file mode 100644 index a8caebff1c..0000000000 --- a/packages/server/src/migrations/functions/backfill/tests/app.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { tenancy, events } from "@budibase/backend-core" -import TestConfig from "../../../../tests/utilities/TestConfiguration" -import { backfill } from "../app/app" - -describe("app backfill", () => { - const config = new TestConfig() - - beforeEach(async () => { - await config.init() - jest.clearAllMocks() - }) - - afterEach(() => { - config.end() - }) - - it("should backfill dev app", async () => { - await config.doInContext(null, async () => { - const db = tenancy.getAppDB({}) - - await backfill(db) - - expect(events.app.created).toBeCalledTimes(1) - expect(events.app.published).toBeCalledTimes(0) - }) - }) - - it("should backfill prod app", async () => { - await config.doInContext( - null, - async () => { - const db = tenancy.getAppDB({}) - - await backfill(db) - - // expect(events.app.created).toBeCalledTimes(0) - expect(events.app.published).toBeCalledTimes(1) - }, - { prod: true } - ) - }) -}) diff --git a/packages/server/src/migrations/tests/index.spec.ts b/packages/server/src/migrations/tests/index.spec.ts new file mode 100644 index 0000000000..9a01778e29 --- /dev/null +++ b/packages/server/src/migrations/tests/index.spec.ts @@ -0,0 +1,52 @@ +import { events, migrations } from "@budibase/backend-core" +import TestConfig from "../../tests/utilities/TestConfiguration" +import structures from "../../tests/utilities/structures" +import { MIGRATIONS } from "../" + +jest.setTimeout(100000) + +describe("migrations", () => { + const config = new TestConfig() + + beforeAll(async () => { + await config.init() + }) + + afterAll(() => { + config.end() + }) + + describe("backfill", () => { + it("runs app db migration", async () => { + await config.doInContext(null, async () => { + await config.createAutomation() + await config.createAutomation(structures.newAutomation()) + await config.createDatasource() + await config.createDatasource() + await config.createLayout() + await config.createQuery() + await config.createQuery() + await config.createRole() + await config.createRole() + await config.createTable() + await config.createTable() + + jest.clearAllMocks() + const migration = MIGRATIONS.filter( + m => m.name === "event_app_backfill" + )[0] + await migrations.runMigration(migration) + + expect(events.app.created).toBeCalledTimes(1) + expect(events.app.published).toBeCalledTimes(1) + expect(events.automation.created).toBeCalledTimes(2) + expect(events.automation.stepCreated).toBeCalledTimes(1) + expect(events.datasource.created).toBeCalledTimes(2) + expect(events.layout.created).toBeCalledTimes(3) + expect(events.query.created).toBeCalledTimes(2) + expect(events.role.created).toBeCalledTimes(2) + expect(events.table.created).toBeCalledTimes(3) + }) + }) + }) +}) diff --git a/packages/types/src/documents/app/automation.ts b/packages/types/src/documents/app/automation.ts index 80a2afa543..4dd64835c6 100644 --- a/packages/types/src/documents/app/automation.ts +++ b/packages/types/src/documents/app/automation.ts @@ -1,4 +1,9 @@ -export interface Automation {} +export interface Automation { + definition: { + steps: AutomationStep[] + trigger: AutomationTrigger + } +} export interface AutomationStep {} diff --git a/packages/types/src/documents/app/index.ts b/packages/types/src/documents/app/index.ts index c4d55173dc..26f498979d 100644 --- a/packages/types/src/documents/app/index.ts +++ b/packages/types/src/documents/app/index.ts @@ -9,3 +9,4 @@ export * from "./screen" export * from "./view" export * from "./document" export * from "./row" +export * from "./user" diff --git a/packages/types/src/documents/app/query.ts b/packages/types/src/documents/app/query.ts index ba038af326..7edda3eba8 100644 --- a/packages/types/src/documents/app/query.ts +++ b/packages/types/src/documents/app/query.ts @@ -1 +1,3 @@ -export interface Query {} +export interface Query { + datasourceId: string +} diff --git a/packages/types/src/documents/app/role.ts b/packages/types/src/documents/app/role.ts index 304e11b799..292ab71806 100644 --- a/packages/types/src/documents/app/role.ts +++ b/packages/types/src/documents/app/role.ts @@ -1 +1,3 @@ -export interface Role {} +import { Document } from "./document" + +export interface Role extends Document {} diff --git a/packages/types/src/documents/app/user.ts b/packages/types/src/documents/app/user.ts new file mode 100644 index 0000000000..233da6c454 --- /dev/null +++ b/packages/types/src/documents/app/user.ts @@ -0,0 +1,3 @@ +export interface UserMetadata { + roleId: string +} diff --git a/packages/types/src/documents/global/user.ts b/packages/types/src/documents/global/user.ts index e918b910d0..43592582c7 100644 --- a/packages/types/src/documents/global/user.ts +++ b/packages/types/src/documents/global/user.ts @@ -1 +1,7 @@ -export interface User {} +export interface User { + roles: UserRoles +} + +export interface UserRoles { + [key: string]: string +} diff --git a/packages/worker/package.json b/packages/worker/package.json index 02609f081e..10f69d2fc5 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -64,6 +64,7 @@ "server-destroy": "^1.0.1" }, "devDependencies": { + "@budibase/types": "^1.0.126-alpha.0", "@types/jest": "^26.0.23", "@types/koa": "^2.13.3", "@types/koa-router": "^7.4.2", diff --git a/packages/worker/src/sdk/users/events.ts b/packages/worker/src/sdk/users/events.ts index 98fb68efda..76108f6853 100644 --- a/packages/worker/src/sdk/users/events.ts +++ b/packages/worker/src/sdk/users/events.ts @@ -1,4 +1,5 @@ -const { events } = require("@budibase/backend-core") +import { events } from "@budibase/backend-core" +import { User, UserRoles } from "@budibase/types" export const handleDeleteEvents = (user: any) => { events.user.deleted(user) @@ -12,23 +13,31 @@ export const handleDeleteEvents = (user: any) => { } } -const assignAppRoleEvents = (roles: any, existingRoles: any) => { +const assignAppRoleEvents = ( + user: User, + roles: UserRoles, + existingRoles: UserRoles +) => { for (const [appId, role] of Object.entries(roles)) { // app role in existing is not same as new if (!existingRoles || existingRoles[appId] !== role) { - events.role.assigned(role) + events.role.assigned(user, role) } } } -const unassignAppRoleEvents = (roles: any, existingRoles: any) => { +const unassignAppRoleEvents = ( + user: User, + roles: UserRoles, + existingRoles: UserRoles +) => { if (!existingRoles) { return } for (const [appId, role] of Object.entries(existingRoles)) { // app role in new is not same as existing if (!roles || roles[appId] !== role) { - events.role.unassigned(role) + events.role.unassigned(user, role) } } } @@ -37,8 +46,8 @@ const handleAppRoleEvents = (user: any, existingUser: any) => { const roles = user.roles const existingRoles = existingUser?.roles - assignAppRoleEvents(roles, existingRoles) - unassignAppRoleEvents(roles, existingRoles) + assignAppRoleEvents(user, roles, existingRoles) + unassignAppRoleEvents(user, roles, existingRoles) } export const handleSaveEvents = (user: any, existingUser: any) => { From 756f5b51aac3a91a74a54891db5a40d9e63ef8af Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Fri, 20 May 2022 12:29:31 +0100 Subject: [PATCH 38/75] App migrations finished --- .../src/events/publishers/view.ts | 3 +- .../src/tests/utilities/mocks/events.js | 2 +- .../src/api/controllers/query/import/index.ts | 4 +- .../query/import/sources/base/index.ts | 2 +- .../server/src/api/routes/tests/table.spec.js | 4 +- .../functions/backfill/{app => }/account.ts | 0 .../src/migrations/functions/backfill/app.ts | 2 + .../migrations/functions/backfill/app/rows.ts | 8 ---- .../functions/backfill/app/screens.ts | 23 +++++++++- .../functions/backfill/app/tables.ts | 14 ++++++ .../migrations/functions/backfill/app/view.ts | 3 -- .../functions/backfill/global/rows.ts | 14 ++++++ .../server/src/migrations/tests/index.spec.ts | 8 ++++ .../src/tests/utilities/TestConfiguration.js | 1 - .../server/src/tests/utilities/structures.js | 35 +++++++++++++++ .../types/src/documents/app/automation.ts | 4 +- .../types/src/documents/app/datasource.ts | 4 +- packages/types/src/documents/app/document.ts | 6 +-- packages/types/src/documents/app/layout.ts | 4 +- packages/types/src/documents/app/query.ts | 43 +++++++++++++++++- packages/types/src/documents/app/row.ts | 4 +- packages/types/src/documents/app/screen.ts | 4 +- packages/types/src/documents/app/table.ts | 7 ++- packages/types/src/documents/app/user.ts | 4 +- packages/types/src/documents/app/view.ts | 44 ++++++++++++++++++- .../worker/src/api/routes/tests/users.spec.js | 20 +++++---- packages/worker/src/sdk/users/users.ts | 1 + 27 files changed, 228 insertions(+), 40 deletions(-) rename packages/server/src/migrations/functions/backfill/{app => }/account.ts (100%) delete mode 100644 packages/server/src/migrations/functions/backfill/app/rows.ts delete mode 100644 packages/server/src/migrations/functions/backfill/app/view.ts create mode 100644 packages/server/src/migrations/functions/backfill/global/rows.ts diff --git a/packages/backend-core/src/events/publishers/view.ts b/packages/backend-core/src/events/publishers/view.ts index 4091fce18e..e9c594e8c9 100644 --- a/packages/backend-core/src/events/publishers/view.ts +++ b/packages/backend-core/src/events/publishers/view.ts @@ -12,6 +12,7 @@ import { ViewFilterUpdatedEvent, ViewUpdatedEvent, View, + ViewCalculation, Table, TableExportFormat, } from "@budibase/types" @@ -53,7 +54,7 @@ export function filterDeleted() { publishEvent(Event.VIEW_FILTER_DELETED, properties) } -export function calculationCreated() { +export function calculationCreated(calculation: ViewCalculation) { const properties: ViewCalculationCreatedEvent = {} publishEvent(Event.VIEW_CALCULATION_CREATED, properties) } diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index b752d946c2..df55bedd37 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -69,7 +69,7 @@ jest.mock("../../../events", () => { assigned: jest.fn(), unassigned: jest.fn(), }, - row: { + rows: { imported: jest.fn(), }, screen: { diff --git a/packages/server/src/api/controllers/query/import/index.ts b/packages/server/src/api/controllers/query/import/index.ts index b8f11a65aa..eb70a33b2f 100644 --- a/packages/server/src/api/controllers/query/import/index.ts +++ b/packages/server/src/api/controllers/query/import/index.ts @@ -3,11 +3,11 @@ import { generateQueryID } from "../../../../db/utils" import { ImportInfo, ImportSource } from "./sources/base" import { OpenAPI2 } from "./sources/openapi2" import { OpenAPI3 } from "./sources/openapi3" -import { Query } from "./../../../../definitions/common" import { Curl } from "./sources/curl" // @ts-ignore import { getAppDB } from "@budibase/backend-core/context" import { events } from "@budibase/backend-core" +import { Datasource, Query } from "@budibase/types" interface ImportResult { errorQueries: Query[] @@ -83,7 +83,7 @@ export class RestImporter { // events const count = successQueries.length const importSource = this.source.getImportSource() - const datasource = await db.get(datasourceId) + const datasource: Datasource = await db.get(datasourceId) events.query.imported(datasource, importSource, count) for (let query of successQueries) { events.query.created(datasource, query) diff --git a/packages/server/src/api/controllers/query/import/sources/base/index.ts b/packages/server/src/api/controllers/query/import/sources/base/index.ts index 28737b9731..c95db6a8b3 100644 --- a/packages/server/src/api/controllers/query/import/sources/base/index.ts +++ b/packages/server/src/api/controllers/query/import/sources/base/index.ts @@ -1,4 +1,4 @@ -import { Query, QueryParameter } from "../../../../../../definitions/datasource" +import { Query, QueryParameter } from "@budibase/types" import { URL } from "url" export interface ImportInfo { diff --git a/packages/server/src/api/routes/tests/table.spec.js b/packages/server/src/api/routes/tests/table.spec.js index 662bce141d..7a7ca797cd 100644 --- a/packages/server/src/api/routes/tests/table.spec.js +++ b/packages/server/src/api/routes/tests/table.spec.js @@ -55,8 +55,8 @@ describe("/tables", () => { expect(events.table.created).toBeCalledWith(res.body) expect(events.table.imported).toBeCalledTimes(1) expect(events.table.imported).toBeCalledWith(res.body, "csv") - expect(events.row.imported).toBeCalledTimes(1) - expect(events.row.imported).toBeCalledWith(res.body, "csv", 1) + expect(events.rowsimported).toBeCalledTimes(1) + expect(events.rows.imported).toBeCalledWith(res.body, "csv", 1) }) it("should apply authorization to endpoint", async () => { diff --git a/packages/server/src/migrations/functions/backfill/app/account.ts b/packages/server/src/migrations/functions/backfill/account.ts similarity index 100% rename from packages/server/src/migrations/functions/backfill/app/account.ts rename to packages/server/src/migrations/functions/backfill/account.ts diff --git a/packages/server/src/migrations/functions/backfill/app.ts b/packages/server/src/migrations/functions/backfill/app.ts index 4b2d20b008..ba8d889343 100644 --- a/packages/server/src/migrations/functions/backfill/app.ts +++ b/packages/server/src/migrations/functions/backfill/app.ts @@ -5,6 +5,7 @@ import * as layouts from "./app/layouts" import * as queries from "./app/queries" import * as roles from "./app/roles" import * as tables from "./app/tables" +import * as screens from "./app/screens" /** * Date: @@ -22,4 +23,5 @@ export const run = async (appDb: any) => { await queries.backfill(appDb) await roles.backfill(appDb) await tables.backfill(appDb) + await screens.backfill(appDb) } diff --git a/packages/server/src/migrations/functions/backfill/app/rows.ts b/packages/server/src/migrations/functions/backfill/app/rows.ts deleted file mode 100644 index c4eb6f2972..0000000000 --- a/packages/server/src/migrations/functions/backfill/app/rows.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { events } from "@budibase/backend-core" -import { Row } from "@budibase/types" - -export const backfill = async (appDb: any) => { - const rows: Row[] = [] - const count = rows.length - events.rows.created(count) -} diff --git a/packages/server/src/migrations/functions/backfill/app/screens.ts b/packages/server/src/migrations/functions/backfill/app/screens.ts index d000321625..d2fe0bd99b 100644 --- a/packages/server/src/migrations/functions/backfill/app/screens.ts +++ b/packages/server/src/migrations/functions/backfill/app/screens.ts @@ -1 +1,22 @@ -// SCREEN_CREATED = "screen:created", +import { events, db } from "@budibase/backend-core" +import { getScreenParams } from "../../../../db/utils" +import { Screen } from "@budibase/types" + +const getScreens = async (appDb: any): Promise => { + const response = await appDb.allDocs( + getScreenParams(null, { + include_docs: true, + }) + ) + return response.rows.map((row: any) => row.doc) +} + +export const backfill = async (appDb: any) => { + if (db.isDevAppID(appDb.name)) { + const screens = await getScreens(appDb) + + for (const screen of screens) { + events.screen.created(screen) + } + } +} diff --git a/packages/server/src/migrations/functions/backfill/app/tables.ts b/packages/server/src/migrations/functions/backfill/app/tables.ts index 233474ffbd..4231875190 100644 --- a/packages/server/src/migrations/functions/backfill/app/tables.ts +++ b/packages/server/src/migrations/functions/backfill/app/tables.ts @@ -17,6 +17,20 @@ export const backfill = async (appDb: any) => { for (const table of tables) { events.table.created(table) + + if (table.views) { + for (const view of Object.values(table.views)) { + events.view.created(view) + + if (view.calculation) { + events.view.calculationCreated(view.calculation) + } + + if (view.filters?.length) { + events.view.filterCreated() + } + } + } } } } diff --git a/packages/server/src/migrations/functions/backfill/app/view.ts b/packages/server/src/migrations/functions/backfill/app/view.ts deleted file mode 100644 index f9f4544f0d..0000000000 --- a/packages/server/src/migrations/functions/backfill/app/view.ts +++ /dev/null @@ -1,3 +0,0 @@ -// VIEW_CREATED = "view:created", -// VIEW_FILTER_CREATED = "view:filter:created", -// VIEW_CALCULATION_CREATED = "view:calculation:created", diff --git a/packages/server/src/migrations/functions/backfill/global/rows.ts b/packages/server/src/migrations/functions/backfill/global/rows.ts new file mode 100644 index 0000000000..07cc1cdaf8 --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/global/rows.ts @@ -0,0 +1,14 @@ +import { events, db } from "@budibase/backend-core" +import { Row } from "@budibase/types" +import { getUniqueRows } from "../../../../utilities/usageQuota/rows" + +// Rows is a special circumstance where we get rows across all apps +// therefore migration is performed at the global level + +export const backfill = async () => { + const allApps = await db.getAllApps({ all: true }) + const appIds = allApps ? allApps.map((app: { appId: any }) => app.appId) : [] + const rows: Row[] = await getUniqueRows(appIds) + const rowCount = rows ? rows.length : 0 + events.rows.created(rowCount) +} diff --git a/packages/server/src/migrations/tests/index.spec.ts b/packages/server/src/migrations/tests/index.spec.ts index 9a01778e29..dd5f130ae4 100644 --- a/packages/server/src/migrations/tests/index.spec.ts +++ b/packages/server/src/migrations/tests/index.spec.ts @@ -29,7 +29,11 @@ describe("migrations", () => { await config.createRole() await config.createRole() await config.createTable() + await config.createView() await config.createTable() + await config.createView(structures.view(config.table._id)) + await config.createScreen() + await config.createScreen() jest.clearAllMocks() const migration = MIGRATIONS.filter( @@ -46,6 +50,10 @@ describe("migrations", () => { expect(events.query.created).toBeCalledTimes(2) expect(events.role.created).toBeCalledTimes(2) expect(events.table.created).toBeCalledTimes(3) + expect(events.view.created).toBeCalledTimes(2) + expect(events.view.calculationCreated).toBeCalledTimes(1) + expect(events.view.filterCreated).toBeCalledTimes(1) + expect(events.screen.created).toBeCalledTimes(2) }) }) }) diff --git a/packages/server/src/tests/utilities/TestConfiguration.js b/packages/server/src/tests/utilities/TestConfiguration.js index be883fb7f6..8155a27940 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.js +++ b/packages/server/src/tests/utilities/TestConfiguration.js @@ -409,7 +409,6 @@ class TestConfiguration { throw "Test requires table to be configured." } const view = config || { - map: "function(doc) { emit(doc[doc.key], doc._id); } ", tableId: this.table._id, name: "ViewTest", } diff --git a/packages/server/src/tests/utilities/structures.js b/packages/server/src/tests/utilities/structures.js index 4cb6c3b141..c4bd6fc774 100644 --- a/packages/server/src/tests/utilities/structures.js +++ b/packages/server/src/tests/utilities/structures.js @@ -30,6 +30,41 @@ exports.basicTable = () => { } } +exports.basicView = tableId => { + return { + tableId, + name: "ViewTest", + } +} + +exports.filterView = tableId => { + return { + ...this.basicView(tableId), + filters: [ + { + value: 0, + condition: "MT", + key: "count", + }, + ], + } +} + +exports.calculationView = tableId => { + return { + ...this.basicView(tableId), + field: "count", + calculation: "sum", + } +} + +exports.view = tableId => { + return { + ...this.filterView(tableId), + ...this.calculationView(tableId), + } +} + exports.automationStep = (actionDefinition = ACTION_DEFINITIONS.CREATE_ROW) => { return { id: uuidv4(), diff --git a/packages/types/src/documents/app/automation.ts b/packages/types/src/documents/app/automation.ts index 4dd64835c6..de584581b0 100644 --- a/packages/types/src/documents/app/automation.ts +++ b/packages/types/src/documents/app/automation.ts @@ -1,4 +1,6 @@ -export interface Automation { +import { Document } from "./document" + +export interface Automation extends Document { definition: { steps: AutomationStep[] trigger: AutomationTrigger diff --git a/packages/types/src/documents/app/datasource.ts b/packages/types/src/documents/app/datasource.ts index 03e8ce7fe8..1e3a712efa 100644 --- a/packages/types/src/documents/app/datasource.ts +++ b/packages/types/src/documents/app/datasource.ts @@ -1 +1,3 @@ -export interface Datasource {} +import { Document } from "./document" + +export interface Datasource extends Document {} diff --git a/packages/types/src/documents/app/document.ts b/packages/types/src/documents/app/document.ts index 5808bd91e6..b1b41394f4 100644 --- a/packages/types/src/documents/app/document.ts +++ b/packages/types/src/documents/app/document.ts @@ -1,6 +1,6 @@ export interface Document { - _id: string + _id?: string _rev?: string - createdAt: string - updatedAt: string + createdAt?: string + updatedAt?: string } diff --git a/packages/types/src/documents/app/layout.ts b/packages/types/src/documents/app/layout.ts index 4f0f85bd87..de7b61ed97 100644 --- a/packages/types/src/documents/app/layout.ts +++ b/packages/types/src/documents/app/layout.ts @@ -1 +1,3 @@ -export interface Layout {} +import { Document } from "./document" + +export interface Layout extends Document {} diff --git a/packages/types/src/documents/app/query.ts b/packages/types/src/documents/app/query.ts index 7edda3eba8..6cdda0306f 100644 --- a/packages/types/src/documents/app/query.ts +++ b/packages/types/src/documents/app/query.ts @@ -1,3 +1,44 @@ -export interface Query { +import { Document } from "./document" + +export interface Query extends Document { datasourceId: string + name: string + parameters: QueryParameter[] + fields: RestQueryFields | any + transformer: string | null + schema: any + readable: boolean + queryVerb: string +} + +export interface QueryParameter { + name: string + default: string +} + +export interface RestQueryFields { + path: string + queryString?: string + headers: { [key: string]: any } + disabledHeaders: { [key: string]: any } + requestBody: any + bodyType: string + json: object + method: string + authConfigId: string + pagination: PaginationConfig | null + paginationValues: PaginationValues | null +} + +export interface PaginationConfig { + type: string + location: string + pageParam: string + sizeParam: string | null + responseParam: string | null +} + +export interface PaginationValues { + page: string | number | null + limit: number | null } diff --git a/packages/types/src/documents/app/row.ts b/packages/types/src/documents/app/row.ts index 3fb0cb00a4..d921beae24 100644 --- a/packages/types/src/documents/app/row.ts +++ b/packages/types/src/documents/app/row.ts @@ -1 +1,3 @@ -export interface Row {} +import { Document } from "./document" + +export interface Row extends Document {} diff --git a/packages/types/src/documents/app/screen.ts b/packages/types/src/documents/app/screen.ts index 1a88fe8db3..ab6e76eaa0 100644 --- a/packages/types/src/documents/app/screen.ts +++ b/packages/types/src/documents/app/screen.ts @@ -1 +1,3 @@ -export interface Screen {} +import { Document } from "./document" + +export interface Screen extends Document {} diff --git a/packages/types/src/documents/app/table.ts b/packages/types/src/documents/app/table.ts index 74923f6554..1b3a523d26 100644 --- a/packages/types/src/documents/app/table.ts +++ b/packages/types/src/documents/app/table.ts @@ -1 +1,6 @@ -export interface Table {} +import { Document } from "./document" +import { View } from "./view" + +export interface Table extends Document { + views: { [key: string]: View } +} diff --git a/packages/types/src/documents/app/user.ts b/packages/types/src/documents/app/user.ts index 233da6c454..a3fcc8ece3 100644 --- a/packages/types/src/documents/app/user.ts +++ b/packages/types/src/documents/app/user.ts @@ -1,3 +1,5 @@ -export interface UserMetadata { +import { Document } from "./document" + +export interface UserMetadata extends Document { roleId: string } diff --git a/packages/types/src/documents/app/view.ts b/packages/types/src/documents/app/view.ts index 038e718355..04566ee100 100644 --- a/packages/types/src/documents/app/view.ts +++ b/packages/types/src/documents/app/view.ts @@ -1 +1,43 @@ -export interface View {} +export interface View { + name: string + tableId: string + field?: string + filters: ViewFilter[] + schema: ViewSchema + calculation?: ViewCalculation +} + +export type ViewSchema = ViewCountOrSumSchema | ViewStatisticsSchema + +export interface ViewCountOrSumSchema { + field: string + value: string +} + +/** + e.g: + "min": { + "type": "number" + }, + "max": { + "type": "number" + } + */ +export interface ViewStatisticsSchema { + [key: string]: { + type: string + } +} + +export interface ViewFilter { + value: any + condition: string + key: string + conjunction?: string +} + +export enum ViewCalculation { + SUM = "sum", + COUNT = "count", + STATISTICS = "stats", +} diff --git a/packages/worker/src/api/routes/tests/users.spec.js b/packages/worker/src/api/routes/tests/users.spec.js index cf4426665b..6a22156976 100644 --- a/packages/worker/src/api/routes/tests/users.spec.js +++ b/packages/worker/src/api/routes/tests/users.spec.js @@ -139,12 +139,13 @@ describe("/api/global/users", () => { } await createUser(user) + const savedUser = await config.getUser(user.email) expect(events.user.created).toBeCalledTimes(1) expect(events.user.updated).not.toBeCalled() expect(events.role.assigned).toBeCalledTimes(2) - expect(events.role.assigned).toBeCalledWith("role1") - expect(events.role.assigned).toBeCalledWith("role2") + expect(events.role.assigned).toBeCalledWith(savedUser, "role1") + expect(events.role.assigned).toBeCalledWith(savedUser, "role2") }) }) @@ -243,12 +244,13 @@ describe("/api/global/users", () => { "app_456": "role2", } await updateUser(user) + const savedUser = await config.getUser(user.email) expect(events.user.created).not.toBeCalled() expect(events.user.updated).toBeCalledTimes(1) expect(events.role.assigned).toBeCalledTimes(2) - expect(events.role.assigned).toBeCalledWith("role1") - expect(events.role.assigned).toBeCalledWith("role2") + expect(events.role.assigned).toBeCalledWith(savedUser, "role1") + expect(events.role.assigned).toBeCalledWith(savedUser, "role2") }) it("should be able to unassign app roles", async () => { @@ -262,12 +264,13 @@ describe("/api/global/users", () => { user.roles = {} await updateUser(user) + const savedUser = await config.getUser(user.email) expect(events.user.created).not.toBeCalled() expect(events.user.updated).toBeCalledTimes(1) expect(events.role.unassigned).toBeCalledTimes(2) - expect(events.role.unassigned).toBeCalledWith("role1") - expect(events.role.unassigned).toBeCalledWith("role2") + expect(events.role.unassigned).toBeCalledWith(savedUser, "role1") + expect(events.role.unassigned).toBeCalledWith(savedUser, "role2") }) it("should be able to update existing app roles", async () => { @@ -284,13 +287,14 @@ describe("/api/global/users", () => { "app_456": "role2-edit", } await updateUser(user) + const savedUser = await config.getUser(user.email) expect(events.user.created).not.toBeCalled() expect(events.user.updated).toBeCalledTimes(1) expect(events.role.unassigned).toBeCalledTimes(1) - expect(events.role.unassigned).toBeCalledWith("role2") + expect(events.role.unassigned).toBeCalledWith(savedUser, "role2") expect(events.role.assigned).toBeCalledTimes(1) - expect(events.role.assigned).toBeCalledWith("role2-edit") + expect(events.role.assigned).toBeCalledWith(savedUser, "role2-edit") }) }) diff --git a/packages/worker/src/sdk/users/users.ts b/packages/worker/src/sdk/users/users.ts index ef22fe88ba..b514392350 100644 --- a/packages/worker/src/sdk/users/users.ts +++ b/packages/worker/src/sdk/users/users.ts @@ -127,6 +127,7 @@ export const save = async ( } else { response = await putUserFn() } + user._rev = response.rev eventHelpers.handleSaveEvents(user, dbUser) From 443b664e33d18632e5bfb6f1f45c75832334225e Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Fri, 20 May 2022 21:16:29 +0100 Subject: [PATCH 39/75] Finish global migration --- .../src/tests/utilities/mocks/events.js | 1 + .../migrations/functions/backfill/global.ts | 10 ++- .../functions/backfill/global/config.ts | 7 -- .../functions/backfill/global/configs.ts | 63 +++++++++++++++++ .../functions/backfill/global/rows.ts | 4 +- .../functions/backfill/global/user.ts | 3 - .../functions/backfill/global/users.ts | 38 +++++++++++ .../server/src/migrations/tests/helpers.ts | 30 ++++++++ .../server/src/migrations/tests/index.spec.ts | 38 ++++++++++- .../server/src/migrations/tests/structures.ts | 66 ++++++++++++++++++ .../src/tests/utilities/TestConfiguration.js | 28 +++++--- packages/types/src/documents/app/app.ts | 2 +- .../types/src/documents/app/automation.ts | 2 +- .../types/src/documents/app/datasource.ts | 2 +- packages/types/src/documents/app/index.ts | 2 +- packages/types/src/documents/app/layout.ts | 2 +- packages/types/src/documents/app/query.ts | 2 +- packages/types/src/documents/app/role.ts | 2 +- packages/types/src/documents/app/row.ts | 2 +- packages/types/src/documents/app/screen.ts | 2 +- packages/types/src/documents/app/table.ts | 2 +- packages/types/src/documents/app/user.ts | 2 +- .../types/src/documents/{app => }/document.ts | 0 packages/types/src/documents/global/config.ts | 68 ++++++++++++++++++- packages/types/src/documents/global/user.ts | 10 ++- 25 files changed, 354 insertions(+), 34 deletions(-) delete mode 100644 packages/server/src/migrations/functions/backfill/global/config.ts create mode 100644 packages/server/src/migrations/functions/backfill/global/configs.ts delete mode 100644 packages/server/src/migrations/functions/backfill/global/user.ts create mode 100644 packages/server/src/migrations/functions/backfill/global/users.ts create mode 100644 packages/server/src/migrations/tests/helpers.ts create mode 100644 packages/server/src/migrations/tests/structures.ts rename packages/types/src/documents/{app => }/document.ts (100%) diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index df55bedd37..650798f23e 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -71,6 +71,7 @@ jest.mock("../../../events", () => { }, rows: { imported: jest.fn(), + created: jest.fn(), }, screen: { created: jest.fn(), diff --git a/packages/server/src/migrations/functions/backfill/global.ts b/packages/server/src/migrations/functions/backfill/global.ts index b28cf48d99..a723e03a75 100644 --- a/packages/server/src/migrations/functions/backfill/global.ts +++ b/packages/server/src/migrations/functions/backfill/global.ts @@ -1,3 +1,7 @@ +import * as users from "./global/users" +import * as rows from "./global/rows" +import * as configs from "./global/configs" + /** * Date: * May 2022 @@ -6,4 +10,8 @@ * Backfill global events. */ -export const run = async (db: any) => {} +export const run = async (db: any) => { + await users.backfill(db) + await rows.backfill() + await configs.backfill(db) +} diff --git a/packages/server/src/migrations/functions/backfill/global/config.ts b/packages/server/src/migrations/functions/backfill/global/config.ts deleted file mode 100644 index 2c058952ca..0000000000 --- a/packages/server/src/migrations/functions/backfill/global/config.ts +++ /dev/null @@ -1,7 +0,0 @@ -// EMAIL_SMTP_CREATED = "email:smtp:created", -// AUTH_SSO_CREATED = "auth:sso:created", -// AUTH_SSO_ACTIVATED = "auth:sso:activated", -// AUTH_SSO_DEACTIVATED = "auth:sso:deactivated", -// ORG_NAME_UPDATED = "org:info:name:updated", -// ORG_LOGO_UPDATED = "org:info:logo:updated", -// ORG_PLATFORM_URL_UPDATED = "org:platformurl:updated", diff --git a/packages/server/src/migrations/functions/backfill/global/configs.ts b/packages/server/src/migrations/functions/backfill/global/configs.ts new file mode 100644 index 0000000000..7b04cbc05f --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/global/configs.ts @@ -0,0 +1,63 @@ +import { events, db } from "@budibase/backend-core" +import { + Config, + isSMTPConfig, + isGoogleConfig, + isOIDCConfig, + isSettingsConfig, +} from "@budibase/types" +import env from "./../../../../environment" + +const getConfigs = async (globalDb: any): Promise => { + const response = await globalDb.allDocs( + db.getConfigParams( + {}, + { + include_docs: true, + } + ) + ) + return response.rows.map((row: any) => row.doc) +} + +export const backfill = async (globalDb: any) => { + const configs = await getConfigs(globalDb) + + for (const config of configs) { + if (isSMTPConfig(config)) { + events.email.SMTPCreated(config) + } + if (isGoogleConfig(config)) { + events.auth.SSOCreated("google") + if (config.config.activated) { + events.auth.SSOActivated("google") + } + } + if (isOIDCConfig(config)) { + events.auth.SSOCreated("oidc") + if (config.config.configs[0].activated) { + events.auth.SSOActivated("oidc") + } + } + if (isSettingsConfig(config)) { + const company = config.config.company + if (company && company !== "Budibase") { + events.org.nameUpdated() + } + + const logoUrl = config.config.logoUrl + if (logoUrl) { + events.org.logoUpdated() + } + + const platformUrl = config.config.platformUrl + if ( + platformUrl && + platformUrl !== "http://localhost:10000" && + env.SELF_HOSTED + ) { + events.org.platformURLUpdated() + } + } + } +} diff --git a/packages/server/src/migrations/functions/backfill/global/rows.ts b/packages/server/src/migrations/functions/backfill/global/rows.ts index 07cc1cdaf8..593b0516d0 100644 --- a/packages/server/src/migrations/functions/backfill/global/rows.ts +++ b/packages/server/src/migrations/functions/backfill/global/rows.ts @@ -10,5 +10,7 @@ export const backfill = async () => { const appIds = allApps ? allApps.map((app: { appId: any }) => app.appId) : [] const rows: Row[] = await getUniqueRows(appIds) const rowCount = rows ? rows.length : 0 - events.rows.created(rowCount) + if (rowCount) { + events.rows.created(rowCount) + } } diff --git a/packages/server/src/migrations/functions/backfill/global/user.ts b/packages/server/src/migrations/functions/backfill/global/user.ts deleted file mode 100644 index 52eadeb3be..0000000000 --- a/packages/server/src/migrations/functions/backfill/global/user.ts +++ /dev/null @@ -1,3 +0,0 @@ -// USER_CREATED = "user:created", -// USER_PERMISSION_ADMIN_ASSIGNED = "user:admin:assigned", -// USER_PERMISSION_BUILDER_ASSIGNED = "user:builder:assigned", diff --git a/packages/server/src/migrations/functions/backfill/global/users.ts b/packages/server/src/migrations/functions/backfill/global/users.ts new file mode 100644 index 0000000000..a3c8ce52cc --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/global/users.ts @@ -0,0 +1,38 @@ +import { events, db } from "@budibase/backend-core" +import { User } from "@budibase/types" + +// manually define user doc params - normally server doesn't read users from the db +const getUserParams = (props: any) => { + return db.getDocParams(db.DocumentTypes.USER, null, props) +} + +const getUsers = async (globalDb: any): Promise => { + const response = await globalDb.allDocs( + getUserParams({ + include_docs: true, + }) + ) + return response.rows.map((row: any) => row.doc) +} + +export const backfill = async (globalDb: any) => { + const users = await getUsers(globalDb) + + for (const user of users) { + events.user.created(user) + + if (user.admin?.global) { + events.user.permissionAdminAssigned(user) + } + + if (user.builder?.global) { + events.user.permissionBuilderAssigned(user) + } + + if (user.roles) { + for (const [appId, role] of Object.entries(user.roles)) { + events.role.assigned(user, role) + } + } + } +} diff --git a/packages/server/src/migrations/tests/helpers.ts b/packages/server/src/migrations/tests/helpers.ts new file mode 100644 index 0000000000..a2e45fbb26 --- /dev/null +++ b/packages/server/src/migrations/tests/helpers.ts @@ -0,0 +1,30 @@ +// Mimic configs test configuration from worker, creation configs directly in database + +import * as structures from "./structures" +import { db } from "@budibase/backend-core" +import { Config } from "@budibase/types" + +export const saveSettingsConfig = async (globalDb: any) => { + const config = structures.settings() + await saveConfig(config, globalDb) +} + +export const saveGoogleConfig = async (globalDb: any) => { + const config = structures.google() + await saveConfig(config, globalDb) +} + +export const saveOIDCConfig = async (globalDb: any) => { + const config = structures.oidc() + await saveConfig(config, globalDb) +} + +export const saveSmtpConfig = async (globalDb: any) => { + const config = structures.smtp() + await saveConfig(config, globalDb) +} + +const saveConfig = async (config: Config, globalDb: any) => { + config._id = db.generateConfigID({ type: config.type }) + await globalDb.put(config) +} diff --git a/packages/server/src/migrations/tests/index.spec.ts b/packages/server/src/migrations/tests/index.spec.ts index dd5f130ae4..908c67efdf 100644 --- a/packages/server/src/migrations/tests/index.spec.ts +++ b/packages/server/src/migrations/tests/index.spec.ts @@ -1,7 +1,8 @@ -import { events, migrations } from "@budibase/backend-core" +import { events, migrations, tenancy } from "@budibase/backend-core" import TestConfig from "../../tests/utilities/TestConfiguration" import structures from "../../tests/utilities/structures" import { MIGRATIONS } from "../" +import * as helpers from "./helpers" jest.setTimeout(100000) @@ -57,4 +58,39 @@ describe("migrations", () => { }) }) }) + + it("runs global db migration", async () => { + await config.doInContext(null, async () => { + await config.createUser(undefined, undefined, false, true) // admin only + await config.createUser(undefined, undefined, false, false) // non admin non builder + await config.createTable() + await config.createRow() + await config.createRow() + + const db = tenancy.getGlobalDB() + await helpers.saveGoogleConfig(db) + await helpers.saveOIDCConfig(db) + await helpers.saveSettingsConfig(db) + await helpers.saveSmtpConfig(db) + + jest.clearAllMocks() + const migration = MIGRATIONS.filter( + m => m.name === "event_global_backfill" + )[0] + await migrations.runMigration(migration) + + expect(events.user.created).toBeCalledTimes(3) + expect(events.role.assigned).toBeCalledTimes(2) + expect(events.user.permissionBuilderAssigned).toBeCalledTimes(1) // default test user + expect(events.user.permissionAdminAssigned).toBeCalledTimes(1) // admin from above + expect(events.rows.created).toBeCalledTimes(1) + expect(events.rows.created).toBeCalledWith(2) + expect(events.email.SMTPCreated).toBeCalledTimes(1) + expect(events.auth.SSOCreated).toBeCalledTimes(2) + expect(events.auth.SSOActivated).toBeCalledTimes(2) + expect(events.org.logoUpdated).toBeCalledTimes(1) + expect(events.org.nameUpdated).toBeCalledTimes(1) + expect(events.org.platformURLUpdated).toBeCalledTimes(1) + }) + }) }) diff --git a/packages/server/src/migrations/tests/structures.ts b/packages/server/src/migrations/tests/structures.ts new file mode 100644 index 0000000000..bd48bf63cd --- /dev/null +++ b/packages/server/src/migrations/tests/structures.ts @@ -0,0 +1,66 @@ +import { utils } from "@budibase/backend-core" +import { + SMTPConfig, + OIDCConfig, + GoogleConfig, + SettingsConfig, + ConfigType, +} from "@budibase/types" + +export const oidc = (conf?: OIDCConfig): OIDCConfig => { + return { + type: ConfigType.OIDC, + config: { + configs: [ + { + configUrl: "http://someconfigurl", + clientID: "clientId", + clientSecret: "clientSecret", + logo: "Microsoft", + name: "Active Directory", + uuid: utils.newid(), + activated: true, + ...conf, + }, + ], + }, + } +} + +export const google = (conf?: GoogleConfig): GoogleConfig => { + return { + type: ConfigType.GOOGLE, + config: { + clientID: "clientId", + clientSecret: "clientSecret", + activated: true, + ...conf, + }, + } +} + +export const smtp = (conf?: SMTPConfig): SMTPConfig => { + return { + type: ConfigType.SMTP, + config: { + port: 12345, + host: "smtptesthost.com", + from: "testfrom@test.com", + subject: "Hello!", + secure: false, + ...conf, + }, + } +} + +export const settings = (conf?: SettingsConfig): SettingsConfig => { + return { + type: ConfigType.SETTINGS, + config: { + platformUrl: "http://mycustomdomain.com", + logoUrl: "http://mylogourl,com", + company: "mycompany", + ...conf, + }, + } +} diff --git a/packages/server/src/tests/utilities/TestConfiguration.js b/packages/server/src/tests/utilities/TestConfiguration.js index 8155a27940..d1e7a9cac1 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.js +++ b/packages/server/src/tests/utilities/TestConfiguration.js @@ -121,6 +121,7 @@ class TestConfiguration { async globalUser({ id = GLOBAL_USER_ID, builder = true, + admin = false, email = EMAIL, roles, } = {}) { @@ -147,6 +148,11 @@ class TestConfiguration { } else { user.builder = { global: false } } + if (admin) { + user.admin = { global: true } + } else { + user.admin = { global: false } + } const resp = await db.put(user) return { _rev: resp._rev, @@ -155,9 +161,17 @@ class TestConfiguration { }) } - async createUser(id = null, email = EMAIL) { + async createUser(id = null, email = EMAIL, builder = true, admin = false) { const globalId = !id ? `us_${Math.random()}` : `us_${id}` - const resp = await this.globalUser({ id: globalId, email }) + const appId = this.prodAppId + const roles = { [appId]: "role_12345" } + const resp = await this.globalUser({ + id: globalId, + email, + builder, + admin, + roles, + }) await userCache.invalidateUser(globalId) return { ...resp, @@ -277,7 +291,7 @@ class TestConfiguration { // APP - async createApp(appName, opts = { deploy: true }) { + async createApp(appName) { // create dev app // clear any old app this.appId = null @@ -287,11 +301,9 @@ class TestConfiguration { await context.updateAppId(this.appId) // create production app - if (opts.deploy) { - this.prodApp = await this.deploy() - this.prodAppId = this.prodApp.appId - this.allApps.push(this.prodApp) - } + this.prodApp = await this.deploy() + this.prodAppId = this.prodApp.appId + this.allApps.push(this.prodApp) this.allApps.push(this.app) diff --git a/packages/types/src/documents/app/app.ts b/packages/types/src/documents/app/app.ts index 358c2afbf1..344c4e36d3 100644 --- a/packages/types/src/documents/app/app.ts +++ b/packages/types/src/documents/app/app.ts @@ -1,4 +1,4 @@ -import { Document } from "./document" +import { Document } from "../document" export interface App extends Document { appId: string diff --git a/packages/types/src/documents/app/automation.ts b/packages/types/src/documents/app/automation.ts index de584581b0..478ef0b7de 100644 --- a/packages/types/src/documents/app/automation.ts +++ b/packages/types/src/documents/app/automation.ts @@ -1,4 +1,4 @@ -import { Document } from "./document" +import { Document } from "../document" export interface Automation extends Document { definition: { diff --git a/packages/types/src/documents/app/datasource.ts b/packages/types/src/documents/app/datasource.ts index 1e3a712efa..63499cd02b 100644 --- a/packages/types/src/documents/app/datasource.ts +++ b/packages/types/src/documents/app/datasource.ts @@ -1,3 +1,3 @@ -import { Document } from "./document" +import { Document } from "../document" export interface Datasource extends Document {} diff --git a/packages/types/src/documents/app/index.ts b/packages/types/src/documents/app/index.ts index 26f498979d..e8b29257fc 100644 --- a/packages/types/src/documents/app/index.ts +++ b/packages/types/src/documents/app/index.ts @@ -7,6 +7,6 @@ export * from "./role" export * from "./table" export * from "./screen" export * from "./view" -export * from "./document" +export * from "../document" export * from "./row" export * from "./user" diff --git a/packages/types/src/documents/app/layout.ts b/packages/types/src/documents/app/layout.ts index de7b61ed97..85ca4b7e94 100644 --- a/packages/types/src/documents/app/layout.ts +++ b/packages/types/src/documents/app/layout.ts @@ -1,3 +1,3 @@ -import { Document } from "./document" +import { Document } from "../document" export interface Layout extends Document {} diff --git a/packages/types/src/documents/app/query.ts b/packages/types/src/documents/app/query.ts index 6cdda0306f..72b6c288a5 100644 --- a/packages/types/src/documents/app/query.ts +++ b/packages/types/src/documents/app/query.ts @@ -1,4 +1,4 @@ -import { Document } from "./document" +import { Document } from "../document" export interface Query extends Document { datasourceId: string diff --git a/packages/types/src/documents/app/role.ts b/packages/types/src/documents/app/role.ts index 292ab71806..0248fe733d 100644 --- a/packages/types/src/documents/app/role.ts +++ b/packages/types/src/documents/app/role.ts @@ -1,3 +1,3 @@ -import { Document } from "./document" +import { Document } from "../document" export interface Role extends Document {} diff --git a/packages/types/src/documents/app/row.ts b/packages/types/src/documents/app/row.ts index d921beae24..d053d3d938 100644 --- a/packages/types/src/documents/app/row.ts +++ b/packages/types/src/documents/app/row.ts @@ -1,3 +1,3 @@ -import { Document } from "./document" +import { Document } from "../document" export interface Row extends Document {} diff --git a/packages/types/src/documents/app/screen.ts b/packages/types/src/documents/app/screen.ts index ab6e76eaa0..01d3f02746 100644 --- a/packages/types/src/documents/app/screen.ts +++ b/packages/types/src/documents/app/screen.ts @@ -1,3 +1,3 @@ -import { Document } from "./document" +import { Document } from "../document" export interface Screen extends Document {} diff --git a/packages/types/src/documents/app/table.ts b/packages/types/src/documents/app/table.ts index 1b3a523d26..4ee7afd03c 100644 --- a/packages/types/src/documents/app/table.ts +++ b/packages/types/src/documents/app/table.ts @@ -1,4 +1,4 @@ -import { Document } from "./document" +import { Document } from "../document" import { View } from "./view" export interface Table extends Document { diff --git a/packages/types/src/documents/app/user.ts b/packages/types/src/documents/app/user.ts index a3fcc8ece3..b5f31ca349 100644 --- a/packages/types/src/documents/app/user.ts +++ b/packages/types/src/documents/app/user.ts @@ -1,4 +1,4 @@ -import { Document } from "./document" +import { Document } from "../document" export interface UserMetadata extends Document { roleId: string diff --git a/packages/types/src/documents/app/document.ts b/packages/types/src/documents/document.ts similarity index 100% rename from packages/types/src/documents/app/document.ts rename to packages/types/src/documents/document.ts diff --git a/packages/types/src/documents/global/config.ts b/packages/types/src/documents/global/config.ts index 6828cabc30..b37e09bb9d 100644 --- a/packages/types/src/documents/global/config.ts +++ b/packages/types/src/documents/global/config.ts @@ -1 +1,67 @@ -export interface SMTPConfig {} +import { Document } from "../document" + +export interface Config extends Document { + type: ConfigType +} + +export interface SMTPConfig extends Config { + config: { + port: number + host: string + from: string + subject: string + secure: boolean + } +} + +export interface SettingsConfig extends Config { + config: { + company: string + logoUrl: string + platformUrl: string + } +} + +export interface GoogleConfig extends Config { + config: { + clientID: string + clientSecret: string + activated: boolean + } +} + +export interface OIDCConfig extends Config { + config: { + configs: { + configUrl: string + clientID: string + clientSecret: string + logo: string + name: string + uuid: string + activated: boolean + }[] + } +} + +export type NestedConfig = + | SMTPConfig + | SettingsConfig + | GoogleConfig + | OIDCConfig + +export const isSettingsConfig = (config: Config): config is SettingsConfig => + config.type === ConfigType.SETTINGS +export const isSMTPConfig = (config: Config): config is SMTPConfig => + config.type === ConfigType.SMTP +export const isGoogleConfig = (config: Config): config is GoogleConfig => + config.type === ConfigType.GOOGLE +export const isOIDCConfig = (config: Config): config is OIDCConfig => + config.type === ConfigType.OIDC + +export enum ConfigType { + SETTINGS = "settings", + SMTP = "smtp", + GOOGLE = "google", + OIDC = "oidc", +} diff --git a/packages/types/src/documents/global/user.ts b/packages/types/src/documents/global/user.ts index 43592582c7..37155dcb73 100644 --- a/packages/types/src/documents/global/user.ts +++ b/packages/types/src/documents/global/user.ts @@ -1,5 +1,13 @@ -export interface User { +import { Document } from "../document" + +export interface User extends Document { roles: UserRoles + builder?: { + global: boolean + } + admin?: { + global: boolean + } } export interface UserRoles { From bd2f297cedd487035df925322e854aa8e93d8538 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Mon, 23 May 2022 08:55:33 +0100 Subject: [PATCH 40/75] Add opt-in / opt-out to org settings --- .../builder/portal/settings/organisation.svelte | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/builder/src/pages/builder/portal/settings/organisation.svelte b/packages/builder/src/pages/builder/portal/settings/organisation.svelte index 7094a0af01..c13b1dc1eb 100644 --- a/packages/builder/src/pages/builder/portal/settings/organisation.svelte +++ b/packages/builder/src/pages/builder/portal/settings/organisation.svelte @@ -9,6 +9,7 @@ Input, Dropzone, notifications, + Toggle, } from "@budibase/bbui" import { auth, organisation, admin } from "stores/portal" import { API } from "api" @@ -25,6 +26,7 @@ const values = writable({ company: $organisation.company, platformUrl: $organisation.platformUrl, + analyticsEnabled: $organisation.analyticsEnabled, logo: $organisation.logoUrl ? { url: $organisation.logoUrl, type: "image", name: "Logo" } : null, @@ -54,6 +56,7 @@ const config = { company: $values.company ?? "", platformUrl: $values.platformUrl ?? "", + analyticsEnabled: $values.analyticsEnabled, } // Remove logo if required @@ -123,6 +126,19 @@ {/if} + {#if !$admin.cloud} + + + Analytics + Choose whether to opt-in or opt-out of analtics + +
+
+ + +
+
+ {/if}
From 28b48a88fe1530695e0f1f6af041497e79bdbb52 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Mon, 23 May 2022 22:14:44 +0100 Subject: [PATCH 41/75] Event identification --- packages/backend-core/package.json | 2 +- packages/backend-core/src/context/index.js | 16 ++++ packages/backend-core/src/db/utils.ts | 7 +- packages/backend-core/src/environment.ts | 8 +- packages/backend-core/src/events/analytics.ts | 45 +++++++++ packages/backend-core/src/events/events.ts | 10 +- .../backend-core/src/events/identification.ts | 93 +++++++++++++++++++ packages/backend-core/src/events/index.ts | 5 +- .../events/processors/AnalyticsProcessor.ts | 43 +++++++++ .../src/events/processors/LoggingProcessor.ts | 15 +-- .../src/events/processors/PosthogProcessor.ts | 16 ++-- .../src/events/processors/Processors.ts | 56 ++--------- .../src/events/processors/index.ts | 8 +- .../src/events/processors/types.ts | 4 +- .../src/events/publishers/account.ts | 12 +-- .../backend-core/src/events/publishers/app.ts | 40 ++++---- .../src/events/publishers/auth.ts | 24 ++--- .../src/events/publishers/automation.ts | 36 +++---- .../src/events/publishers/datasource.ts | 12 +-- .../src/events/publishers/email.ts | 8 +- .../src/events/publishers/layout.ts | 8 +- .../src/events/publishers/license.ts | 20 ++-- .../backend-core/src/events/publishers/org.ts | 25 +++-- .../src/events/publishers/query.ts | 26 ++---- .../src/events/publishers/role.ts | 20 ++-- .../src/events/publishers/rows.ts | 8 +- .../src/events/publishers/screen.ts | 8 +- .../src/events/publishers/serve.ts | 12 +-- .../src/events/publishers/table.ts | 26 ++---- .../src/events/publishers/user.ts | 52 +++++------ .../src/events/publishers/view.ts | 40 ++++---- .../src/middleware/authenticated.js | 10 +- .../src/tests/utilities/mocks/events.js | 4 +- packages/backend-core/src/utils.js | 2 +- packages/builder/cypress/setup.js | 2 +- .../portal/settings/organisation.svelte | 2 +- packages/server/package.json | 2 +- packages/server/scripts/likeCypress.ts | 2 +- .../server/src/api/controllers/analytics.ts | 2 +- .../server/src/api/controllers/application.ts | 20 ++-- .../server/src/api/controllers/automation.js | 19 ++-- packages/server/src/api/controllers/backup.js | 2 +- .../server/src/api/controllers/datasource.js | 6 +- .../src/api/controllers/deploy/index.ts | 2 +- packages/server/src/api/controllers/dev.js | 2 +- packages/server/src/api/controllers/layout.js | 4 +- .../src/api/controllers/query/import/index.ts | 4 +- .../server/src/api/controllers/query/index.ts | 6 +- packages/server/src/api/controllers/role.js | 6 +- packages/server/src/api/controllers/screen.js | 4 +- .../src/api/controllers/static/index.js | 6 +- .../server/src/api/controllers/table/index.js | 8 +- .../server/src/api/controllers/table/utils.ts | 2 +- .../server/src/api/controllers/view/index.js | 34 +++---- .../migrations/functions/backfill/app/apps.ts | 4 +- .../functions/backfill/app/automations.ts | 4 +- .../functions/backfill/app/datasources.ts | 2 +- .../functions/backfill/app/layouts.ts | 2 +- .../functions/backfill/app/queries.ts | 2 +- .../functions/backfill/app/roles.ts | 2 +- .../functions/backfill/app/screens.ts | 2 +- .../functions/backfill/app/tables.ts | 8 +- .../functions/backfill/global/configs.ts | 14 +-- .../functions/backfill/global/rows.ts | 2 +- .../functions/backfill/global/users.ts | 8 +- packages/types/package.json | 2 +- packages/types/src/core/index.ts | 1 + packages/types/src/core/sessions.ts | 36 +++++++ .../types/src/documents/account/account.ts | 81 +++++++++++++++- packages/types/src/documents/global/user.ts | 2 + packages/types/src/events/event.ts | 8 +- packages/types/src/events/identification.ts | 29 ++++++ packages/types/src/events/index.ts | 1 + packages/worker/package.json | 2 +- .../worker/src/api/controllers/global/auth.ts | 10 +- .../src/api/controllers/global/configs.js | 2 +- .../worker/src/api/controllers/global/self.js | 4 +- .../src/api/controllers/global/users.ts | 4 +- packages/worker/src/sdk/users/events.ts | 40 ++++---- packages/worker/src/sdk/users/users.ts | 4 +- 80 files changed, 724 insertions(+), 408 deletions(-) create mode 100644 packages/backend-core/src/events/analytics.ts create mode 100644 packages/backend-core/src/events/identification.ts create mode 100644 packages/backend-core/src/events/processors/AnalyticsProcessor.ts create mode 100644 packages/types/src/core/sessions.ts create mode 100644 packages/types/src/events/identification.ts diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 54280d2ef3..da038d4818 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -46,7 +46,7 @@ }, "devDependencies": { "@shopify/jest-koa-mocks": "^3.1.5", - "@budibase/types": "^1.0.126-alpha.0", + "@budibase/types": "^1.0.167-alpha.8", "@types/jest": "^27.4.1", "@types/koa": "^2.13.3", "@types/node": "^15.12.4", diff --git a/packages/backend-core/src/context/index.js b/packages/backend-core/src/context/index.js index 20e5e26693..2af8b9b0f8 100644 --- a/packages/backend-core/src/context/index.js +++ b/packages/backend-core/src/context/index.js @@ -16,6 +16,7 @@ const ContextKeys = { TENANT_ID: "tenantId", GLOBAL_DB: "globalDb", APP_ID: "appId", + USER: "user", // whatever the request app DB was CURRENT_DB: "currentDb", // get the prod app DB from the request @@ -173,6 +174,21 @@ exports.doInAppContext = (appId, task) => { } } +exports.doInUserContext = (user, task) => { + return cls.run(async () => { + cls.setOnContext(ContextKeys.USER, user) + return task() + }) +} + +exports.getUser = () => { + try { + return cls.getFromContext(ContextKeys.USER) + } catch (e) { + // do nothing - user is not in context + } +} + exports.updateTenantId = tenantId => { cls.setOnContext(ContextKeys.TENANT_ID, tenantId) exports.setGlobalDB(tenantId) diff --git a/packages/backend-core/src/db/utils.ts b/packages/backend-core/src/db/utils.ts index a4e06d8217..9bf1abd18c 100644 --- a/packages/backend-core/src/db/utils.ts +++ b/packages/backend-core/src/db/utils.ts @@ -10,6 +10,7 @@ import { getAppMetadata } from "../cache/appMetadata" import { checkSlashesInUrl } from "../helpers" import { isDevApp, isDevAppID } from "./conversions" import { APP_PREFIX } from "./constants" +import * as events from "../events" const UNICODE_MAX = "\ufff0" @@ -380,15 +381,19 @@ export const getScopedFullConfig = async function ( )[0] // custom logic for settings doc - // always provide the platform URL if (type === Configs.SETTINGS) { if (scopedConfig && scopedConfig.doc) { + // overrides affected by environment variables scopedConfig.doc.config.platformUrl = await getPlatformUrl() + scopedConfig.doc.config.analyticsEnabled = + await events.analytics.enabled() } else { + // defaults scopedConfig = { doc: { config: { platformUrl: await getPlatformUrl(), + analyticsEnabled: await events.analytics.enabled(), }, }, } diff --git a/packages/backend-core/src/environment.ts b/packages/backend-core/src/environment.ts index 9210355283..417fcd7866 100644 --- a/packages/backend-core/src/environment.ts +++ b/packages/backend-core/src/environment.ts @@ -16,7 +16,7 @@ if (!LOADED && isDev() && !isTest()) { LOADED = true } -export = { +const env: any = { isTest, isDev, JWT_SECRET: process.env.JWT_SECRET, @@ -59,9 +59,11 @@ export = { } // clean up any environment variable edge cases -for (let [key, value] of Object.entries(module.exports)) { +for (let [key, value] of Object.entries(env)) { // handle the edge case of "0" to disable an environment variable if (value === "0") { - module.exports[key] = 0 + env[key] = 0 } } + +export = env diff --git a/packages/backend-core/src/events/analytics.ts b/packages/backend-core/src/events/analytics.ts new file mode 100644 index 0000000000..e2f219f554 --- /dev/null +++ b/packages/backend-core/src/events/analytics.ts @@ -0,0 +1,45 @@ +import env from "../environment" +import * as tenancy from "../tenancy" +import * as dbUtils from "../db/utils" +import { Configs } from "../constants" + +export const enabled = async () => { + // cloud - always use the environment variable + if (!env.SELF_HOSTED) { + return !!env.ENABLE_ANALYTICS + } + + // self host - prefer the settings doc + // check for explicit true/false values to support + // backwards compatibility where setting may not exist + const settings = await getSettingsDoc() + if (settings?.config?.analyticsEnabled === false) { + return false + } else if (settings?.config?.analyticsEnabled === true) { + return true + } + + // fallback to the environment variable + // explicitly check for 0 or false here, undefined or otherwise is treated as true + const envEnabled: any = env.ENABLE_ANALYTICS + if (envEnabled === 0 || envEnabled === false) { + return false + } else { + return true + } +} + +const getSettingsDoc = async () => { + const db = tenancy.getGlobalDB() + let settings + try { + settings = await db.get( + dbUtils.generateConfigID({ type: Configs.SETTINGS }) + ) + } catch (e: any) { + if (e.status !== 404) { + throw e + } + } + return settings +} diff --git a/packages/backend-core/src/events/events.ts b/packages/backend-core/src/events/events.ts index 8234a13b13..21dfba6090 100644 --- a/packages/backend-core/src/events/events.ts +++ b/packages/backend-core/src/events/events.ts @@ -1,9 +1,9 @@ import { Event } from "@budibase/types" import { processors } from "./processors" +import * as identification from "./identification" -export const publishEvent = (event: Event, properties: any) => { - // in future this should use async events - // via a queue. For now we can use sync as - // this is non-blocking - processors.processEvent(event, properties) +export const publishEvent = async (event: Event, properties: any) => { + // in future this should use async events via a distributed queue. + const identity = identification.getCurrentIdentity() + await processors.processEvent(event, identity, properties) } diff --git a/packages/backend-core/src/events/identification.ts b/packages/backend-core/src/events/identification.ts new file mode 100644 index 0000000000..f86d6abe46 --- /dev/null +++ b/packages/backend-core/src/events/identification.ts @@ -0,0 +1,93 @@ +import { + isCloudAccount, + isSSOAccount, +} from "./../../../types/src/documents/account/account" +import * as context from "../context" +import env from "../environment" +import { + Hosting, + User, + SessionUser, + Identity, + IdentityType, + Account, + AccountIdentity, + BudibaseIdentity, +} from "@budibase/types" +import { analyticsProcessor } from "./processors" + +export const getCurrentIdentity = (): Identity => { + const user: SessionUser | undefined = context.getUser() + const tenantId = context.getTenantId() + let id: string + + if (user) { + id = user._id + } else if (env.SELF_HOSTED) { + id = "installationId" // TODO + } else { + id = context.getTenantId() + } + + return { + id, + tenantId, + } +} + +export const identifyUser = async (user: User) => { + const id = user._id as string + const tenantId = user.tenantId + const hosting = env.SELF_HOSTED ? Hosting.SELF : Hosting.CLOUD + const type = IdentityType.USER + let builder = user.builder?.global + let admin = user.admin?.global + let authType = user.providerType ? user.providerType : "password" + + const identity: BudibaseIdentity = { + id, + tenantId, + hosting, + type, + builder, + admin, + authType, + } + + await identify(identity) +} + +export const identifyAccount = async (account: Account) => { + let id = account.accountId + const tenantId = account.tenantId + const hosting = account.hosting + let type = IdentityType.ACCOUNT + let authType = isSSOAccount(account) + ? (account.providerType as string) + : "password" + + if (isCloudAccount(account)) { + if (account.budibaseUserId) { + // use the budibase user as the id if set + id = account.budibaseUserId + type = IdentityType.USER + } + } + + const identity: AccountIdentity = { + id, + tenantId, + hosting, + type, + authType, + verified: account.verified, + profession: account.profession, + companySize: account.size, + } + + await identify(identity) +} + +const identify = async (identity: Identity) => { + await analyticsProcessor.identify(identity) +} diff --git a/packages/backend-core/src/events/index.ts b/packages/backend-core/src/events/index.ts index 08d82e5112..e850ea8b26 100644 --- a/packages/backend-core/src/events/index.ts +++ b/packages/backend-core/src/events/index.ts @@ -1,10 +1,7 @@ import { processors } from "./processors" export * from "./publishers" +export * as analytics from "./analytics" export const shutdown = () => { processors.shutdown() } - -export const analyticsEnabled = () => { - return true -} diff --git a/packages/backend-core/src/events/processors/AnalyticsProcessor.ts b/packages/backend-core/src/events/processors/AnalyticsProcessor.ts new file mode 100644 index 0000000000..d2f82d59fb --- /dev/null +++ b/packages/backend-core/src/events/processors/AnalyticsProcessor.ts @@ -0,0 +1,43 @@ +import { Event, Identity } from "@budibase/types" +import { EventProcessor } from "./types" +import env from "../../environment" +import * as analytics from "../analytics" +import PosthogProcessor from "./PosthogProcessor" + +export default class AnalyticsProcessor implements EventProcessor { + posthog: PosthogProcessor | undefined + + constructor() { + if (env.POSTHOG_TOKEN) { + this.posthog = new PosthogProcessor(env.POSTHOG_TOKEN) + } + } + + async processEvent( + event: Event, + identity: Identity, + properties: any + ): Promise { + if (!(await analytics.enabled())) { + return + } + if (this.posthog) { + this.posthog.processEvent(event, identity, properties) + } + } + + async identify(identity: Identity) { + if (!(await analytics.enabled())) { + return + } + if (this.posthog) { + this.posthog.identify(identity) + } + } + + shutdown() { + if (this.posthog) { + this.posthog.shutdown() + } + } +} diff --git a/packages/backend-core/src/events/processors/LoggingProcessor.ts b/packages/backend-core/src/events/processors/LoggingProcessor.ts index 8c46f0649a..1094411298 100644 --- a/packages/backend-core/src/events/processors/LoggingProcessor.ts +++ b/packages/backend-core/src/events/processors/LoggingProcessor.ts @@ -1,12 +1,15 @@ -import { Event } from "@budibase/types" -import { getTenantId } from "../../context" +import { Event, Identity } from "@budibase/types" import { EventProcessor } from "./types" export default class LoggingProcessor implements EventProcessor { - processEvent(event: Event, properties: any): void { - const tenantId = getTenantId() - const userId = getTenantId() // TODO - console.log(`[audit] [tenant=${tenantId}] [user=${userId}] ${event}`) + async processEvent( + event: Event, + identity: Identity, + properties: any + ): Promise { + console.log( + `[audit] [tenant=${identity.tenantId}] [identity=${identity.id}] ${event}` + ) } shutdown(): void { diff --git a/packages/backend-core/src/events/processors/PosthogProcessor.ts b/packages/backend-core/src/events/processors/PosthogProcessor.ts index e97ae6680c..499f69f581 100644 --- a/packages/backend-core/src/events/processors/PosthogProcessor.ts +++ b/packages/backend-core/src/events/processors/PosthogProcessor.ts @@ -1,7 +1,6 @@ import PostHog from "posthog-node" -import { Event } from "@budibase/types" +import { Event, Identity } from "@budibase/types" import { EventProcessor } from "./types" -import { getTenantId } from "../../context" export default class PosthogProcessor implements EventProcessor { posthog: PostHog @@ -13,13 +12,16 @@ export default class PosthogProcessor implements EventProcessor { this.posthog = new PostHog(token) } - processEvent(event: Event, properties: any): void { - const userId = getTenantId() // TODO - this.posthog.capture({ distinctId: userId, event, properties }) + async processEvent( + event: Event, + identity: Identity, + properties: any + ): Promise { + this.posthog.capture({ distinctId: identity.id, event, properties }) } - identify(distinctId: string, properties: any) { - this.posthog.identify({ distinctId, properties }) + async identify(identity: Identity) { + this.posthog.identify({ distinctId: identity.id, properties: identity }) } shutdown() { diff --git a/packages/backend-core/src/events/processors/Processors.ts b/packages/backend-core/src/events/processors/Processors.ts index 2b77fa90b2..ed23af4780 100644 --- a/packages/backend-core/src/events/processors/Processors.ts +++ b/packages/backend-core/src/events/processors/Processors.ts @@ -1,22 +1,21 @@ -import { Event } from "@budibase/types" +import { Event, Identity } from "@budibase/types" import { EventProcessor } from "./types" -import env from "../../environment" -import LoggingProcessor from "./LoggingProcessor" -import PosthogProcessor from "./PosthogProcessor" export default class Processor implements EventProcessor { + initialised: boolean = false processors: EventProcessor[] = [] - constructor() { - if (env.ENABLE_ANALYTICS && env.POSTHOG_TOKEN) { - this.processors.push(new PosthogProcessor(env.POSTHOG_TOKEN)) - } - this.processors.push(new LoggingProcessor()) + constructor(processors: EventProcessor[]) { + this.processors = processors } - processEvent(event: Event, properties: any): void { + async processEvent( + event: Event, + identity: Identity, + properties: any + ): Promise { for (const eventProcessor of this.processors) { - eventProcessor.processEvent(event, properties) + await eventProcessor.processEvent(event, identity, properties) } } @@ -25,39 +24,4 @@ export default class Processor implements EventProcessor { eventProcessor.shutdown() } } - - // Identity todo - - // export const identify(type: IdentityType, id: string, hosting?: Hosting) { - // const tenantId = getTenantId() - // if (!hosting) { - // hosting = env.SELF_HOSTED ? Hosting.SELF : Hosting.CLOUD - // } - // const properties = { - // type, - // hosting, - // tenantId, - // } - // this.posthog!.identify(id, properties) - // } - - // identifyUser(userId: string) { - // this.identify(IdentityType.USER, userId) - // } - - // identifyTenant() { - // let distinctId - // if (env.SELF_HOSTED) { - // distinctId = getTenantId() // TODO: Get installation ID - // } else { - // distinctId = getTenantId() - // } - // this.identify(IdentityType.TENANT, distinctId) - // } - - // identifyAccount(account: Account) { - // const distinctId = account.accountId - // const hosting = account.hosting - // this.identify(IdentityType.ACCOUNT, distinctId, hosting) - // } } diff --git a/packages/backend-core/src/events/processors/index.ts b/packages/backend-core/src/events/processors/index.ts index 9269648ff2..0e75f050db 100644 --- a/packages/backend-core/src/events/processors/index.ts +++ b/packages/backend-core/src/events/processors/index.ts @@ -1,2 +1,8 @@ +import AnalyticsProcessor from "./AnalyticsProcessor" +import LoggingProcessor from "./LoggingProcessor" import Processors from "./Processors" -export const processors = new Processors() + +export const analyticsProcessor = new AnalyticsProcessor() +const loggingProcessor = new LoggingProcessor() + +export const processors = new Processors([analyticsProcessor, loggingProcessor]) diff --git a/packages/backend-core/src/events/processors/types.ts b/packages/backend-core/src/events/processors/types.ts index c5d32f6c75..0be1606351 100644 --- a/packages/backend-core/src/events/processors/types.ts +++ b/packages/backend-core/src/events/processors/types.ts @@ -1,4 +1,4 @@ -import { Event } from "@budibase/types" +import { Event, Identity } from "@budibase/types" export enum EventProcessorType { POSTHOG = "posthog", @@ -6,6 +6,6 @@ export enum EventProcessorType { } export interface EventProcessor { - processEvent(event: Event, properties: any): void + processEvent(event: Event, identity: Identity, properties: any): Promise shutdown(): void } diff --git a/packages/backend-core/src/events/publishers/account.ts b/packages/backend-core/src/events/publishers/account.ts index 34ca42db02..66780a0b56 100644 --- a/packages/backend-core/src/events/publishers/account.ts +++ b/packages/backend-core/src/events/publishers/account.ts @@ -1,17 +1,17 @@ import { publishEvent } from "../events" import { Event, Account } from "@budibase/types" -export function created(account: Account) { +export async function created(account: Account) { const properties = {} - publishEvent(Event.ACCOUNT_CREATED, properties) + await publishEvent(Event.ACCOUNT_CREATED, properties) } -export function deleted(account: Account) { +export async function deleted(account: Account) { const properties = {} - publishEvent(Event.ACCOUNT_DELETED, properties) + await publishEvent(Event.ACCOUNT_DELETED, properties) } -export function verified(account: Account) { +export async function verified(account: Account) { const properties = {} - publishEvent(Event.ACCOUNT_VERIFIED, properties) + await publishEvent(Event.ACCOUNT_VERIFIED, properties) } diff --git a/packages/backend-core/src/events/publishers/app.ts b/packages/backend-core/src/events/publishers/app.ts index dc8ca165fd..00df1f18bc 100644 --- a/packages/backend-core/src/events/publishers/app.ts +++ b/packages/backend-core/src/events/publishers/app.ts @@ -20,54 +20,54 @@ export const created = async (app: App) => { await publishEvent(Event.APP_CREATED, properties) } -export function updated(app: App) { +export async function updated(app: App) { const properties: AppUpdatedEvent = {} - publishEvent(Event.APP_UPDATED, properties) + await publishEvent(Event.APP_UPDATED, properties) } -export function deleted(app: App) { +export async function deleted(app: App) { const properties: AppDeletedEvent = {} - publishEvent(Event.APP_DELETED, properties) + await publishEvent(Event.APP_DELETED, properties) } -export function published(app: App) { +export async function published(app: App) { const properties: AppPublishedEvent = {} - publishEvent(Event.APP_PUBLISHED, properties) + await publishEvent(Event.APP_PUBLISHED, properties) } -export function unpublished(app: App) { +export async function unpublished(app: App) { const properties: AppUnpublishedEvent = {} - publishEvent(Event.APP_UNPUBLISHED, properties) + await publishEvent(Event.APP_UNPUBLISHED, properties) } -export function fileImported(app: App) { +export async function fileImported(app: App) { const properties: AppFileImportedEvent = {} - publishEvent(Event.APP_FILE_IMPORTED, properties) + await publishEvent(Event.APP_FILE_IMPORTED, properties) } -export function templateImported(templateKey: string) { +export async function templateImported(templateKey: string) { const properties: AppTemplateImportedEvent = { templateKey, } - publishEvent(Event.APP_TEMPLATE_IMPORTED, properties) + await publishEvent(Event.APP_TEMPLATE_IMPORTED, properties) } -export function versionUpdated(app: App) { +export async function versionUpdated(app: App) { const properties: AppVersionUpdatedEvent = {} - publishEvent(Event.APP_VERSION_UPDATED, properties) + await publishEvent(Event.APP_VERSION_UPDATED, properties) } -export function versionReverted(app: App) { +export async function versionReverted(app: App) { const properties: AppVersionRevertedEvent = {} - publishEvent(Event.APP_VERSION_REVERTED, properties) + await publishEvent(Event.APP_VERSION_REVERTED, properties) } -export function reverted(app: App) { +export async function reverted(app: App) { const properties: AppRevertedEvent = {} - publishEvent(Event.APP_REVERTED, properties) + await publishEvent(Event.APP_REVERTED, properties) } -export function exported(app: App) { +export async function exported(app: App) { const properties: AppExportedEvent = {} - publishEvent(Event.APP_EXPORTED, properties) + await publishEvent(Event.APP_EXPORTED, properties) } diff --git a/packages/backend-core/src/events/publishers/auth.ts b/packages/backend-core/src/events/publishers/auth.ts index 61a73fbfc5..32af58258d 100644 --- a/packages/backend-core/src/events/publishers/auth.ts +++ b/packages/backend-core/src/events/publishers/auth.ts @@ -11,42 +11,42 @@ import { SSOUpdatedEvent, } from "@budibase/types" -export function login(source: LoginSource) { +export async function login(source: LoginSource) { const properties: LoginEvent = { source, } - publishEvent(Event.AUTH_LOGIN, properties) + await publishEvent(Event.AUTH_LOGIN, properties) } -export function logout() { +export async function logout() { const properties: LogoutEvent = {} - publishEvent(Event.AUTH_LOGOUT, properties) + await publishEvent(Event.AUTH_LOGOUT, properties) } -export function SSOCreated(type: SSOType) { +export async function SSOCreated(type: SSOType) { const properties: SSOCreatedEvent = { type, } - publishEvent(Event.AUTH_SSO_CREATED, properties) + await publishEvent(Event.AUTH_SSO_CREATED, properties) } -export function SSOUpdated(type: SSOType) { +export async function SSOUpdated(type: SSOType) { const properties: SSOUpdatedEvent = { type, } - publishEvent(Event.AUTH_SSO_UPDATED, properties) + await publishEvent(Event.AUTH_SSO_UPDATED, properties) } -export function SSOActivated(type: SSOType) { +export async function SSOActivated(type: SSOType) { const properties: SSOActivatedEvent = { type, } - publishEvent(Event.AUTH_SSO_ACTIVATED, properties) + await publishEvent(Event.AUTH_SSO_ACTIVATED, properties) } -export function SSODeactivated(type: SSOType) { +export async function SSODeactivated(type: SSOType) { const properties: SSODeactivatedEvent = { type, } - publishEvent(Event.AUTH_SSO_DEACTIVATED, properties) + await publishEvent(Event.AUTH_SSO_DEACTIVATED, properties) } diff --git a/packages/backend-core/src/events/publishers/automation.ts b/packages/backend-core/src/events/publishers/automation.ts index dfdddc86db..2a20a90929 100644 --- a/packages/backend-core/src/events/publishers/automation.ts +++ b/packages/backend-core/src/events/publishers/automation.ts @@ -12,41 +12,41 @@ import { AutomationTriggerUpdatedEvent, } from "@budibase/types" -export function created(automation: Automation) { +export async function created(automation: Automation) { const properties: AutomationCreatedEvent = {} - publishEvent(Event.AUTOMATION_CREATED, properties) + await publishEvent(Event.AUTOMATION_CREATED, properties) } -export function deleted(automation: Automation) { +export async function deleted(automation: Automation) { const properties: AutomationDeletedEvent = {} - publishEvent(Event.AUTOMATION_DELETED, properties) + await publishEvent(Event.AUTOMATION_DELETED, properties) } -export function tested(automation: Automation) { +export async function tested(automation: Automation) { const properties: AutomationTestedEvent = {} - publishEvent(Event.AUTOMATION_TESTED, properties) + await publishEvent(Event.AUTOMATION_TESTED, properties) } -// TODO -// exports.run = () => { -// const properties = {} -// events.processEvent(Events.AUTOMATION_RUN, properties) -// } - -export function stepCreated(automation: Automation, step: AutomationStep) { +export async function stepCreated( + automation: Automation, + step: AutomationStep +) { const properties: AutomationStepCreatedEvent = {} - publishEvent(Event.AUTOMATION_STEP_CREATED, properties) + await publishEvent(Event.AUTOMATION_STEP_CREATED, properties) } -export function stepDeleted(automation: Automation, step: AutomationStep) { +export async function stepDeleted( + automation: Automation, + step: AutomationStep +) { const properties: AutomationStepDeletedEvent = {} - publishEvent(Event.AUTOMATION_STEP_DELETED, properties) + await publishEvent(Event.AUTOMATION_STEP_DELETED, properties) } -export function triggerUpdated( +export async function triggerUpdated( automation: Automation, trigger: AutomationTrigger ) { const properties: AutomationTriggerUpdatedEvent = {} - publishEvent(Event.AUTOMATION_TRIGGER_UPDATED, properties) + await publishEvent(Event.AUTOMATION_TRIGGER_UPDATED, properties) } diff --git a/packages/backend-core/src/events/publishers/datasource.ts b/packages/backend-core/src/events/publishers/datasource.ts index 4ca4b1f9b7..e5392d3bb8 100644 --- a/packages/backend-core/src/events/publishers/datasource.ts +++ b/packages/backend-core/src/events/publishers/datasource.ts @@ -7,17 +7,17 @@ import { DatasourceDeletedEvent, } from "@budibase/types" -export function created(datasource: Datasource) { +export async function created(datasource: Datasource) { const properties: DatasourceCreatedEvent = {} - publishEvent(Event.DATASOURCE_CREATED, properties) + await publishEvent(Event.DATASOURCE_CREATED, properties) } -export function updated(datasource: Datasource) { +export async function updated(datasource: Datasource) { const properties: DatasourceUpdatedEvent = {} - publishEvent(Event.DATASOURCE_UPDATED, properties) + await publishEvent(Event.DATASOURCE_UPDATED, properties) } -export function deleted(datasource: Datasource) { +export async function deleted(datasource: Datasource) { const properties: DatasourceDeletedEvent = {} - publishEvent(Event.DATASOURCE_DELETED, properties) + await publishEvent(Event.DATASOURCE_DELETED, properties) } diff --git a/packages/backend-core/src/events/publishers/email.ts b/packages/backend-core/src/events/publishers/email.ts index 72d14120e7..76745453f9 100644 --- a/packages/backend-core/src/events/publishers/email.ts +++ b/packages/backend-core/src/events/publishers/email.ts @@ -6,12 +6,12 @@ import { SMTPUpdatedEvent, } from "@budibase/types" -export function SMTPCreated(config: SMTPConfig) { +export async function SMTPCreated(config: SMTPConfig) { const properties: SMTPCreatedEvent = {} - publishEvent(Event.EMAIL_SMTP_CREATED, properties) + await publishEvent(Event.EMAIL_SMTP_CREATED, properties) } -export function SMTPUpdated(config: SMTPConfig) { +export async function SMTPUpdated(config: SMTPConfig) { const properties: SMTPUpdatedEvent = {} - publishEvent(Event.EMAIL_SMTP_UPDATED, properties) + await publishEvent(Event.EMAIL_SMTP_UPDATED, properties) } diff --git a/packages/backend-core/src/events/publishers/layout.ts b/packages/backend-core/src/events/publishers/layout.ts index ae0129ab35..109f23571a 100644 --- a/packages/backend-core/src/events/publishers/layout.ts +++ b/packages/backend-core/src/events/publishers/layout.ts @@ -6,12 +6,12 @@ import { LayoutDeletedEvent, } from "@budibase/types" -export function created(layout: Layout) { +export async function created(layout: Layout) { const properties: LayoutCreatedEvent = {} - publishEvent(Event.LAYOUT_CREATED, properties) + await publishEvent(Event.LAYOUT_CREATED, properties) } -export function deleted(layout: Layout) { +export async function deleted(layout: Layout) { const properties: LayoutDeletedEvent = {} - publishEvent(Event.LAYOUT_DELETED, properties) + await publishEvent(Event.LAYOUT_DELETED, properties) } diff --git a/packages/backend-core/src/events/publishers/license.ts b/packages/backend-core/src/events/publishers/license.ts index 22fc19cd10..1ec6649223 100644 --- a/packages/backend-core/src/events/publishers/license.ts +++ b/packages/backend-core/src/events/publishers/license.ts @@ -10,34 +10,34 @@ import { } from "@budibase/types" // TODO -export function updgraded(license: License) { +export async function updgraded(license: License) { const properties: LicenseUpgradedEvent = {} - publishEvent(Event.LICENSE_UPGRADED, properties) + await publishEvent(Event.LICENSE_UPGRADED, properties) } // TODO -export function downgraded(license: License) { +export async function downgraded(license: License) { const properties: LicenseDowngradedEvent = {} - publishEvent(Event.LICENSE_DOWNGRADED, properties) + await publishEvent(Event.LICENSE_DOWNGRADED, properties) } // TODO -export function updated(license: License) { +export async function updated(license: License) { const properties: LicenseUpdatedEvent = {} - publishEvent(Event.LICENSE_UPDATED, properties) + await publishEvent(Event.LICENSE_UPDATED, properties) } // TODO -export function activated(license: License) { +export async function activated(license: License) { const properties: LicenseActivatedEvent = {} - publishEvent(Event.LICENSE_ACTIVATED, properties) + await publishEvent(Event.LICENSE_ACTIVATED, properties) } // TODO -export function quotaExceeded(quotaName: string, value: number) { +export async function quotaExceeded(quotaName: string, value: number) { const properties: LicenseQuotaExceededEvent = { name: quotaName, value, } - publishEvent(Event.LICENSE_QUOTA_EXCEEDED, properties) + await publishEvent(Event.LICENSE_QUOTA_EXCEEDED, properties) } diff --git a/packages/backend-core/src/events/publishers/org.ts b/packages/backend-core/src/events/publishers/org.ts index c1e5948f8a..ae2670d4da 100644 --- a/packages/backend-core/src/events/publishers/org.ts +++ b/packages/backend-core/src/events/publishers/org.ts @@ -1,30 +1,35 @@ import { publishEvent } from "../events" import { Event, VersionCheckedEvent } from "@budibase/types" -export function nameUpdated() { +export async function nameUpdated() { const properties = {} - publishEvent(Event.ORG_NAME_UPDATED, properties) + await publishEvent(Event.ORG_NAME_UPDATED, properties) } -export function logoUpdated() { +export async function logoUpdated() { const properties = {} - publishEvent(Event.ORG_LOGO_UPDATED, properties) + await publishEvent(Event.ORG_LOGO_UPDATED, properties) } -export function platformURLUpdated() { +export async function platformURLUpdated() { const properties = {} - publishEvent(Event.ORG_PLATFORM_URL_UPDATED, properties) + await publishEvent(Event.ORG_PLATFORM_URL_UPDATED, properties) } -export function versionChecked(version: number) { +export async function versionChecked(version: number) { const properties: VersionCheckedEvent = { version, } - publishEvent(Event.UPDATE_VERSION_CHECKED, properties) + await publishEvent(Event.UPDATE_VERSION_CHECKED, properties) } // TODO -export function analyticsOptOut() { +export async function analyticsOptOut() { const properties = {} - publishEvent(Event.ANALYTICS_OPT_OUT, properties) + await publishEvent(Event.ANALYTICS_OPT_OUT, properties) +} + +export async function analyticsOptIn() { + const properties = {} + await publishEvent(Event.ANALYTICS_OPT_OUT, properties) } diff --git a/packages/backend-core/src/events/publishers/query.ts b/packages/backend-core/src/events/publishers/query.ts index 94e83c2fa7..e012d5c830 100644 --- a/packages/backend-core/src/events/publishers/query.ts +++ b/packages/backend-core/src/events/publishers/query.ts @@ -12,37 +12,31 @@ import { /* eslint-disable */ -export const created = (datasource: Datasource, query: Query) => { +export const created = async (datasource: Datasource, query: Query) => { const properties: QueryCreatedEvent = {} - publishEvent(Event.QUERY_CREATED, properties) + await publishEvent(Event.QUERY_CREATED, properties) } -export const updated = (datasource: Datasource, query: Query) => { +export const updated = async (datasource: Datasource, query: Query) => { const properties: QueryUpdatedEvent = {} - publishEvent(Event.QUERY_UPDATED, properties) + await publishEvent(Event.QUERY_UPDATED, properties) } -export const deleted = (datasource: Datasource, query: Query) => { +export const deleted = async (datasource: Datasource, query: Query) => { const properties: QueryDeletedEvent = {} - publishEvent(Event.QUERY_DELETED, properties) + await publishEvent(Event.QUERY_DELETED, properties) } -export const imported = ( +export const imported = async ( datasource: Datasource, importSource: any, count: any ) => { const properties: QueryImportedEvent = {} - publishEvent(Event.QUERY_IMPORT, properties) + await publishEvent(Event.QUERY_IMPORT, properties) } -// TODO -// exports.run = () => { -// const properties = {} -// events.processEvent(Events.QUERY_RUN, properties) -// } - -export const previewed = (datasource: Datasource) => { +export const previewed = async (datasource: Datasource) => { const properties: QueryPreviewedEvent = {} - publishEvent(Event.QUERY_PREVIEWED, properties) + await publishEvent(Event.QUERY_PREVIEWED, properties) } diff --git a/packages/backend-core/src/events/publishers/role.ts b/packages/backend-core/src/events/publishers/role.ts index b08d001c77..d257eadf2b 100644 --- a/packages/backend-core/src/events/publishers/role.ts +++ b/packages/backend-core/src/events/publishers/role.ts @@ -12,27 +12,27 @@ import { /* eslint-disable */ -export function created(role: Role) { +export async function created(role: Role) { const properties: RoleCreatedEvent = {} - publishEvent(Event.ROLE_CREATED, properties) + await publishEvent(Event.ROLE_CREATED, properties) } -export function updated(role: Role) { +export async function updated(role: Role) { const properties: RoleUpdatedEvent = {} - publishEvent(Event.ROLE_UPDATED, properties) + await publishEvent(Event.ROLE_UPDATED, properties) } -export function deleted(role: Role) { +export async function deleted(role: Role) { const properties: RoleDeletedEvent = {} - publishEvent(Event.ROLE_DELETED, properties) + await publishEvent(Event.ROLE_DELETED, properties) } -export function assigned(user: User, role: string) { +export async function assigned(user: User, role: string) { const properties: RoleAssignedEvent = {} - publishEvent(Event.ROLE_ASSIGNED, properties) + await publishEvent(Event.ROLE_ASSIGNED, properties) } -export function unassigned(user: User, role: string) { +export async function unassigned(user: User, role: string) { const properties: RoleUnassignedEvent = {} - publishEvent(Event.ROLE_UNASSIGNED, properties) + await publishEvent(Event.ROLE_UNASSIGNED, properties) } diff --git a/packages/backend-core/src/events/publishers/rows.ts b/packages/backend-core/src/events/publishers/rows.ts index 450a5a6495..0877b77b7a 100644 --- a/packages/backend-core/src/events/publishers/rows.ts +++ b/packages/backend-core/src/events/publishers/rows.ts @@ -9,18 +9,18 @@ import { /* eslint-disable */ -export const created = (count: number) => { +export const created = async (count: number) => { const properties: RowsCreatedEvent = { count, } - publishEvent(Event.ROWS_CREATED, properties) + await publishEvent(Event.ROWS_CREATED, properties) } -export const imported = ( +export const imported = async ( table: Table, format: RowImportFormat, count: number ) => { const properties: RowsImportedEvent = {} - publishEvent(Event.ROWS_IMPORTED, properties) + await publishEvent(Event.ROWS_IMPORTED, properties) } diff --git a/packages/backend-core/src/events/publishers/screen.ts b/packages/backend-core/src/events/publishers/screen.ts index bf5ffef811..efb7d9538f 100644 --- a/packages/backend-core/src/events/publishers/screen.ts +++ b/packages/backend-core/src/events/publishers/screen.ts @@ -6,12 +6,12 @@ import { ScreenDeletedEvent, } from "@budibase/types" -export function created(screen: Screen) { +export async function created(screen: Screen) { const properties: ScreenCreatedEvent = {} - publishEvent(Event.SCREEN_CREATED, properties) + await publishEvent(Event.SCREEN_CREATED, properties) } -export function deleted(screen: Screen) { +export async function deleted(screen: Screen) { const properties: ScreenDeletedEvent = {} - publishEvent(Event.SCREEN_DELETED, properties) + await publishEvent(Event.SCREEN_DELETED, properties) } diff --git a/packages/backend-core/src/events/publishers/serve.ts b/packages/backend-core/src/events/publishers/serve.ts index 3a61751a8b..62aaf4fb4c 100644 --- a/packages/backend-core/src/events/publishers/serve.ts +++ b/packages/backend-core/src/events/publishers/serve.ts @@ -9,17 +9,17 @@ import { /* eslint-disable */ -export function servedBuilder(version: number) { +export async function servedBuilder(version: number) { const properties: BuilderServedEvent = {} - publishEvent(Event.SERVED_BUILDER, properties) + await publishEvent(Event.SERVED_BUILDER, properties) } -export function servedApp(app: App) { +export async function servedApp(app: App) { const properties: AppServedEvent = {} - publishEvent(Event.SERVED_APP, properties) + await publishEvent(Event.SERVED_APP, properties) } -export function servedAppPreview(app: App) { +export async function servedAppPreview(app: App) { const properties: AppPreviewServedEvent = {} - publishEvent(Event.SERVED_APP_PREVIEW, properties) + await publishEvent(Event.SERVED_APP_PREVIEW, properties) } diff --git a/packages/backend-core/src/events/publishers/table.ts b/packages/backend-core/src/events/publishers/table.ts index fca966873c..cfa4b28835 100644 --- a/packages/backend-core/src/events/publishers/table.ts +++ b/packages/backend-core/src/events/publishers/table.ts @@ -13,33 +13,27 @@ import { /* eslint-disable */ -export function created(table: Table) { +export async function created(table: Table) { const properties: TableCreatedEvent = {} - publishEvent(Event.TABLE_CREATED, properties) + await publishEvent(Event.TABLE_CREATED, properties) } -export function updated(table: Table) { +export async function updated(table: Table) { const properties: TableUpdatedEvent = {} - publishEvent(Event.TABLE_UPDATED, properties) + await publishEvent(Event.TABLE_UPDATED, properties) } -export function deleted(table: Table) { +export async function deleted(table: Table) { const properties: TableDeletedEvent = {} - publishEvent(Event.TABLE_DELETED, properties) + await publishEvent(Event.TABLE_DELETED, properties) } -export function exported(table: Table, format: TableExportFormat) { +export async function exported(table: Table, format: TableExportFormat) { const properties: TableExportedEvent = {} - publishEvent(Event.TABLE_EXPORTED, properties) + await publishEvent(Event.TABLE_EXPORTED, properties) } -export function imported(table: Table, format: TableImportFormat) { +export async function imported(table: Table, format: TableImportFormat) { const properties: TableImportedEvent = {} - publishEvent(Event.TABLE_IMPORTED, properties) -} - -// TODO -export function permissionUpdated() { - const properties = {} - publishEvent(Event.TABLE_PERMISSION_UPDATED, properties) + await publishEvent(Event.TABLE_IMPORTED, properties) } diff --git a/packages/backend-core/src/events/publishers/user.ts b/packages/backend-core/src/events/publishers/user.ts index a5da10d3a7..eeea5234cb 100644 --- a/packages/backend-core/src/events/publishers/user.ts +++ b/packages/backend-core/src/events/publishers/user.ts @@ -17,73 +17,73 @@ import { /* eslint-disable */ -export function created(user: User) { +export async function created(user: User) { const properties: UserCreatedEvent = {} - publishEvent(Event.USER_CREATED, properties) + await publishEvent(Event.USER_CREATED, properties) } -export function updated(user: User) { +export async function updated(user: User) { const properties: UserUpdatedEvent = {} - publishEvent(Event.USER_UPDATED, properties) + await publishEvent(Event.USER_UPDATED, properties) } -export function deleted(user: User) { +export async function deleted(user: User) { const properties: UserDeletedEvent = {} - publishEvent(Event.USER_DELETED, properties) + await publishEvent(Event.USER_DELETED, properties) } // PERMISSIONS -export function permissionAdminAssigned(user: User) { +export async function permissionAdminAssigned(user: User) { const properties: UserPermissionAssignedEvent = {} - publishEvent(Event.USER_PERMISSION_ADMIN_ASSIGNED, properties) + await publishEvent(Event.USER_PERMISSION_ADMIN_ASSIGNED, properties) } -export function permissionAdminRemoved(user: User) { +export async function permissionAdminRemoved(user: User) { const properties: UserPermissionRemovedEvent = {} - publishEvent(Event.USER_PERMISSION_ADMIN_REMOVED, properties) + await publishEvent(Event.USER_PERMISSION_ADMIN_REMOVED, properties) } -export function permissionBuilderAssigned(user: User) { +export async function permissionBuilderAssigned(user: User) { const properties: UserPermissionAssignedEvent = {} - publishEvent(Event.USER_PERMISSION_BUILDER_ASSIGNED, properties) + await publishEvent(Event.USER_PERMISSION_BUILDER_ASSIGNED, properties) } -export function permissionBuilderRemoved(user: User) { +export async function permissionBuilderRemoved(user: User) { const properties: UserPermissionRemovedEvent = {} - publishEvent(Event.USER_PERMISSION_BUILDER_REMOVED, properties) + await publishEvent(Event.USER_PERMISSION_BUILDER_REMOVED, properties) } // INVITE -export function invited(userInfo: any) { +export async function invited(userInfo: any) { const properties: UserInvitedEvent = {} - publishEvent(Event.USER_INVITED, properties) + await publishEvent(Event.USER_INVITED, properties) } -export function inviteAccepted(user: User) { +export async function inviteAccepted(user: User) { const properties: UserInviteAcceptedEvent = {} - publishEvent(Event.USER_INVITED_ACCEPTED, properties) + await publishEvent(Event.USER_INVITED_ACCEPTED, properties) } // PASSWORD -export function passwordForceReset(user: User) { +export async function passwordForceReset(user: User) { const properties: UserPasswordForceResetEvent = {} - publishEvent(Event.USER_PASSWORD_FORCE_RESET, properties) + await publishEvent(Event.USER_PASSWORD_FORCE_RESET, properties) } -export function passwordUpdated(user: User) { +export async function passwordUpdated(user: User) { const properties: UserPasswordUpdatedEvent = {} - publishEvent(Event.USER_PASSWORD_UPDATED, properties) + await publishEvent(Event.USER_PASSWORD_UPDATED, properties) } -export function passwordResetRequested(user: User) { +export async function passwordResetRequested(user: User) { const properties: UserPasswordResetRequestedEvent = {} - publishEvent(Event.USER_PASSWORD_RESET_REQUESTED, properties) + await publishEvent(Event.USER_PASSWORD_RESET_REQUESTED, properties) } -export function passwordReset(user: User) { +export async function passwordReset(user: User) { const properties: UserPasswordResetEvent = {} - publishEvent(Event.USER_PASSWORD_RESET, properties) + await publishEvent(Event.USER_PASSWORD_RESET, properties) } diff --git a/packages/backend-core/src/events/publishers/view.ts b/packages/backend-core/src/events/publishers/view.ts index e9c594e8c9..ff0c8a7f0d 100644 --- a/packages/backend-core/src/events/publishers/view.ts +++ b/packages/backend-core/src/events/publishers/view.ts @@ -19,52 +19,52 @@ import { /* eslint-disable */ -export function created(view: View) { +export async function created(view: View) { const properties: ViewCreatedEvent = {} - publishEvent(Event.VIEW_CREATED, properties) + await publishEvent(Event.VIEW_CREATED, properties) } -export function updated(view: View) { +export async function updated(view: View) { const properties: ViewUpdatedEvent = {} - publishEvent(Event.VIEW_UPDATED, properties) + await publishEvent(Event.VIEW_UPDATED, properties) } -export function deleted() { +export async function deleted() { const properties: ViewDeletedEvent = {} - publishEvent(Event.VIEW_DELETED, properties) + await publishEvent(Event.VIEW_DELETED, properties) } -export function exported(table: Table, format: TableExportFormat) { +export async function exported(table: Table, format: TableExportFormat) { const properties: ViewExportedEvent = {} - publishEvent(Event.VIEW_EXPORTED, properties) + await publishEvent(Event.VIEW_EXPORTED, properties) } -export function filterCreated() { +export async function filterCreated() { const properties: ViewFilterCreatedEvent = {} - publishEvent(Event.VIEW_FILTER_CREATED, properties) + await publishEvent(Event.VIEW_FILTER_CREATED, properties) } -export function filterUpdated() { +export async function filterUpdated() { const properties: ViewFilterUpdatedEvent = {} - publishEvent(Event.VIEW_FILTER_UPDATED, properties) + await publishEvent(Event.VIEW_FILTER_UPDATED, properties) } -export function filterDeleted() { +export async function filterDeleted() { const properties: ViewFilterDeletedEvent = {} - publishEvent(Event.VIEW_FILTER_DELETED, properties) + await publishEvent(Event.VIEW_FILTER_DELETED, properties) } -export function calculationCreated(calculation: ViewCalculation) { +export async function calculationCreated(calculation: ViewCalculation) { const properties: ViewCalculationCreatedEvent = {} - publishEvent(Event.VIEW_CALCULATION_CREATED, properties) + await publishEvent(Event.VIEW_CALCULATION_CREATED, properties) } -export function calculationUpdated() { +export async function calculationUpdated() { const properties: ViewCalculationUpdatedEvent = {} - publishEvent(Event.VIEW_CALCULATION_UPDATED, properties) + await publishEvent(Event.VIEW_CALCULATION_UPDATED, properties) } -export function calculationDeleted() { +export async function calculationDeleted() { const properties: ViewCalculationDeletedEvent = {} - publishEvent(Event.VIEW_CALCULATION_DELETED, properties) + await publishEvent(Event.VIEW_CALCULATION_DELETED, properties) } diff --git a/packages/backend-core/src/middleware/authenticated.js b/packages/backend-core/src/middleware/authenticated.js index ef982c799b..a1bb0a8ef1 100644 --- a/packages/backend-core/src/middleware/authenticated.js +++ b/packages/backend-core/src/middleware/authenticated.js @@ -7,6 +7,7 @@ const env = require("../environment") const { SEPARATOR, ViewNames, queryGlobalView } = require("../../db") const { getGlobalDB, doInTenant } = require("../tenancy") const { decrypt } = require("../security/encryption") +const context = require("../context") function finalise( ctx, @@ -132,7 +133,12 @@ module.exports = ( } // isAuthenticated is a function, so use a variable to be able to check authed state finalise(ctx, { authenticated, user, internal, version, publicEndpoint }) - return next() + + if (user && user.email) { + return context.doInUserContext(user, next) + } else { + return next + } } catch (err) { // invalid token, clear the cookie if (err && err.name === "JsonWebTokenError") { @@ -141,7 +147,7 @@ module.exports = ( // allow configuring for public access if ((opts && opts.publicAllowed) || publicEndpoint) { finalise(ctx, { authenticated: false, version, publicEndpoint }) - return next() + return context.doInUserContext({ _id: "public_user" }, next) } else { ctx.throw(err.status || 403, err) } diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index 8f660286cc..86bfda2dbe 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -1,6 +1,8 @@ jest.mock("../../../events", () => { return { - analyticsEnabled: () => false, + analytics: { + enabled: () => false, + }, shutdown: () => {}, account: { created: jest.fn(), diff --git a/packages/backend-core/src/utils.js b/packages/backend-core/src/utils.js index bb8d1d4805..9d2524bbeb 100644 --- a/packages/backend-core/src/utils.js +++ b/packages/backend-core/src/utils.js @@ -194,6 +194,6 @@ exports.platformLogout = async ({ ctx, userId, keepActiveSession }) => { userId, sessions.map(({ sessionId }) => sessionId) ) - events.auth.logout() + await events.auth.logout() await userCache.invalidateUser(userId) } diff --git a/packages/builder/cypress/setup.js b/packages/builder/cypress/setup.js index d10990573a..d858801990 100644 --- a/packages/builder/cypress/setup.js +++ b/packages/builder/cypress/setup.js @@ -8,7 +8,7 @@ const SERVER_PORT = cypressConfig.env.PORT const WORKER_PORT = cypressConfig.env.WORKER_PORT process.env.NODE_ENV = "cypress" -process.env.ENABLE_ANALYTICS = "false" +process.env.ENABLE_ANALYTICS = "0" process.env.JWT_SECRET = cypressConfig.env.JWT_SECRET process.env.COUCH_URL = `leveldb://${tmpdir}/.data/` process.env.SELF_HOSTED = 1 diff --git a/packages/builder/src/pages/builder/portal/settings/organisation.svelte b/packages/builder/src/pages/builder/portal/settings/organisation.svelte index c13b1dc1eb..8c091ce952 100644 --- a/packages/builder/src/pages/builder/portal/settings/organisation.svelte +++ b/packages/builder/src/pages/builder/portal/settings/organisation.svelte @@ -130,7 +130,7 @@ Analytics - Choose whether to opt-in or opt-out of analtics + Choose whether to opt-in or opt-out of analytics.
diff --git a/packages/server/package.json b/packages/server/package.json index 3468b5d6ae..fdaecef212 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -145,7 +145,7 @@ "@babel/core": "^7.14.3", "@babel/preset-env": "^7.14.4", "@budibase/standard-components": "^0.9.139", - "@budibase/types": "^1.0.126-alpha.0", + "@budibase/types": "^1.0.167-alpha.8", "@jest/test-sequencer": "^24.8.0", "@types/apidoc": "^0.50.0", "@types/bson": "^4.2.0", diff --git a/packages/server/scripts/likeCypress.ts b/packages/server/scripts/likeCypress.ts index ac32608a32..6e9f763635 100644 --- a/packages/server/scripts/likeCypress.ts +++ b/packages/server/scripts/likeCypress.ts @@ -10,7 +10,7 @@ const WORKER_PORT = "4200" // @ts-ignore process.env.NODE_ENV = "cypress" -process.env.ENABLE_ANALYTICS = "false" +process.env.ENABLE_ANALYTICS = "0" process.env.JWT_SECRET = "budibase" process.env.COUCH_URL = `leveldb://${tmpdir}/.data/` process.env.SELF_HOSTED = "1" diff --git a/packages/server/src/api/controllers/analytics.ts b/packages/server/src/api/controllers/analytics.ts index 9d0329bd04..e8fa8d82fb 100644 --- a/packages/server/src/api/controllers/analytics.ts +++ b/packages/server/src/api/controllers/analytics.ts @@ -2,6 +2,6 @@ import { events } from "@budibase/backend-core" export const isEnabled = async (ctx: any) => { ctx.body = { - enabled: events.analyticsEnabled(), + enabled: events.analytics.enabled(), } } diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index a9679f959a..221df7f0dd 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -292,14 +292,14 @@ const performAppCreate = async (ctx: any) => { return newApplication } -const creationEvents = (request: any, app: App) => { - let creationFns = [] +const creationEvents = async (request: any, app: App) => { + let creationFns: ((app: App) => Promise)[] = [] const body = request.body if (body.useTemplate === "true") { // from template if (body.templateKey) { - creationFns.push(() => events.app.templateImported(body.templateKey)) + creationFns.push(app => events.app.templateImported(body.templateKey)) } // from file else if (request.files?.templateFile) { @@ -313,12 +313,12 @@ const creationEvents = (request: any, app: App) => { creationFns.push(events.app.created) for (let fn of creationFns) { - fn(app) + await fn(app) } } const appPostCreate = async (ctx: any, app: App) => { - creationEvents(ctx.request, app) + await creationEvents(ctx.request, app) // app import & template creation if (ctx.request.body.useTemplate === "true") { const rows = await getUniqueRows([app.appId]) @@ -363,7 +363,7 @@ export const update = async (ctx: any) => { } const app = await updateAppPackage(ctx.request.body, ctx.params.appId) - events.app.updated(app) + await events.app.updated(app) ctx.status = 200 ctx.body = app } @@ -386,7 +386,7 @@ export const updateClient = async (ctx: any) => { revertableVersion: currentVersion, } const app = await updateAppPackage(appPackageUpdates, ctx.params.appId) - events.app.versionUpdated(app) + await events.app.versionUpdated(app) ctx.status = 200 ctx.body = app } @@ -410,7 +410,7 @@ export const revertClient = async (ctx: any) => { revertableVersion: null, } const app = await updateAppPackage(appPackageUpdates, ctx.params.appId) - events.app.versionReverted(app) + await events.app.versionReverted(app) ctx.status = 200 ctx.body = app } @@ -429,10 +429,10 @@ const destroyApp = async (ctx: any) => { if (isUnpublish) { await quotas.removePublishedApp() - events.app.unpublished(app) + await events.app.unpublished(app) } else { await quotas.removeApp() - events.app.deleted(app) + await events.app.deleted(app) } /* istanbul ignore next */ diff --git a/packages/server/src/api/controllers/automation.js b/packages/server/src/api/controllers/automation.js index 2c4c669392..ddea76beb4 100644 --- a/packages/server/src/api/controllers/automation.js +++ b/packages/server/src/api/controllers/automation.js @@ -71,9 +71,9 @@ exports.create = async function (ctx) { newAuto: automation, }) const response = await db.put(automation) - events.automation.created() + await events.automation.created() for (let step of automation.definition.steps) { - events.automation.stepCreated(step) + await events.automation.stepCreated(step) } automation._rev = response.rev @@ -97,19 +97,20 @@ const getDeletedSteps = (oldAutomation, automation) => { return oldAutomation.definition.steps.filter(s => !stepIds.includes(s.id)) } -const handleStepEvents = (oldAutomation, automation) => { +const handleStepEvents = async (oldAutomation, automation) => { // new steps const newSteps = getNewSteps(oldAutomation, automation) for (let step of newSteps) { - events.automation.stepCreated(step) + await events.automation.stepCreated(step) } // old steps const deletedSteps = getDeletedSteps(oldAutomation, automation) for (let step of deletedSteps) { - events.automation.stepDeleted(step) + await events.automation.stepDeleted(step) } } + exports.update = async function (ctx) { const db = getAppDB() let automation = ctx.request.body @@ -133,7 +134,7 @@ exports.update = async function (ctx) { : {} // trigger has been updated, remove the test inputs if (oldAutoTrigger && oldAutoTrigger.id !== newAutoTrigger.id) { - events.automation.triggerUpdated() + await events.automation.triggerUpdated() await deleteEntityMetadata( ctx.appId, MetadataTypes.AUTOMATION_TEST_INPUT, @@ -141,7 +142,7 @@ exports.update = async function (ctx) { ) } - handleStepEvents(oldAutomation, automation) + await handleStepEvents(oldAutomation, automation) ctx.status = 200 ctx.body = { @@ -179,7 +180,7 @@ exports.destroy = async function (ctx) { // delete metadata first await cleanupAutomationMetadata(automationId) ctx.body = await db.remove(automationId, ctx.params.rev) - events.automation.deleted() + await events.automation.deleted() } exports.getActionList = async function (ctx) { @@ -247,5 +248,5 @@ exports.test = async function (ctx) { }) await clearTestFlag(automation._id) ctx.body = response - events.automation.tested() + await events.automation.tested() } diff --git a/packages/server/src/api/controllers/backup.js b/packages/server/src/api/controllers/backup.js index 193f4139c5..a6b7adf98a 100644 --- a/packages/server/src/api/controllers/backup.js +++ b/packages/server/src/api/controllers/backup.js @@ -7,5 +7,5 @@ exports.exportAppDump = async function (ctx) { const backupIdentifier = `${appName}-export-${new Date().getTime()}.txt` ctx.attachment(backupIdentifier) ctx.body = await streamBackup(appId) - events.app.exported() + await events.app.exported() } diff --git a/packages/server/src/api/controllers/datasource.js b/packages/server/src/api/controllers/datasource.js index d9e9dda046..63c51fb92c 100644 --- a/packages/server/src/api/controllers/datasource.js +++ b/packages/server/src/api/controllers/datasource.js @@ -109,7 +109,7 @@ exports.update = async function (ctx) { } const response = await db.put(datasource) - events.datasource.updated() + await events.datasource.updated() datasource._rev = response.rev // Drain connection pools when configuration is changed @@ -144,7 +144,7 @@ exports.save = async function (ctx) { } const dbResp = await db.put(datasource) - events.datasource.created() + await events.datasource.created() datasource._rev = dbResp.rev // Drain connection pools when configuration is changed @@ -179,7 +179,7 @@ exports.destroy = async function (ctx) { // delete the datasource await db.remove(ctx.params.datasourceId, ctx.params.revId) - events.datasource.deleted() + await events.datasource.deleted() ctx.message = `Datasource deleted.` ctx.status = 200 diff --git a/packages/server/src/api/controllers/deploy/index.ts b/packages/server/src/api/controllers/deploy/index.ts index 3520fc9f02..7c1a093398 100644 --- a/packages/server/src/api/controllers/deploy/index.ts +++ b/packages/server/src/api/controllers/deploy/index.ts @@ -195,7 +195,7 @@ const _deployApp = async function (ctx: any) { app = await deployApp(deployment) } - events.app.published(app) + await events.app.published(app) ctx.body = deployment } diff --git a/packages/server/src/api/controllers/dev.js b/packages/server/src/api/controllers/dev.js index d59a9660fc..1146013c3b 100644 --- a/packages/server/src/api/controllers/dev.js +++ b/packages/server/src/api/controllers/dev.js @@ -118,7 +118,7 @@ exports.revert = async ctx => { ctx.body = { message: "Reverted changes successfully.", } - events.app.reverted() + await events.app.reverted() } catch (err) { ctx.throw(400, `Unable to revert. ${err}`) } finally { diff --git a/packages/server/src/api/controllers/layout.js b/packages/server/src/api/controllers/layout.js index 1809fce344..044d4a1acb 100644 --- a/packages/server/src/api/controllers/layout.js +++ b/packages/server/src/api/controllers/layout.js @@ -20,7 +20,7 @@ exports.save = async function (ctx) { layout._id = layout._id || generateLayoutID() const response = await db.put(layout) - events.layout.created() + await events.layout.created() layout._rev = response.rev ctx.body = layout @@ -48,7 +48,7 @@ exports.destroy = async function (ctx) { } await db.remove(layoutId, layoutRev) - events.layout.deleted() + await events.layout.deleted() ctx.body = { message: "Layout deleted successfully" } ctx.status = 200 } diff --git a/packages/server/src/api/controllers/query/import/index.ts b/packages/server/src/api/controllers/query/import/index.ts index eb70a33b2f..339775cbdc 100644 --- a/packages/server/src/api/controllers/query/import/index.ts +++ b/packages/server/src/api/controllers/query/import/index.ts @@ -84,9 +84,9 @@ export class RestImporter { const count = successQueries.length const importSource = this.source.getImportSource() const datasource: Datasource = await db.get(datasourceId) - events.query.imported(datasource, importSource, count) + await events.query.imported(datasource, importSource, count) for (let query of successQueries) { - events.query.created(datasource, query) + await events.query.created(datasource, query) } return { diff --git a/packages/server/src/api/controllers/query/index.ts b/packages/server/src/api/controllers/query/index.ts index a9bbdcf53c..ff101d48a7 100644 --- a/packages/server/src/api/controllers/query/index.ts +++ b/packages/server/src/api/controllers/query/index.ts @@ -92,7 +92,7 @@ export async function save(ctx: any) { } const response = await db.put(query) - eventFn() + await eventFn() query._rev = response.rev ctx.body = query @@ -132,7 +132,7 @@ export async function preview(ctx: any) { }) const { rows, keys, info, extra } = await quotas.addQuery(runFn) - events.query.previewed(datasource) + await events.query.previewed(datasource) ctx.body = { rows, schemaFields: [...new Set(keys)], @@ -223,5 +223,5 @@ export async function destroy(ctx: any) { await db.remove(ctx.params.queryId, ctx.params.revId) ctx.message = `Query deleted.` ctx.status = 200 - events.query.deleted(datasource, query) + await events.query.deleted(datasource, query) } diff --git a/packages/server/src/api/controllers/role.js b/packages/server/src/api/controllers/role.js index 8f28847266..9a406ad63f 100644 --- a/packages/server/src/api/controllers/role.js +++ b/packages/server/src/api/controllers/role.js @@ -66,9 +66,9 @@ exports.save = async function (ctx) { } const result = await db.put(role) if (isCreate) { - events.role.created(role) + await events.role.created(role) } else { - events.role.updated(role) + await events.role.updated(role) } await updateRolesOnUserTable(db, _id, UpdateRolesOptions.CREATED) role._rev = result.rev @@ -97,7 +97,7 @@ exports.destroy = async function (ctx) { } await db.remove(roleId, ctx.params.rev) - events.role.deleted(role) + await events.role.deleted(role) await updateRolesOnUserTable( db, ctx.params.roleId, diff --git a/packages/server/src/api/controllers/screen.js b/packages/server/src/api/controllers/screen.js index 01e6a22611..dd96493e5b 100644 --- a/packages/server/src/api/controllers/screen.js +++ b/packages/server/src/api/controllers/screen.js @@ -33,7 +33,7 @@ exports.save = async ctx => { const response = await db.put(screen) if (eventFn) { - eventFn(screen) + await eventFn(screen) } ctx.message = `Screen ${screen.name} saved.` ctx.body = { @@ -50,7 +50,7 @@ exports.destroy = async ctx => { await db.remove(id, ctx.params.screenRev) - events.screen.deleted(screen) + await events.screen.deleted(screen) ctx.body = { message: "Screen deleted successfully", } diff --git a/packages/server/src/api/controllers/static/index.js b/packages/server/src/api/controllers/static/index.js index bcd12172dd..07ad691faa 100644 --- a/packages/server/src/api/controllers/static/index.js +++ b/packages/server/src/api/controllers/static/index.js @@ -43,7 +43,7 @@ async function prepareUpload({ s3Key, bucket, metadata, file }) { exports.serveBuilder = async function (ctx) { let builderPath = resolve(TOP_LEVEL_PATH, "builder") await send(ctx, ctx.file, { root: builderPath }) - events.serve.servedBuilder(version) + await events.serve.servedBuilder(version) } exports.uploadFile = async function (ctx) { @@ -94,9 +94,9 @@ exports.serveApp = async function (ctx) { } if (isDevAppID(appInfo.appId)) { - events.serve.servedAppPreview(appInfo) + await events.serve.servedAppPreview(appInfo) } else { - events.serve.servedApp(appInfo) + await events.serve.servedApp(appInfo) } } diff --git a/packages/server/src/api/controllers/table/index.js b/packages/server/src/api/controllers/table/index.js index 701b805b50..e6192457af 100644 --- a/packages/server/src/api/controllers/table/index.js +++ b/packages/server/src/api/controllers/table/index.js @@ -60,12 +60,12 @@ exports.save = async function (ctx) { table.dataImport && table.dataImport.csvString ? "csv" : undefined const savedTable = await pickApi({ table }).save(ctx) if (!table._id) { - events.table.created(savedTable) + await events.table.created(savedTable) } else { - events.table.updated(savedTable) + await events.table.updated(savedTable) } if (importFormat) { - events.table.imported(savedTable, importFormat) + await events.table.imported(savedTable, importFormat) } ctx.status = 200 ctx.message = `Table ${table.name} saved successfully.` @@ -78,7 +78,7 @@ exports.destroy = async function (ctx) { const appId = ctx.appId const tableId = ctx.params.tableId const deletedTable = await pickApi({ tableId }).destroy(ctx) - events.table.deleted(deletedTable) + await events.table.deleted(deletedTable) ctx.eventEmitter && ctx.eventEmitter.emitTable(`table:delete`, appId, deletedTable) ctx.status = 200 diff --git a/packages/server/src/api/controllers/table/utils.ts b/packages/server/src/api/controllers/table/utils.ts index 31fd1d4220..896221dddf 100644 --- a/packages/server/src/api/controllers/table/utils.ts +++ b/packages/server/src/api/controllers/table/utils.ts @@ -149,7 +149,7 @@ export async function handleDataImport(user: any, table: any, dataImport: any) { } await quotas.addRows(finalData.length, () => db.bulkDocs(finalData)) - events.rows.imported(table, "csv", finalData.length) + await events.rows.imported(table, "csv", finalData.length) return table } diff --git a/packages/server/src/api/controllers/view/index.js b/packages/server/src/api/controllers/view/index.js index 16d30d350d..404652a8db 100644 --- a/packages/server/src/api/controllers/view/index.js +++ b/packages/server/src/api/controllers/view/index.js @@ -39,7 +39,7 @@ exports.save = async ctx => { existingTable.views[viewName] = existingTable.views[originalName] } await db.put(table) - handleViewEvents(existingTable.views[viewName], table.views[viewName]) + await handleViewEvents(existingTable.views[viewName], table.views[viewName]) ctx.body = { ...table.views[viewToSave.name], @@ -47,16 +47,16 @@ exports.save = async ctx => { } } -const calculationEvents = (existingView, newView) => { +const calculationEvents = async (existingView, newView) => { const existingCalculation = existingView && existingView.calculation const newCalculation = newView && newView.calculation if (existingCalculation && !newCalculation) { - events.view.calculationDeleted() + await events.view.calculationDeleted() } if (!existingCalculation && newCalculation) { - events.view.calculationCreated() + await events.view.calculationCreated() } if ( @@ -64,11 +64,11 @@ const calculationEvents = (existingView, newView) => { newCalculation && existingCalculation !== newCalculation ) { - events.view.calculationUpdated() + await events.view.calculationUpdated() } } -const filterEvents = (existingView, newView) => { +const filterEvents = async (existingView, newView) => { const hasExistingFilters = !!( existingView && existingView.filters && @@ -77,11 +77,11 @@ const filterEvents = (existingView, newView) => { const hasNewFilters = !!(newView && newView.filters && newView.filters.length) if (hasExistingFilters && !hasNewFilters) { - events.view.filterDeleted() + await events.view.filterDeleted() } if (!hasExistingFilters && hasNewFilters) { - events.view.filterCreated() + await events.view.filterCreated() } if ( @@ -89,18 +89,18 @@ const filterEvents = (existingView, newView) => { hasNewFilters && !isEqual(existingView.filters, newView.filters) ) { - events.view.filterUpdated() + await events.view.filterUpdated() } } -const handleViewEvents = (existingView, newView) => { +const handleViewEvents = async (existingView, newView) => { if (!existingView) { - events.view.created() + await events.view.created() } else { - events.view.updated() + await events.view.updated() } - calculationEvents(existingView, newView) - filterEvents(existingView, newView) + await calculationEvents(existingView, newView) + await filterEvents(existingView, newView) } exports.destroy = async ctx => { @@ -110,7 +110,7 @@ exports.destroy = async ctx => { const table = await db.get(view.meta.tableId) delete table.views[viewName] await db.put(table) - events.view.deleted() + await events.view.deleted() ctx.body = view } @@ -182,8 +182,8 @@ exports.exportView = async ctx => { ctx.body = apiFileReturn(exporter(headers, rows)) if (viewName.startsWith(DocumentTypes.TABLE)) { - events.table.exported(table, format) + await events.table.exported(table, format) } else { - events.view.exported(table, format) + await events.view.exported(table, format) } } diff --git a/packages/server/src/migrations/functions/backfill/app/apps.ts b/packages/server/src/migrations/functions/backfill/app/apps.ts index 0099f04290..35536198aa 100644 --- a/packages/server/src/migrations/functions/backfill/app/apps.ts +++ b/packages/server/src/migrations/functions/backfill/app/apps.ts @@ -5,10 +5,10 @@ export const backfill = async (appDb: any) => { const app: App = await appDb.get(db.DocumentTypes.APP_METADATA) if (db.isDevAppID(app.appId)) { - events.app.created(app) + await events.app.created(app) } if (db.isProdAppID(app.appId)) { - events.app.published(app) + await events.app.published(app) } } diff --git a/packages/server/src/migrations/functions/backfill/app/automations.ts b/packages/server/src/migrations/functions/backfill/app/automations.ts index 1c1f0e1da4..ccdc1cbdb0 100644 --- a/packages/server/src/migrations/functions/backfill/app/automations.ts +++ b/packages/server/src/migrations/functions/backfill/app/automations.ts @@ -16,10 +16,10 @@ export const backfill = async (appDb: any) => { const automations = await getAutomations(appDb) for (const automation of automations) { - events.automation.created(automation) + await events.automation.created(automation) for (const step of automation.definition.steps) { - events.automation.stepCreated(automation, step) + await events.automation.stepCreated(automation, step) } } } diff --git a/packages/server/src/migrations/functions/backfill/app/datasources.ts b/packages/server/src/migrations/functions/backfill/app/datasources.ts index 7ebc92ab7c..c1ad9d02e3 100644 --- a/packages/server/src/migrations/functions/backfill/app/datasources.ts +++ b/packages/server/src/migrations/functions/backfill/app/datasources.ts @@ -16,7 +16,7 @@ export const backfill = async (appDb: any) => { const datasources: Datasource[] = await getDatasources(appDb) for (const datasource of datasources) { - events.datasource.created(datasource) + await events.datasource.created(datasource) } } } diff --git a/packages/server/src/migrations/functions/backfill/app/layouts.ts b/packages/server/src/migrations/functions/backfill/app/layouts.ts index 89c3aa9dc2..fb0db1b5a6 100644 --- a/packages/server/src/migrations/functions/backfill/app/layouts.ts +++ b/packages/server/src/migrations/functions/backfill/app/layouts.ts @@ -16,7 +16,7 @@ export const backfill = async (appDb: any) => { const layouts: Layout[] = await getLayouts(appDb) for (const layout of layouts) { - events.layout.created(layout) + await events.layout.created(layout) } } } diff --git a/packages/server/src/migrations/functions/backfill/app/queries.ts b/packages/server/src/migrations/functions/backfill/app/queries.ts index 6c6ad985f2..dfc236ec8b 100644 --- a/packages/server/src/migrations/functions/backfill/app/queries.ts +++ b/packages/server/src/migrations/functions/backfill/app/queries.ts @@ -27,7 +27,7 @@ export const backfill = async (appDb: any) => { appDb, query.datasourceId ) - events.query.created(datasource, query) + await events.query.created(datasource, query) } } } diff --git a/packages/server/src/migrations/functions/backfill/app/roles.ts b/packages/server/src/migrations/functions/backfill/app/roles.ts index de2ce9d37b..4672030b55 100644 --- a/packages/server/src/migrations/functions/backfill/app/roles.ts +++ b/packages/server/src/migrations/functions/backfill/app/roles.ts @@ -16,7 +16,7 @@ export const backfill = async (appDb: any) => { const roles = await getRoles(appDb) for (const role of roles) { - events.role.created(role) + await events.role.created(role) } } } diff --git a/packages/server/src/migrations/functions/backfill/app/screens.ts b/packages/server/src/migrations/functions/backfill/app/screens.ts index d2fe0bd99b..340187ae40 100644 --- a/packages/server/src/migrations/functions/backfill/app/screens.ts +++ b/packages/server/src/migrations/functions/backfill/app/screens.ts @@ -16,7 +16,7 @@ export const backfill = async (appDb: any) => { const screens = await getScreens(appDb) for (const screen of screens) { - events.screen.created(screen) + await events.screen.created(screen) } } } diff --git a/packages/server/src/migrations/functions/backfill/app/tables.ts b/packages/server/src/migrations/functions/backfill/app/tables.ts index 4231875190..c7401e921d 100644 --- a/packages/server/src/migrations/functions/backfill/app/tables.ts +++ b/packages/server/src/migrations/functions/backfill/app/tables.ts @@ -16,18 +16,18 @@ export const backfill = async (appDb: any) => { const tables = await getTables(appDb) for (const table of tables) { - events.table.created(table) + await events.table.created(table) if (table.views) { for (const view of Object.values(table.views)) { - events.view.created(view) + await events.view.created(view) if (view.calculation) { - events.view.calculationCreated(view.calculation) + await events.view.calculationCreated(view.calculation) } if (view.filters?.length) { - events.view.filterCreated() + await events.view.filterCreated() } } } diff --git a/packages/server/src/migrations/functions/backfill/global/configs.ts b/packages/server/src/migrations/functions/backfill/global/configs.ts index 7b04cbc05f..5c3644fca5 100644 --- a/packages/server/src/migrations/functions/backfill/global/configs.ts +++ b/packages/server/src/migrations/functions/backfill/global/configs.ts @@ -25,16 +25,16 @@ export const backfill = async (globalDb: any) => { for (const config of configs) { if (isSMTPConfig(config)) { - events.email.SMTPCreated(config) + await events.email.SMTPCreated(config) } if (isGoogleConfig(config)) { - events.auth.SSOCreated("google") + await events.auth.SSOCreated("google") if (config.config.activated) { - events.auth.SSOActivated("google") + await events.auth.SSOActivated("google") } } if (isOIDCConfig(config)) { - events.auth.SSOCreated("oidc") + await events.auth.SSOCreated("oidc") if (config.config.configs[0].activated) { events.auth.SSOActivated("oidc") } @@ -42,12 +42,12 @@ export const backfill = async (globalDb: any) => { if (isSettingsConfig(config)) { const company = config.config.company if (company && company !== "Budibase") { - events.org.nameUpdated() + await events.org.nameUpdated() } const logoUrl = config.config.logoUrl if (logoUrl) { - events.org.logoUpdated() + await events.org.logoUpdated() } const platformUrl = config.config.platformUrl @@ -56,7 +56,7 @@ export const backfill = async (globalDb: any) => { platformUrl !== "http://localhost:10000" && env.SELF_HOSTED ) { - events.org.platformURLUpdated() + await events.org.platformURLUpdated() } } } diff --git a/packages/server/src/migrations/functions/backfill/global/rows.ts b/packages/server/src/migrations/functions/backfill/global/rows.ts index 593b0516d0..4aaa47c1ee 100644 --- a/packages/server/src/migrations/functions/backfill/global/rows.ts +++ b/packages/server/src/migrations/functions/backfill/global/rows.ts @@ -11,6 +11,6 @@ export const backfill = async () => { const rows: Row[] = await getUniqueRows(appIds) const rowCount = rows ? rows.length : 0 if (rowCount) { - events.rows.created(rowCount) + await events.rows.created(rowCount) } } diff --git a/packages/server/src/migrations/functions/backfill/global/users.ts b/packages/server/src/migrations/functions/backfill/global/users.ts index a3c8ce52cc..60ae9c699c 100644 --- a/packages/server/src/migrations/functions/backfill/global/users.ts +++ b/packages/server/src/migrations/functions/backfill/global/users.ts @@ -19,19 +19,19 @@ export const backfill = async (globalDb: any) => { const users = await getUsers(globalDb) for (const user of users) { - events.user.created(user) + await events.user.created(user) if (user.admin?.global) { - events.user.permissionAdminAssigned(user) + await events.user.permissionAdminAssigned(user) } if (user.builder?.global) { - events.user.permissionBuilderAssigned(user) + await events.user.permissionBuilderAssigned(user) } if (user.roles) { for (const [appId, role] of Object.entries(user.roles)) { - events.role.assigned(user, role) + await events.role.assigned(user, role) } } } diff --git a/packages/types/package.json b/packages/types/package.json index 2b73c142c1..6841478d4b 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/types", - "version": "1.0.126-alpha.0", + "version": "1.0.167-alpha.8", "description": "Budibase types", "main": "src/index.ts", "types": "src/index.ts", diff --git a/packages/types/src/core/index.ts b/packages/types/src/core/index.ts index 11d05c91f4..23d772467a 100644 --- a/packages/types/src/core/index.ts +++ b/packages/types/src/core/index.ts @@ -1 +1,2 @@ export * from "./hosting" +export * from "./sessions" diff --git a/packages/types/src/core/sessions.ts b/packages/types/src/core/sessions.ts new file mode 100644 index 0000000000..a47fd90223 --- /dev/null +++ b/packages/types/src/core/sessions.ts @@ -0,0 +1,36 @@ +import { User, Account } from "../documents" +import { Hosting } from "./hosting" + +/** + * Account portal user session. Used for self hosted accounts only. + */ +export interface AccountUserSession { + _id: string + email: string + tenantId: string + accountPortalAccess: boolean + account: Account +} + +/** + * Budibase user session. + */ +export interface BudibaseUserSession extends User { + _id: string // overwrite potentially undefined + account?: Account + accountPortalAccess?: boolean +} + +export const isAccountSession = ( + user: AccountUserSession | BudibaseUserSession +): user is AccountUserSession => { + return user.account?.hosting === Hosting.SELF +} + +export const isUserSession = ( + user: AccountUserSession | BudibaseUserSession +): user is BudibaseUserSession => { + return !user.account || user.account?.hosting === Hosting.CLOUD +} + +export type SessionUser = AccountUserSession | BudibaseUserSession diff --git a/packages/types/src/documents/account/account.ts b/packages/types/src/documents/account/account.ts index 98d6235f03..a31ccda59f 100644 --- a/packages/types/src/documents/account/account.ts +++ b/packages/types/src/documents/account/account.ts @@ -1,7 +1,82 @@ import { Hosting } from "../../core" -export interface Account { - accountId: string +export interface CreateAccount { + email: string + tenantId: string hosting: Hosting - verified: boolean + authType: AuthType + // optional fields - for sso based sign ups + registrationStep?: string + // profile + tenantName?: string + name?: string + size?: string + profession?: string +} + +export interface CreatePassswordAccount extends CreateAccount { + password: string +} + +export const isCreatePasswordAccount = ( + account: CreateAccount +): account is CreatePassswordAccount => account.authType === AuthType.PASSWORD + +export interface UpdateAccount { + stripeCustomerId?: string + licenseKey?: string +} + +export interface Account extends CreateAccount { + // generated + accountId: string + createdAt: number + // registration + verified: boolean + verificationSent: boolean + // licensing + tier: string // deprecated + stripeCustomerId?: string + licenseKey?: string +} + +export interface PasswordAccount extends Account { + password: string +} + +export const isPasswordAccount = ( + account: Account +): account is PasswordAccount => + account.authType === AuthType.PASSWORD && account.hosting === Hosting.SELF + +export interface CloudAccount extends Account { + password?: string + budibaseUserId: string +} + +export const isCloudAccount = (account: Account): account is CloudAccount => + account.hosting === Hosting.CLOUD + +export const isSelfHostAccount = (account: Account) => + account.hosting === Hosting.SELF + +export const isSSOAccount = (account: Account): account is SSOAccount => + account.authType === AuthType.SSO + +export interface SSOAccount extends Account { + pictureUrl?: string + provider?: string + providerType?: string + oauth2?: OAuthTokens + thirdPartyProfile: any // TODO: define what the google profile looks like +} + +export enum AuthType { + SSO = "sso", + PASSWORD = "password", +} + +export interface OAuthTokens { + accessToken: string + refreshToken: string } diff --git a/packages/types/src/documents/global/user.ts b/packages/types/src/documents/global/user.ts index 37155dcb73..1cf3025ed9 100644 --- a/packages/types/src/documents/global/user.ts +++ b/packages/types/src/documents/global/user.ts @@ -8,6 +8,8 @@ export interface User extends Document { admin?: { global: boolean } + providerType?: string + tenantId: string } export interface UserRoles { diff --git a/packages/types/src/events/event.ts b/packages/types/src/events/event.ts index 7838f0e45e..e4d6b40d6e 100644 --- a/packages/types/src/events/event.ts +++ b/packages/types/src/events/event.ts @@ -42,6 +42,7 @@ export enum Event { // ORG / ANALYTICS ANALYTICS_OPT_OUT = "analytics:opt:out", + ANALYTICS_OPT_IN = "analytics:opt:in", // APP APP_CREATED = "app:created", @@ -88,7 +89,6 @@ export enum Event { TABLE_EXPORTED = "table:exported", TABLE_IMPORTED = "table:imported", TABLE_DATA_IMPORTED = "table:data:imported", - TABLE_PERMISSION_UPDATED = "table:permission:updated", // VIEW VIEW_CREATED = "view:created", @@ -140,12 +140,6 @@ export enum Event { ACCOUNT_VERIFIED = "account:verified", } -export enum IdentityType { - TENANT = "tenant", - USER = "user", - ACCOUNT = "account", -} - export type RowImportFormat = "csv" export type TableExportFormat = "json" | "csv" export type TableImportFormat = "csv" diff --git a/packages/types/src/events/identification.ts b/packages/types/src/events/identification.ts new file mode 100644 index 0000000000..0429882ecd --- /dev/null +++ b/packages/types/src/events/identification.ts @@ -0,0 +1,29 @@ +import { Hosting } from "../core" + +export enum IdentityType { + USER = "user", // cloud and self hosted users + ACCOUNT = "account", // self hosted accounts + TENANT = "tenant", // cloud and self hosted tenants +} + +export interface Identity { + id: string + tenantId: string +} + +export interface UserIdentity extends Identity { + hosting: Hosting + type: IdentityType + authType: string +} + +export interface BudibaseIdentity extends UserIdentity { + builder?: boolean + admin?: boolean +} + +export interface AccountIdentity extends UserIdentity { + verified: boolean + profession: string | undefined + companySize: string | undefined +} diff --git a/packages/types/src/events/index.ts b/packages/types/src/events/index.ts index e2c4ae8a6d..defd2c7b5b 100644 --- a/packages/types/src/events/index.ts +++ b/packages/types/src/events/index.ts @@ -15,3 +15,4 @@ export * from "./serve" export * from "./table" export * from "./user" export * from "./view" +export * from "./identification" diff --git a/packages/worker/package.json b/packages/worker/package.json index cd7412fa64..9fe6c82325 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -64,7 +64,7 @@ "server-destroy": "^1.0.1" }, "devDependencies": { - "@budibase/types": "^1.0.126-alpha.0", + "@budibase/types": "^1.0.167-alpha.8", "@types/jest": "^26.0.23", "@types/koa": "^2.13.3", "@types/koa-router": "^7.4.2", diff --git a/packages/worker/src/api/controllers/global/auth.ts b/packages/worker/src/api/controllers/global/auth.ts index 247a45c0a3..4555ef05d0 100644 --- a/packages/worker/src/api/controllers/global/auth.ts +++ b/packages/worker/src/api/controllers/global/auth.ts @@ -71,7 +71,7 @@ export const authenticate = async (ctx: any, next: any) => { "local", async (err: any, user: any, info: any) => { await authInternal(ctx, user, err, info) - events.auth.login("local") + await events.auth.login("local") ctx.status = 200 } )(ctx, next) @@ -112,7 +112,7 @@ export const reset = async (ctx: any) => { user, subject: "{{ company }} platform password reset", }) - events.user.passwordResetRequested(user) + await events.user.passwordResetRequested(user) } } catch (err) { console.log(err) @@ -139,7 +139,7 @@ export const resetUpdate = async (ctx: any) => { } // remove password from the user before sending events delete user.password - events.user.passwordReset(user) + await events.user.passwordReset(user) } catch (err) { ctx.throw(400, "Cannot reset password.") } @@ -212,7 +212,7 @@ export const googleAuth = async (ctx: any, next: any) => { { successRedirect: "/", failureRedirect: "/error" }, async (err: any, user: any, info: any) => { await authInternal(ctx, user, err, info) - events.auth.login("google") + await events.auth.login("google") ctx.redirect("/") } )(ctx, next) @@ -256,7 +256,7 @@ export const oidcAuth = async (ctx: any, next: any) => { { successRedirect: "/", failureRedirect: "/error" }, async (err: any, user: any, info: any) => { await authInternal(ctx, user, err, info) - events.auth.login("oidc") + await events.auth.login("oidc") ctx.redirect("/") } )(ctx, next) diff --git a/packages/worker/src/api/controllers/global/configs.js b/packages/worker/src/api/controllers/global/configs.js index 8c820d83e8..bfc4ba0f16 100644 --- a/packages/worker/src/api/controllers/global/configs.js +++ b/packages/worker/src/api/controllers/global/configs.js @@ -162,7 +162,7 @@ exports.save = async function (ctx) { try { const response = await db.put(ctx.request.body) for (const fn of eventFns) { - fn() + await fn() } ctx.body = { type, diff --git a/packages/worker/src/api/controllers/global/self.js b/packages/worker/src/api/controllers/global/self.js index ebfc55c55b..28afa69fa0 100644 --- a/packages/worker/src/api/controllers/global/self.js +++ b/packages/worker/src/api/controllers/global/self.js @@ -145,8 +145,8 @@ exports.updateSelf = async ctx => { // remove the old password from the user before sending events delete user.password - events.user.updated(user) + await events.user.updated(user) if (passwordChange) { - events.user.passwordUpdated(user) + await events.user.passwordUpdated(user) } } diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index bcdc7a21bb..acd5bf8c89 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -123,7 +123,7 @@ export const invite = async (ctx: any) => { ctx.body = { message: "Invitation has been sent.", } - events.user.invited(userInfo) + await events.user.invited(userInfo) } export const inviteAccept = async (ctx: any) => { @@ -139,7 +139,7 @@ export const inviteAccept = async (ctx: any) => { email, ...info, }) - events.user.inviteAccepted(user) + await events.user.inviteAccepted(user) return user }) } catch (err: any) { diff --git a/packages/worker/src/sdk/users/events.ts b/packages/worker/src/sdk/users/events.ts index 76108f6853..eace03a3af 100644 --- a/packages/worker/src/sdk/users/events.ts +++ b/packages/worker/src/sdk/users/events.ts @@ -1,19 +1,19 @@ import { events } from "@budibase/backend-core" import { User, UserRoles } from "@budibase/types" -export const handleDeleteEvents = (user: any) => { - events.user.deleted(user) +export const handleDeleteEvents = async (user: any) => { + await events.user.deleted(user) if (isBuilder(user)) { - events.user.permissionBuilderRemoved(user) + await events.user.permissionBuilderRemoved(user) } if (isAdmin(user)) { - events.user.permissionAdminRemoved(user) + await events.user.permissionAdminRemoved(user) } } -const assignAppRoleEvents = ( +const assignAppRoleEvents = async ( user: User, roles: UserRoles, existingRoles: UserRoles @@ -21,12 +21,12 @@ const assignAppRoleEvents = ( for (const [appId, role] of Object.entries(roles)) { // app role in existing is not same as new if (!existingRoles || existingRoles[appId] !== role) { - events.role.assigned(user, role) + await events.role.assigned(user, role) } } } -const unassignAppRoleEvents = ( +const unassignAppRoleEvents = async ( user: User, roles: UserRoles, existingRoles: UserRoles @@ -37,29 +37,29 @@ const unassignAppRoleEvents = ( for (const [appId, role] of Object.entries(existingRoles)) { // app role in new is not same as existing if (!roles || roles[appId] !== role) { - events.role.unassigned(user, role) + await events.role.unassigned(user, role) } } } -const handleAppRoleEvents = (user: any, existingUser: any) => { +const handleAppRoleEvents = async (user: any, existingUser: any) => { const roles = user.roles const existingRoles = existingUser?.roles - assignAppRoleEvents(user, roles, existingRoles) - unassignAppRoleEvents(user, roles, existingRoles) + await assignAppRoleEvents(user, roles, existingRoles) + await unassignAppRoleEvents(user, roles, existingRoles) } -export const handleSaveEvents = (user: any, existingUser: any) => { +export const handleSaveEvents = async (user: any, existingUser: any) => { if (existingUser) { - events.user.updated(user) + await events.user.updated(user) if (isRemovingBuilder(user, existingUser)) { - events.user.permissionBuilderRemoved(user) + await events.user.permissionBuilderRemoved(user) } if (isRemovingAdmin(user, existingUser)) { - events.user.permissionAdminRemoved(user) + await events.user.permissionAdminRemoved(user) } if ( @@ -67,21 +67,21 @@ export const handleSaveEvents = (user: any, existingUser: any) => { user.forceResetPassword && user.password ) { - events.user.passwordForceReset(user) + await events.user.passwordForceReset(user) } } else { - events.user.created(user) + await events.user.created(user) } if (isAddingBuilder(user, existingUser)) { - events.user.permissionBuilderAssigned(user) + await events.user.permissionBuilderAssigned(user) } if (isAddingAdmin(user, existingUser)) { - events.user.permissionAdminAssigned(user) + await events.user.permissionAdminAssigned(user) } - handleAppRoleEvents(user, existingUser) + await handleAppRoleEvents(user, existingUser) } const isBuilder = (user: any) => user.builder && user.builder.global diff --git a/packages/worker/src/sdk/users/users.ts b/packages/worker/src/sdk/users/users.ts index b514392350..0d29dfd190 100644 --- a/packages/worker/src/sdk/users/users.ts +++ b/packages/worker/src/sdk/users/users.ts @@ -129,7 +129,7 @@ export const save = async ( } user._rev = response.rev - eventHelpers.handleSaveEvents(user, dbUser) + await eventHelpers.handleSaveEvents(user, dbUser) await tenancy.tryAddTenant(tenantId, _id, email) await cache.user.invalidateUser(response.id) @@ -169,7 +169,7 @@ export const destroy = async (id: string, currentUser: any) => { await deprovisioning.removeUserFromInfoDB(dbUser) await db.remove(dbUser._id, dbUser._rev) - eventHelpers.handleDeleteEvents(dbUser) + await eventHelpers.handleDeleteEvents(dbUser) await quotas.removeUser(dbUser) await cache.user.invalidateUser(dbUser._id) await sessions.invalidateSessions(dbUser._id) From ff48aaec6e45939f797ac21f9f6558c2d2dcf0f3 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 24 May 2022 09:54:36 +0100 Subject: [PATCH 42/75] User context updates and misc fixes --- packages/backend-core/context.js | 2 + packages/backend-core/src/context/index.js | 79 +++++++------------ .../backend-core/src/events/identification.ts | 8 +- .../src/middleware/authenticated.js | 2 +- .../backend-core/src/middleware/tenancy.js | 51 +++++++++--- packages/backend-core/tsconfig.build.json | 2 - packages/backend-core/tsconfig.json | 1 - packages/server/src/api/controllers/dev.js | 2 +- .../src/api/controllers/table/external.js | 2 +- .../migrations/functions/backfill/account.ts | 16 ---- .../functions/backfill/global/configs.ts | 2 +- packages/types/src/events/auth.ts | 2 +- .../worker/src/api/controllers/global/auth.ts | 17 ++-- 13 files changed, 91 insertions(+), 95 deletions(-) delete mode 100644 packages/server/src/migrations/functions/backfill/account.ts diff --git a/packages/backend-core/context.js b/packages/backend-core/context.js index 4bc100687d..4ba3e6bffe 100644 --- a/packages/backend-core/context.js +++ b/packages/backend-core/context.js @@ -5,6 +5,7 @@ const { getAppId, updateAppId, doInAppContext, + doInUserContext, } = require("./src/context") module.exports = { @@ -14,4 +15,5 @@ module.exports = { getAppId, updateAppId, doInAppContext, + doInUserContext, } diff --git a/packages/backend-core/src/context/index.js b/packages/backend-core/src/context/index.js index 2af8b9b0f8..ae9c7b92ea 100644 --- a/packages/backend-core/src/context/index.js +++ b/packages/backend-core/src/context/index.js @@ -1,5 +1,4 @@ const env = require("../environment") -const { Headers } = require("../../constants") const { SEPARATOR, DocumentTypes } = require("../db/constants") const { DEFAULT_TENANT_ID } = require("../constants") const cls = require("./FunctionContext") @@ -77,7 +76,11 @@ exports.isMultiTenant = () => { exports.doInTenant = (tenantId, task) => { // the internal function is so that we can re-use an existing // context - don't want to close DB on a parent context - async function internal(opts = { existing: false }) { + async function internal(opts = { existing: false, user: undefined }) { + // preserve the user + if (user) { + exports.setUser(user) + } // set the tenant id if (!opts.existing) { cls.setOnContext(ContextKeys.TENANT_ID, tenantId) @@ -98,14 +101,16 @@ exports.doInTenant = (tenantId, task) => { } } } + + const user = cls.getFromContext(ContextKeys.USER) const using = cls.getFromContext(ContextKeys.IN_USE) if (using && cls.getFromContext(ContextKeys.TENANT_ID) === tenantId) { cls.setOnContext(ContextKeys.IN_USE, using + 1) - return internal({ existing: true }) + return internal({ existing: true, user }) } else { return cls.run(async () => { cls.setOnContext(ContextKeys.IN_USE, 1) - return internal() + return internal({ existing: false, user }) }) } } @@ -143,7 +148,11 @@ exports.doInAppContext = (appId, task) => { // the internal function is so that we can re-use an existing // context - don't want to close DB on a parent context - async function internal(opts = { existing: false }) { + async function internal(opts = { existing: false, user: undefined }) { + // preserve the user + if (user) { + exports.setUser(user) + } // set the app tenant id if (!opts.existing) { setAppTenantId(appId) @@ -162,28 +171,39 @@ exports.doInAppContext = (appId, task) => { } } } + const user = cls.getFromContext(ContextKeys.USER) const using = cls.getFromContext(ContextKeys.IN_USE) if (using && cls.getFromContext(ContextKeys.APP_ID) === appId) { cls.setOnContext(ContextKeys.IN_USE, using + 1) - return internal({ existing: true }) + return internal({ existing: true, user }) } else { return cls.run(async () => { cls.setOnContext(ContextKeys.IN_USE, 1) - return internal() + return internal({ existing: false, user }) }) } } exports.doInUserContext = (user, task) => { - return cls.run(async () => { - cls.setOnContext(ContextKeys.USER, user) + return cls.run(() => { + let tenantId = user.tenantId + if (!tenantId) { + tenantId = exports.getTenantId() + } + cls.setOnContext(ContextKeys.TENANT_ID, tenantId) + exports.setUser(user) return task() }) } +exports.setUser = user => { + cls.setOnContext(ContextKeys.USER, user) +} + exports.getUser = () => { try { - return cls.getFromContext(ContextKeys.USER) + const user = cls.getFromContext(ContextKeys.USER) + return user } catch (e) { // do nothing - user is not in context } @@ -208,45 +228,6 @@ exports.updateAppId = async appId => { } } -exports.setTenantId = ( - ctx, - opts = { allowQs: false, allowNoTenant: false } -) => { - let tenantId - // exit early if not multi-tenant - if (!exports.isMultiTenant()) { - cls.setOnContext(ContextKeys.TENANT_ID, exports.DEFAULT_TENANT_ID) - return exports.DEFAULT_TENANT_ID - } - - const allowQs = opts && opts.allowQs - const allowNoTenant = opts && opts.allowNoTenant - const header = ctx.request.headers[Headers.TENANT_ID] - const user = ctx.user || {} - if (allowQs) { - const query = ctx.request.query || {} - tenantId = query.tenantId - } - // override query string (if allowed) by user, or header - // URL params cannot be used in a middleware, as they are - // processed later in the chain - tenantId = user.tenantId || header || tenantId - - // Set the tenantId from the subdomain - if (!tenantId) { - tenantId = ctx.subdomains && ctx.subdomains[0] - } - - if (!tenantId && !allowNoTenant) { - ctx.throw(403, "Tenant id not set") - } - // check tenant ID just incase no tenant was allowed - if (tenantId) { - cls.setOnContext(ContextKeys.TENANT_ID, tenantId) - } - return tenantId -} - exports.setGlobalDB = tenantId => { const dbName = baseGlobalDBName(tenantId) const db = dangerousGetDB(dbName) diff --git a/packages/backend-core/src/events/identification.ts b/packages/backend-core/src/events/identification.ts index f86d6abe46..0b7e1f12f9 100644 --- a/packages/backend-core/src/events/identification.ts +++ b/packages/backend-core/src/events/identification.ts @@ -1,7 +1,3 @@ -import { - isCloudAccount, - isSSOAccount, -} from "./../../../types/src/documents/account/account" import * as context from "../context" import env from "../environment" import { @@ -13,6 +9,8 @@ import { Account, AccountIdentity, BudibaseIdentity, + isCloudAccount, + isSSOAccount, } from "@budibase/types" import { analyticsProcessor } from "./processors" @@ -26,7 +24,7 @@ export const getCurrentIdentity = (): Identity => { } else if (env.SELF_HOSTED) { id = "installationId" // TODO } else { - id = context.getTenantId() + id = tenantId } return { diff --git a/packages/backend-core/src/middleware/authenticated.js b/packages/backend-core/src/middleware/authenticated.js index a1bb0a8ef1..3fef6eadac 100644 --- a/packages/backend-core/src/middleware/authenticated.js +++ b/packages/backend-core/src/middleware/authenticated.js @@ -137,7 +137,7 @@ module.exports = ( if (user && user.email) { return context.doInUserContext(user, next) } else { - return next + return next() } } catch (err) { // invalid token, clear the cookie diff --git a/packages/backend-core/src/middleware/tenancy.js b/packages/backend-core/src/middleware/tenancy.js index 9a0cb8a0c6..b3e61a2fe4 100644 --- a/packages/backend-core/src/middleware/tenancy.js +++ b/packages/backend-core/src/middleware/tenancy.js @@ -1,6 +1,38 @@ -const { setTenantId, setGlobalDB, closeTenancy } = require("../tenancy") -const cls = require("../context/FunctionContext") +const { doInTenant, isMultiTenant, DEFAULT_TENANT_ID } = require("../tenancy") const { buildMatcherRegex, matches } = require("./matchers") +const { Headers } = require("../../constants") + +const getTenantID = (ctx, opts = { allowQs: false, allowNoTenant: false }) => { + // exit early if not multi-tenant + if (!isMultiTenant()) { + return DEFAULT_TENANT_ID + } + + let tenantId + const allowQs = opts && opts.allowQs + const allowNoTenant = opts && opts.allowNoTenant + const header = ctx.request.headers[Headers.TENANT_ID] + const user = ctx.user || {} + if (allowQs) { + const query = ctx.request.query || {} + tenantId = query.tenantId + } + // override query string (if allowed) by user, or header + // URL params cannot be used in a middleware, as they are + // processed later in the chain + tenantId = user.tenantId || header || tenantId + + // Set the tenantId from the subdomain + if (!tenantId) { + tenantId = ctx.subdomains && ctx.subdomains[0] + } + + if (!tenantId && !allowNoTenant) { + ctx.throw(403, "Tenant id not set") + } + + return tenantId +} module.exports = ( allowQueryStringPatterns, @@ -11,15 +43,10 @@ module.exports = ( const noTenancyOptions = buildMatcherRegex(noTenancyPatterns) return async function (ctx, next) { - return cls.run(async () => { - const allowNoTenant = - opts.noTenancyRequired || !!matches(ctx, noTenancyOptions) - const allowQs = !!matches(ctx, allowQsOptions) - const tenantId = setTenantId(ctx, { allowQs, allowNoTenant }) - setGlobalDB(tenantId) - const res = await next() - await closeTenancy() - return res - }) + const allowNoTenant = + opts.noTenancyRequired || !!matches(ctx, noTenancyOptions) + const allowQs = !!matches(ctx, allowQsOptions) + const tenantId = getTenantID(ctx, { allowQs, allowNoTenant }) + return doInTenant(tenantId, next) } } diff --git a/packages/backend-core/tsconfig.build.json b/packages/backend-core/tsconfig.build.json index 57783aab7a..b5f8607023 100644 --- a/packages/backend-core/tsconfig.build.json +++ b/packages/backend-core/tsconfig.build.json @@ -3,8 +3,6 @@ "extends": "./tsconfig.json", "exclude": [ "node_modules", - "dist/**/*", - "**/*.json", "**/*.spec.js", "**/*.spec.ts" ] diff --git a/packages/backend-core/tsconfig.json b/packages/backend-core/tsconfig.json index 373b8440f7..5f9000c18f 100644 --- a/packages/backend-core/tsconfig.json +++ b/packages/backend-core/tsconfig.json @@ -29,7 +29,6 @@ ], "exclude": [ "node_modules", - "dist/**/*", "**/*.spec.js", // "**/*.spec.ts" // don't exclude spec.ts files for editor support ] diff --git a/packages/server/src/api/controllers/dev.js b/packages/server/src/api/controllers/dev.js index 1146013c3b..e78fe7ddce 100644 --- a/packages/server/src/api/controllers/dev.js +++ b/packages/server/src/api/controllers/dev.js @@ -131,5 +131,5 @@ exports.getBudibaseVersion = async ctx => { ctx.body = { version, } - events.org.versionChecked(version) + await events.org.versionChecked(version) } diff --git a/packages/server/src/api/controllers/table/external.js b/packages/server/src/api/controllers/table/external.js index 5a7239e90c..34319c5bff 100644 --- a/packages/server/src/api/controllers/table/external.js +++ b/packages/server/src/api/controllers/table/external.js @@ -315,6 +315,6 @@ exports.bulkImport = async function (ctx) { await handleRequest(DataSourceOperation.BULK_CREATE, table._id, { rows, }) - events.row.import(table, "csv", rows.length) + await events.rows.imported(table, "csv", rows.length) return table } diff --git a/packages/server/src/migrations/functions/backfill/account.ts b/packages/server/src/migrations/functions/backfill/account.ts deleted file mode 100644 index a0f10d218f..0000000000 --- a/packages/server/src/migrations/functions/backfill/account.ts +++ /dev/null @@ -1,16 +0,0 @@ -// TODO: Add migrations to account portal - -import { events, db } from "@budibase/backend-core" -import { Account } from "@budibase/types" - -export const backfill = async (appDb: any) => { - const accounts: Account[] = [] - - for (const account of accounts) { - events.account.created(account) - - if (account.verified) { - events.account.verified(account) - } - } -} diff --git a/packages/server/src/migrations/functions/backfill/global/configs.ts b/packages/server/src/migrations/functions/backfill/global/configs.ts index 5c3644fca5..4705e49a38 100644 --- a/packages/server/src/migrations/functions/backfill/global/configs.ts +++ b/packages/server/src/migrations/functions/backfill/global/configs.ts @@ -36,7 +36,7 @@ export const backfill = async (globalDb: any) => { if (isOIDCConfig(config)) { await events.auth.SSOCreated("oidc") if (config.config.configs[0].activated) { - events.auth.SSOActivated("oidc") + await events.auth.SSOActivated("oidc") } } if (isSettingsConfig(config)) { diff --git a/packages/types/src/events/auth.ts b/packages/types/src/events/auth.ts index aaa2cdd834..e5dfb224bc 100644 --- a/packages/types/src/events/auth.ts +++ b/packages/types/src/events/auth.ts @@ -1,4 +1,4 @@ -export type LoginSource = "local" | "sso" +export type LoginSource = "local" | "google" | "oidc" export type SSOType = "oidc" | "google" export interface LoginEvent { diff --git a/packages/worker/src/api/controllers/global/auth.ts b/packages/worker/src/api/controllers/global/auth.ts index 4555ef05d0..71d0afbb52 100644 --- a/packages/worker/src/api/controllers/global/auth.ts +++ b/packages/worker/src/api/controllers/global/auth.ts @@ -14,8 +14,9 @@ const { isMultiTenant, } = require("@budibase/backend-core/tenancy") const env = require("../../../environment") -const { events, users: usersCore } = require("@budibase/backend-core") +import { events, users as usersCore, context } from "@budibase/backend-core" import { users } from "../../../sdk" +import { User } from "@budibase/types" const ssoCallbackUrl = async (config: any, type: any) => { // incase there is a callback URL from before @@ -71,7 +72,9 @@ export const authenticate = async (ctx: any, next: any) => { "local", async (err: any, user: any, info: any) => { await authInternal(ctx, user, err, info) - await events.auth.login("local") + await context.doInUserContext(user, async () => { + await events.auth.login("local") + }) ctx.status = 200 } )(ctx, next) @@ -105,7 +108,7 @@ export const reset = async (ctx: any) => { ) } try { - const user = await usersCore.getGlobalUserByEmail(email) + const user = (await usersCore.getGlobalUserByEmail(email)) as User // only if user exists, don't error though if they don't if (user) { await sendEmail(email, EmailTemplatePurpose.PASSWORD_RECOVERY, { @@ -212,7 +215,9 @@ export const googleAuth = async (ctx: any, next: any) => { { successRedirect: "/", failureRedirect: "/error" }, async (err: any, user: any, info: any) => { await authInternal(ctx, user, err, info) - await events.auth.login("google") + await context.doInUserContext(user, async () => { + await events.auth.login("google") + }) ctx.redirect("/") } )(ctx, next) @@ -256,7 +261,9 @@ export const oidcAuth = async (ctx: any, next: any) => { { successRedirect: "/", failureRedirect: "/error" }, async (err: any, user: any, info: any) => { await authInternal(ctx, user, err, info) - await events.auth.login("oidc") + await context.doInUserContext(user, async () => { + await events.auth.login("oidc") + }) ctx.redirect("/") } )(ctx, next) From b3f04e7e8fa49ff68eaef69337aafd0af3377d84 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 24 May 2022 20:01:13 +0100 Subject: [PATCH 43/75] Identity updates --- packages/backend-core/src/context/index.js | 80 ++++++++++++------- packages/backend-core/src/events/events.ts | 2 +- .../backend-core/src/events/identification.ts | 74 ++++++++++++++--- packages/backend-core/src/events/index.ts | 1 + .../middleware/passport/third-party-common.js | 6 +- packages/backend-core/tsconfig.build.json | 1 + packages/backend-core/tsconfig.json | 1 + packages/types/src/documents/global/config.ts | 1 + packages/types/src/documents/global/user.ts | 3 + packages/types/src/events/identification.ts | 9 ++- .../src/api/controllers/global/users.ts | 14 ++-- packages/worker/src/sdk/users/users.ts | 9 ++- 12 files changed, 146 insertions(+), 55 deletions(-) diff --git a/packages/backend-core/src/context/index.js b/packages/backend-core/src/context/index.js index ae9c7b92ea..7c7c5c45e6 100644 --- a/packages/backend-core/src/context/index.js +++ b/packages/backend-core/src/context/index.js @@ -76,17 +76,10 @@ exports.isMultiTenant = () => { exports.doInTenant = (tenantId, task) => { // the internal function is so that we can re-use an existing // context - don't want to close DB on a parent context - async function internal(opts = { existing: false, user: undefined }) { - // preserve the user - if (user) { - exports.setUser(user) - } + async function internal(opts = { existing: false }) { // set the tenant id if (!opts.existing) { - cls.setOnContext(ContextKeys.TENANT_ID, tenantId) - if (env.USE_COUCH) { - exports.setGlobalDB(tenantId) - } + exports.updateTenantId(tenantId) } try { @@ -102,15 +95,14 @@ exports.doInTenant = (tenantId, task) => { } } - const user = cls.getFromContext(ContextKeys.USER) const using = cls.getFromContext(ContextKeys.IN_USE) if (using && cls.getFromContext(ContextKeys.TENANT_ID) === tenantId) { cls.setOnContext(ContextKeys.IN_USE, using + 1) - return internal({ existing: true, user }) + return internal({ existing: true }) } else { return cls.run(async () => { cls.setOnContext(ContextKeys.IN_USE, 1) - return internal({ existing: false, user }) + return internal() }) } } @@ -146,19 +138,19 @@ exports.doInAppContext = (appId, task) => { throw new Error("appId is required") } + const user = exports.getUser() + // the internal function is so that we can re-use an existing // context - don't want to close DB on a parent context async function internal(opts = { existing: false, user: undefined }) { - // preserve the user - if (user) { - exports.setUser(user) - } // set the app tenant id if (!opts.existing) { setAppTenantId(appId) } // set the app ID cls.setOnContext(ContextKeys.APP_ID, appId) + // preserve the user + exports.setUser(user) try { // invoke the task return await task() @@ -171,29 +163,56 @@ exports.doInAppContext = (appId, task) => { } } } - const user = cls.getFromContext(ContextKeys.USER) const using = cls.getFromContext(ContextKeys.IN_USE) if (using && cls.getFromContext(ContextKeys.APP_ID) === appId) { cls.setOnContext(ContextKeys.IN_USE, using + 1) - return internal({ existing: true, user }) + return internal({ existing: true }) } else { return cls.run(async () => { cls.setOnContext(ContextKeys.IN_USE, 1) - return internal({ existing: false, user }) + return internal() }) } } exports.doInUserContext = (user, task) => { - return cls.run(() => { - let tenantId = user.tenantId - if (!tenantId) { - tenantId = exports.getTenantId() + if (!user) { + throw new Error("user is required") + } + + async function internal(opts = { existing: false }) { + if (!opts.existing) { + cls.setOnContext(ContextKeys.USER, user) + // set the tenant so that doInTenant will preserve user + if (user.tenantId) { + exports.updateTenantId(user.tenantId) + } } - cls.setOnContext(ContextKeys.TENANT_ID, tenantId) - exports.setUser(user) - return task() - }) + + try { + // invoke the task + return await task() + } finally { + const using = cls.getFromContext(ContextKeys.IN_USE) + if (!using || using <= 1) { + exports.setUser(null) + } else { + cls.setOnContext(using - 1) + } + } + } + + const existing = cls.getFromContext(ContextKeys.USER) + const using = cls.getFromContext(ContextKeys.IN_USE) + if (using && existing && existing._id === user._id) { + cls.setOnContext(ContextKeys.IN_USE, using + 1) + return internal({ existing: true }) + } else { + return cls.run(async () => { + cls.setOnContext(ContextKeys.IN_USE, 1) + return internal({ existing: false }) + }) + } } exports.setUser = user => { @@ -202,8 +221,7 @@ exports.setUser = user => { exports.getUser = () => { try { - const user = cls.getFromContext(ContextKeys.USER) - return user + return cls.getFromContext(ContextKeys.USER) } catch (e) { // do nothing - user is not in context } @@ -211,7 +229,9 @@ exports.getUser = () => { exports.updateTenantId = tenantId => { cls.setOnContext(ContextKeys.TENANT_ID, tenantId) - exports.setGlobalDB(tenantId) + if (env.USE_COUCH) { + exports.setGlobalDB(tenantId) + } } exports.updateAppId = async appId => { diff --git a/packages/backend-core/src/events/events.ts b/packages/backend-core/src/events/events.ts index 21dfba6090..4ae90be745 100644 --- a/packages/backend-core/src/events/events.ts +++ b/packages/backend-core/src/events/events.ts @@ -4,6 +4,6 @@ import * as identification from "./identification" export const publishEvent = async (event: Event, properties: any) => { // in future this should use async events via a distributed queue. - const identity = identification.getCurrentIdentity() + const identity = await identification.getCurrentIdentity() await processors.processEvent(event, identity, properties) } diff --git a/packages/backend-core/src/events/identification.ts b/packages/backend-core/src/events/identification.ts index 0b7e1f12f9..d22cfe7720 100644 --- a/packages/backend-core/src/events/identification.ts +++ b/packages/backend-core/src/events/identification.ts @@ -11,20 +11,25 @@ import { BudibaseIdentity, isCloudAccount, isSSOAccount, + TenantIdentity, + SettingsConfig, } from "@budibase/types" import { analyticsProcessor } from "./processors" +import * as dbUtils from "../db/utils" +import { Configs } from "../constants" +import * as hashing from "../hashing" -export const getCurrentIdentity = (): Identity => { +export const getCurrentIdentity = async (): Promise => { const user: SessionUser | undefined = context.getUser() - const tenantId = context.getTenantId() + let tenantId = context.getTenantId() let id: string if (user) { id = user._id - } else if (env.SELF_HOSTED) { - id = "installationId" // TODO } else { - id = tenantId + const global = await getGlobalIdentifiers(tenantId) + id = global.id + tenantId = global.tenantId } return { @@ -33,6 +38,55 @@ export const getCurrentIdentity = (): Identity => { } } +const getGlobalId = async (): Promise => { + const db = context.getGlobalDB() + const config: SettingsConfig = await dbUtils.getScopedFullConfig(db, { + type: Configs.SETTINGS, + }) + if (config.config.globalId) { + return config.config.globalId + } else { + const globalId = `global_${hashing.newid()}` + config.config.globalId = globalId + await db.put(config) + return globalId + } +} + +const getGlobalIdentifiers = async ( + tenantId: string +): Promise<{ id: string; tenantId: string }> => { + if (env.SELF_HOSTED) { + const globalId = await getGlobalId() + return { + id: globalId, + tenantId: `${globalId}-${tenantId}`, + } + } else { + // tenant id's in the cloud are already unique + return { + id: tenantId, + tenantId: tenantId, + } + } +} + +const getHostingFromEnv = () => { + return env.SELF_HOSTED ? Hosting.SELF : Hosting.CLOUD +} + +export const identifyTenant = async (tenantId: string) => { + const global = await getGlobalIdentifiers(tenantId) + + const identity: TenantIdentity = { + id: global.id, + tenantId: global.tenantId, + hosting: getHostingFromEnv(), + type: IdentityType.TENANT, + } + await identify(identity) +} + export const identifyUser = async (user: User) => { const id = user._id as string const tenantId = user.tenantId @@ -40,7 +94,7 @@ export const identifyUser = async (user: User) => { const type = IdentityType.USER let builder = user.builder?.global let admin = user.admin?.global - let authType = user.providerType ? user.providerType : "password" + let providerType = user.providerType const identity: BudibaseIdentity = { id, @@ -49,7 +103,7 @@ export const identifyUser = async (user: User) => { type, builder, admin, - authType, + providerType, } await identify(identity) @@ -60,9 +114,7 @@ export const identifyAccount = async (account: Account) => { const tenantId = account.tenantId const hosting = account.hosting let type = IdentityType.ACCOUNT - let authType = isSSOAccount(account) - ? (account.providerType as string) - : "password" + let providerType = isSSOAccount(account) ? account.providerType : undefined if (isCloudAccount(account)) { if (account.budibaseUserId) { @@ -77,7 +129,7 @@ export const identifyAccount = async (account: Account) => { tenantId, hosting, type, - authType, + providerType, verified: account.verified, profession: account.profession, companySize: account.size, diff --git a/packages/backend-core/src/events/index.ts b/packages/backend-core/src/events/index.ts index e850ea8b26..ae5b66023d 100644 --- a/packages/backend-core/src/events/index.ts +++ b/packages/backend-core/src/events/index.ts @@ -1,6 +1,7 @@ import { processors } from "./processors" export * from "./publishers" export * as analytics from "./analytics" +export * as identification from "./identification" export const shutdown = () => { processors.shutdown() diff --git a/packages/backend-core/src/middleware/passport/third-party-common.js b/packages/backend-core/src/middleware/passport/third-party-common.js index 44d80f7ee7..845362200f 100644 --- a/packages/backend-core/src/middleware/passport/third-party-common.js +++ b/packages/backend-core/src/middleware/passport/third-party-common.js @@ -79,14 +79,14 @@ exports.authenticateThirdParty = async function ( dbUser.forceResetPassword = false // create or sync the user - let response try { - response = await saveUserFn(dbUser, false, false) + await saveUserFn(dbUser, false, false) } catch (err) { return authError(done, err) } - dbUser._rev = response.rev + // now that we're sure user exists, load them from the db + dbUser = await users.getGlobalUserByEmail(thirdPartyUser.email) // authenticate const sessionId = newid() diff --git a/packages/backend-core/tsconfig.build.json b/packages/backend-core/tsconfig.build.json index b5f8607023..602d4b3a7d 100644 --- a/packages/backend-core/tsconfig.build.json +++ b/packages/backend-core/tsconfig.build.json @@ -3,6 +3,7 @@ "extends": "./tsconfig.json", "exclude": [ "node_modules", + "dist/**/*", "**/*.spec.js", "**/*.spec.ts" ] diff --git a/packages/backend-core/tsconfig.json b/packages/backend-core/tsconfig.json index 5f9000c18f..373b8440f7 100644 --- a/packages/backend-core/tsconfig.json +++ b/packages/backend-core/tsconfig.json @@ -29,6 +29,7 @@ ], "exclude": [ "node_modules", + "dist/**/*", "**/*.spec.js", // "**/*.spec.ts" // don't exclude spec.ts files for editor support ] diff --git a/packages/types/src/documents/global/config.ts b/packages/types/src/documents/global/config.ts index b37e09bb9d..9ad80e4eef 100644 --- a/packages/types/src/documents/global/config.ts +++ b/packages/types/src/documents/global/config.ts @@ -19,6 +19,7 @@ export interface SettingsConfig extends Config { company: string logoUrl: string platformUrl: string + globalId?: string } } diff --git a/packages/types/src/documents/global/user.ts b/packages/types/src/documents/global/user.ts index 1cf3025ed9..61a942fdb8 100644 --- a/packages/types/src/documents/global/user.ts +++ b/packages/types/src/documents/global/user.ts @@ -10,6 +10,9 @@ export interface User extends Document { } providerType?: string tenantId: string + email: string + password?: string + status?: string } export interface UserRoles { diff --git a/packages/types/src/events/identification.ts b/packages/types/src/events/identification.ts index 0429882ecd..e35167d00f 100644 --- a/packages/types/src/events/identification.ts +++ b/packages/types/src/events/identification.ts @@ -11,10 +11,15 @@ export interface Identity { tenantId: string } -export interface UserIdentity extends Identity { +export interface TenantIdentity extends Identity { hosting: Hosting type: IdentityType - authType: string +} + +export interface UserIdentity extends TenantIdentity { + hosting: Hosting + type: IdentityType + providerType?: string } export interface BudibaseIdentity extends UserIdentity { diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index acd5bf8c89..d0a96b5362 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -2,13 +2,15 @@ import { EmailTemplatePurpose } from "../../../constants" import { checkInviteCode } from "../../../utilities/redis" import { sendEmail } from "../../../utilities/email" import { users } from "../../../sdk" +import { User } from "@budibase/types" +import { events } from "@budibase/backend-core" +import { getGlobalDB } from "@budibase/backend-core/dist/src/context" const { errors, users: usersCore, tenancy, db: dbUtils, - events, } = require("@budibase/backend-core") export const save = async (ctx: any) => { @@ -48,10 +50,9 @@ export const adminUser = async (ctx: any) => { ctx.throw(403, "You cannot initialise once a global user has been created.") } - const user = { + const user: User = { email: email, password: password, - createdAt: Date.now(), roles: {}, builder: { global: true, @@ -65,6 +66,7 @@ export const adminUser = async (ctx: any) => { ctx.body = await tenancy.doInTenant(tenantId, async () => { return users.save(user, hashPassword, requirePassword) }) + await events.identification.identifyTenant(tenantId) } catch (err: any) { ctx.throw(err.status || 400, err) } @@ -132,15 +134,17 @@ export const inviteAccept = async (ctx: any) => { // info is an extension of the user object that was stored by global const { email, info }: any = await checkInviteCode(inviteCode) ctx.body = await tenancy.doInTenant(info.tenantId, async () => { - const user = await users.save({ + const saved = await users.save({ firstName, lastName, password, email, ...info, }) + const db = getGlobalDB() + const user = await db.get(saved._id) await events.user.inviteAccepted(user) - return user + return saved }) } catch (err: any) { if (err.code === errors.codes.USAGE_LIMIT_EXCEEDED) { diff --git a/packages/worker/src/sdk/users/users.ts b/packages/worker/src/sdk/users/users.ts index 0d29dfd190..b9218b1dea 100644 --- a/packages/worker/src/sdk/users/users.ts +++ b/packages/worker/src/sdk/users/users.ts @@ -2,6 +2,7 @@ import env from "../../environment" import { quotas } from "@budibase/pro" import * as apps from "../../utilities/appService" import * as eventHelpers from "./events" +import { User } from "@budibase/types" const { tenancy, @@ -16,6 +17,8 @@ const { HTTPError, } = require("@budibase/backend-core") +import { events } from "@budibase/backend-core" + /** * Retrieves all users from the current tenancy. */ @@ -51,7 +54,7 @@ export const getUser = async (userId: string) => { } export const save = async ( - user: any, + user: User, hashPassword = true, requirePassword = true ) => { @@ -97,7 +100,7 @@ export const save = async ( } if (!_id) { - _id = dbUtils.generateGlobalUserID(email) + _id = dbUtils.generateGlobalUserID() } user = { @@ -130,7 +133,7 @@ export const save = async ( user._rev = response.rev await eventHelpers.handleSaveEvents(user, dbUser) - + await events.identification.identifyUser(user) await tenancy.tryAddTenant(tenantId, _id, email) await cache.user.invalidateUser(response.id) // let server know to sync user From 6a4c601ae8685978006cf64cbf1b914b7b790a56 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 25 May 2022 00:15:52 +0100 Subject: [PATCH 44/75] timestamp support, logging updates, migration progress indicators, identification updates --- packages/backend-core/context.js | 2 ++ packages/backend-core/src/events/events.ts | 8 +++-- .../backend-core/src/events/identification.ts | 7 ++-- .../events/processors/AnalyticsProcessor.ts | 5 +-- .../src/events/processors/LoggingProcessor.ts | 6 +++- .../src/events/processors/PosthogProcessor.ts | 9 +++-- .../src/events/processors/Processors.ts | 5 +-- .../src/events/processors/types.ts | 7 +++- packages/backend-core/src/migrations/index.js | 33 ++++++++++++++++--- .../src/migrations/functions/backfill/app.ts | 8 +++++ .../migrations/functions/backfill/global.ts | 10 ++++++ .../functions/backfill/global/users.ts | 1 + packages/server/src/migrations/index.ts | 14 +++++--- packages/types/src/events/identification.ts | 3 +- 14 files changed, 94 insertions(+), 24 deletions(-) diff --git a/packages/backend-core/context.js b/packages/backend-core/context.js index 4ba3e6bffe..0d86c530a7 100644 --- a/packages/backend-core/context.js +++ b/packages/backend-core/context.js @@ -6,6 +6,7 @@ const { updateAppId, doInAppContext, doInUserContext, + doInTenant, } = require("./src/context") module.exports = { @@ -16,4 +17,5 @@ module.exports = { updateAppId, doInAppContext, doInUserContext, + doInTenant, } diff --git a/packages/backend-core/src/events/events.ts b/packages/backend-core/src/events/events.ts index 4ae90be745..8d81608365 100644 --- a/packages/backend-core/src/events/events.ts +++ b/packages/backend-core/src/events/events.ts @@ -2,8 +2,12 @@ import { Event } from "@budibase/types" import { processors } from "./processors" import * as identification from "./identification" -export const publishEvent = async (event: Event, properties: any) => { +export const publishEvent = async ( + event: Event, + properties: any, + timestamp?: string | number +) => { // in future this should use async events via a distributed queue. const identity = await identification.getCurrentIdentity() - await processors.processEvent(event, identity, properties) + await processors.processEvent(event, identity, properties, timestamp) } diff --git a/packages/backend-core/src/events/identification.ts b/packages/backend-core/src/events/identification.ts index d22cfe7720..d6aca63e68 100644 --- a/packages/backend-core/src/events/identification.ts +++ b/packages/backend-core/src/events/identification.ts @@ -23,18 +23,22 @@ export const getCurrentIdentity = async (): Promise => { const user: SessionUser | undefined = context.getUser() let tenantId = context.getTenantId() let id: string + let type: IdentityType if (user) { id = user._id + type = IdentityType.USER } else { const global = await getGlobalIdentifiers(tenantId) id = global.id tenantId = global.tenantId + type = IdentityType.TENANT } return { id, tenantId, + type, } } @@ -113,14 +117,13 @@ export const identifyAccount = async (account: Account) => { let id = account.accountId const tenantId = account.tenantId const hosting = account.hosting - let type = IdentityType.ACCOUNT + let type = IdentityType.USER let providerType = isSSOAccount(account) ? account.providerType : undefined if (isCloudAccount(account)) { if (account.budibaseUserId) { // use the budibase user as the id if set id = account.budibaseUserId - type = IdentityType.USER } } diff --git a/packages/backend-core/src/events/processors/AnalyticsProcessor.ts b/packages/backend-core/src/events/processors/AnalyticsProcessor.ts index d2f82d59fb..63b7d3ed97 100644 --- a/packages/backend-core/src/events/processors/AnalyticsProcessor.ts +++ b/packages/backend-core/src/events/processors/AnalyticsProcessor.ts @@ -16,13 +16,14 @@ export default class AnalyticsProcessor implements EventProcessor { async processEvent( event: Event, identity: Identity, - properties: any + properties: any, + timestamp?: string ): Promise { if (!(await analytics.enabled())) { return } if (this.posthog) { - this.posthog.processEvent(event, identity, properties) + this.posthog.processEvent(event, identity, properties, timestamp) } } diff --git a/packages/backend-core/src/events/processors/LoggingProcessor.ts b/packages/backend-core/src/events/processors/LoggingProcessor.ts index 1094411298..102a50f8d9 100644 --- a/packages/backend-core/src/events/processors/LoggingProcessor.ts +++ b/packages/backend-core/src/events/processors/LoggingProcessor.ts @@ -1,5 +1,6 @@ import { Event, Identity } from "@budibase/types" import { EventProcessor } from "./types" +import env from "../../environment" export default class LoggingProcessor implements EventProcessor { async processEvent( @@ -7,8 +8,11 @@ export default class LoggingProcessor implements EventProcessor { identity: Identity, properties: any ): Promise { + if (env.SELF_HOSTED && !env.isDev()) { + return + } console.log( - `[audit] [tenant=${identity.tenantId}] [identity=${identity.id}] ${event}` + `[audit] [tenant=${identity.tenantId}] [identityType=${identity.type}] [identity=${identity.id}] ${event}` ) } diff --git a/packages/backend-core/src/events/processors/PosthogProcessor.ts b/packages/backend-core/src/events/processors/PosthogProcessor.ts index 499f69f581..dbbb034428 100644 --- a/packages/backend-core/src/events/processors/PosthogProcessor.ts +++ b/packages/backend-core/src/events/processors/PosthogProcessor.ts @@ -15,9 +15,14 @@ export default class PosthogProcessor implements EventProcessor { async processEvent( event: Event, identity: Identity, - properties: any + properties: any, + timestamp?: string | number ): Promise { - this.posthog.capture({ distinctId: identity.id, event, properties }) + const payload: any = { distinctId: identity.id, event, properties } + if (timestamp) { + payload.timestamp = new Date(timestamp) + } + this.posthog.capture(payload) } async identify(identity: Identity) { diff --git a/packages/backend-core/src/events/processors/Processors.ts b/packages/backend-core/src/events/processors/Processors.ts index ed23af4780..d2ee44a4a6 100644 --- a/packages/backend-core/src/events/processors/Processors.ts +++ b/packages/backend-core/src/events/processors/Processors.ts @@ -12,10 +12,11 @@ export default class Processor implements EventProcessor { async processEvent( event: Event, identity: Identity, - properties: any + properties: any, + timestamp?: string | number ): Promise { for (const eventProcessor of this.processors) { - await eventProcessor.processEvent(event, identity, properties) + await eventProcessor.processEvent(event, identity, properties, timestamp) } } diff --git a/packages/backend-core/src/events/processors/types.ts b/packages/backend-core/src/events/processors/types.ts index 0be1606351..9b48b2beee 100644 --- a/packages/backend-core/src/events/processors/types.ts +++ b/packages/backend-core/src/events/processors/types.ts @@ -6,6 +6,11 @@ export enum EventProcessorType { } export interface EventProcessor { - processEvent(event: Event, identity: Identity, properties: any): Promise + processEvent( + event: Event, + identity: Identity, + properties: any, + timestamp?: string | number + ): Promise shutdown(): void } diff --git a/packages/backend-core/src/migrations/index.js b/packages/backend-core/src/migrations/index.js index eb51613e60..26be4c90bc 100644 --- a/packages/backend-core/src/migrations/index.js +++ b/packages/backend-core/src/migrations/index.js @@ -31,6 +31,13 @@ exports.runMigration = async (migration, options = {}) => { const tenantId = getTenantId() const migrationType = migration.type const migrationName = migration.name + const silent = migration.silent + + const log = message => { + if (!silent) { + console.log(message) + } + } // get the db to store the migration in let dbNames @@ -45,8 +52,14 @@ exports.runMigration = async (migration, options = {}) => { ) } + const length = dbNames.length + let count = 0 + // run the migration against each db for (const dbName of dbNames) { + count++ + const lengthStatement = length > 1 ? `[${count}/${length}]` : "" + await doWithDB(dbName, async db => { try { const doc = await exports.getMigrationsDoc(db) @@ -58,7 +71,7 @@ exports.runMigration = async (migration, options = {}) => { options.force[migrationType] && options.force[migrationType].includes(migrationName) ) { - console.log( + log( `[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Forcing` ) } else { @@ -67,12 +80,12 @@ exports.runMigration = async (migration, options = {}) => { } } - console.log( - `[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Running` + log( + `[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Running ${lengthStatement}` ) // run the migration with tenant context await migration.fn(db) - console.log( + log( `[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Complete` ) @@ -91,7 +104,6 @@ exports.runMigration = async (migration, options = {}) => { } exports.runMigrations = async (migrations, options = {}) => { - console.log("Running migrations") let tenantIds if (environment.MULTI_TENANCY) { if (!options.tenantIds || !options.tenantIds.length) { @@ -105,8 +117,19 @@ exports.runMigrations = async (migrations, options = {}) => { tenantIds = [DEFAULT_TENANT_ID] } + if (tenantIds.length > 1) { + console.log(`Checking migrations for ${tenantIds.length} tenants`) + } else { + console.log("Checking migrations") + } + + let count = 0 // for all tenants for (const tenantId of tenantIds) { + count++ + if (tenantIds.length > 1) { + console.log(`Progress [${count}/${tenantIds.length}]`) + } // for all migrations for (const migration of migrations) { // run the migration diff --git a/packages/server/src/migrations/functions/backfill/app.ts b/packages/server/src/migrations/functions/backfill/app.ts index ba8d889343..ec905d6efb 100644 --- a/packages/server/src/migrations/functions/backfill/app.ts +++ b/packages/server/src/migrations/functions/backfill/app.ts @@ -6,6 +6,7 @@ import * as queries from "./app/queries" import * as roles from "./app/roles" import * as tables from "./app/tables" import * as screens from "./app/screens" +import * as global from "./global" /** * Date: @@ -16,6 +17,13 @@ import * as screens from "./app/screens" */ export const run = async (appDb: any) => { + if (await global.isComplete()) { + // make sure new apps aren't backfilled + // return if the global migration for this tenant is complete + // which runs after the app migrations + return + } + await apps.backfill(appDb) await automations.backfill(appDb) await datasources.backfill(appDb) diff --git a/packages/server/src/migrations/functions/backfill/global.ts b/packages/server/src/migrations/functions/backfill/global.ts index a723e03a75..44aa29a561 100644 --- a/packages/server/src/migrations/functions/backfill/global.ts +++ b/packages/server/src/migrations/functions/backfill/global.ts @@ -1,6 +1,7 @@ import * as users from "./global/users" import * as rows from "./global/rows" import * as configs from "./global/configs" +import { tenancy, events, migrations } from "@budibase/backend-core" /** * Date: @@ -11,7 +12,16 @@ import * as configs from "./global/configs" */ export const run = async (db: any) => { + const tenantId = tenancy.getTenantId() + await events.identification.identifyTenant(tenantId) + await users.backfill(db) await rows.backfill() await configs.backfill(db) } + +export const isComplete = async (): Promise => { + const globalDb = tenancy.getGlobalDB() + const migrationsDoc = await migrations.getMigrationsDoc(globalDb) + return !!migrationsDoc.event_global_backfill +} diff --git a/packages/server/src/migrations/functions/backfill/global/users.ts b/packages/server/src/migrations/functions/backfill/global/users.ts index 60ae9c699c..1816c970db 100644 --- a/packages/server/src/migrations/functions/backfill/global/users.ts +++ b/packages/server/src/migrations/functions/backfill/global/users.ts @@ -19,6 +19,7 @@ export const backfill = async (globalDb: any) => { const users = await getUsers(globalDb) for (const user of users) { + await events.identification.identifyUser(user) await events.user.created(user) if (user.admin?.global) { diff --git a/packages/server/src/migrations/index.ts b/packages/server/src/migrations/index.ts index 991cde650d..92e17ed24e 100644 --- a/packages/server/src/migrations/index.ts +++ b/packages/server/src/migrations/index.ts @@ -7,12 +7,14 @@ import * as appUrls from "./functions/appUrls" import * as developerQuota from "./functions/developerQuota" import * as publishedAppsQuota from "./functions/publishedAppsQuota" import * as backfill from "./functions/backfill" +import env from "../environment" export interface Migration { type: string name: string opts?: object fn: Function + silent?: boolean } /** @@ -58,16 +60,18 @@ export const MIGRATIONS: Migration[] = [ name: "published_apps_quota", fn: publishedAppsQuota.run, }, - { - type: migrations.MIGRATION_TYPES.GLOBAL, - name: "event_global_backfill", - fn: backfill.global.run, - }, { type: migrations.MIGRATION_TYPES.APP, name: "event_app_backfill", opts: { all: true }, fn: backfill.app.run, + silent: !!env.SELF_HOSTED, // reduce noisy logging + }, + { + type: migrations.MIGRATION_TYPES.GLOBAL, + name: "event_global_backfill", + fn: backfill.global.run, + silent: !!env.SELF_HOSTED, // reduce noisy logging }, ] diff --git a/packages/types/src/events/identification.ts b/packages/types/src/events/identification.ts index e35167d00f..1c20fd2ed6 100644 --- a/packages/types/src/events/identification.ts +++ b/packages/types/src/events/identification.ts @@ -2,18 +2,17 @@ import { Hosting } from "../core" export enum IdentityType { USER = "user", // cloud and self hosted users - ACCOUNT = "account", // self hosted accounts TENANT = "tenant", // cloud and self hosted tenants } export interface Identity { id: string tenantId: string + type: IdentityType } export interface TenantIdentity extends Identity { hosting: Hosting - type: IdentityType } export interface UserIdentity extends TenantIdentity { From 1eed4da35f378e3382959761cd23185236c9b111 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 25 May 2022 13:31:49 +0100 Subject: [PATCH 45/75] Add TS references to improve build watching --- package.json | 2 +- packages/backend-core/package.json | 1 - packages/backend-core/tsconfig.json | 1 + packages/server/package.json | 2 +- packages/server/tsconfig.build.json | 3 +++ packages/server/tsconfig.json | 3 +++ packages/worker/package.json | 2 +- packages/worker/tsconfig.json | 3 +++ 8 files changed, 13 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 14f98b716f..aca1762bea 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "setup": "node ./hosting/scripts/setup.js && yarn && yarn bootstrap && yarn build && yarn dev", "bootstrap": "lerna link && lerna bootstrap && ./scripts/link-dependencies.sh", "build": "lerna run build", - "build:watch": "lerna run build:watch --ignore @budibase/backend-core --stream --parallel", + "build:watch": "lerna run build:watch --stream --parallel", "release": "lerna publish patch --yes --force-publish && yarn release:pro", "release:develop": "lerna publish prerelease --yes --force-publish --dist-tag develop && yarn release:pro:develop", "release:pro": "bash scripts/pro/release.sh", diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index da038d4818..3f8b0bef81 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -8,7 +8,6 @@ "license": "GPL-3.0", "scripts": { "build": "rimraf dist/ && tsc -p tsconfig.build.json", - "build:watch": "rimraf dist/ && tsc -p tsconfig.build.json --watch --preserveWatchOutput", "test": "jest", "test:watch": "jest --watchAll" }, diff --git a/packages/backend-core/tsconfig.json b/packages/backend-core/tsconfig.json index 373b8440f7..3cc4e7b2b8 100644 --- a/packages/backend-core/tsconfig.json +++ b/packages/backend-core/tsconfig.json @@ -22,6 +22,7 @@ "resolveJsonModule": true, "incremental": true, "types": [ "node", "jest"], + "composite": true }, "include": [ "**/*.js", diff --git a/packages/server/package.json b/packages/server/package.json index fdaecef212..36ea8391ee 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -10,7 +10,7 @@ }, "scripts": { "build": "rimraf dist/ && tsc -p tsconfig.build.json && mv dist/src/* dist/ && rimraf dist/src/ && yarn postbuild", - "build:watch": "rimraf dist/ && tsc -p tsconfig.build.json --watch --preserveWatchOutput", + "build:watch": "rimraf dist/ && tsc --build tsconfig.build.json --watch --preserveWatchOutput", "debug": "yarn build && node --expose-gc --inspect=9222 dist/index.js", "postbuild": "copyfiles -u 1 src/**/*.svelte dist/ && copyfiles -u 1 src/**/*.hbs dist/ && copyfiles -u 1 src/**/*.json dist/", "test": "jest --coverage --maxWorkers=2", diff --git a/packages/server/tsconfig.build.json b/packages/server/tsconfig.build.json index 9c67698e91..836a726805 100644 --- a/packages/server/tsconfig.build.json +++ b/packages/server/tsconfig.build.json @@ -1,6 +1,9 @@ { // Used for building with tsc "extends": "./tsconfig.json", + "references": [ + { "path": "../backend-core/tsconfig.build.json" } + ], "exclude": [ "node_modules", "**/*.json", diff --git a/packages/server/tsconfig.json b/packages/server/tsconfig.json index d0fed6e824..4412d07b1d 100644 --- a/packages/server/tsconfig.json +++ b/packages/server/tsconfig.json @@ -16,6 +16,9 @@ "./src/**/*", "./src/module.d.ts" ], + "references": [ + { "path": "../backend-core/tsconfig.build.json" } + ], "exclude": [ "node_modules", "**/*.json", diff --git a/packages/worker/package.json b/packages/worker/package.json index 9fe6c82325..30d11ae11d 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -13,7 +13,7 @@ ], "scripts": { "build": "rimraf dist/ && tsc", - "build:watch": "rimraf dist/ && tsc --watch --preserveWatchOutput", + "build:watch": "rimraf dist/ && tsc --build --watch --preserveWatchOutput", "postbuild": "copyfiles -u 1 src/**/*.hbs dist/", "run:docker": "node dist/index.js", "build:docker": "docker build . -t worker-service --label version=$BUDIBASE_RELEASE_VERSION", diff --git a/packages/worker/tsconfig.json b/packages/worker/tsconfig.json index a66169ce54..080766172a 100644 --- a/packages/worker/tsconfig.json +++ b/packages/worker/tsconfig.json @@ -12,6 +12,9 @@ "incremental": true, "types": [ "node", "jest"], }, + "references": [ + { "path": "../backend-core/tsconfig.build.json" } + ], "include": [ "./src/**/*" ], From b986b689d6eddb1240a7a99d1cf5bbdf8593c47f Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 25 May 2022 21:32:08 +0100 Subject: [PATCH 46/75] Historical timestamps --- .../backend-core/src/events/identification.ts | 15 ++++++---- .../events/processors/AnalyticsProcessor.ts | 6 ++-- .../src/events/processors/PosthogProcessor.ts | 8 ++++-- .../backend-core/src/events/publishers/app.ts | 8 +++--- .../src/events/publishers/auth.ts | 8 +++--- .../src/events/publishers/automation.ts | 9 +++--- .../src/events/publishers/datasource.ts | 4 +-- .../src/events/publishers/email.ts | 7 +++-- .../src/events/publishers/layout.ts | 4 +-- .../backend-core/src/events/publishers/org.ts | 12 ++++---- .../src/events/publishers/query.ts | 8 ++++-- .../src/events/publishers/role.ts | 8 +++--- .../src/events/publishers/rows.ts | 4 +-- .../src/events/publishers/screen.ts | 4 +-- .../src/events/publishers/table.ts | 4 +-- .../src/events/publishers/user.ts | 23 +++++++++++---- .../src/events/publishers/view.ts | 15 ++++++---- .../src/migrations/functions/backfill/app.ts | 28 +++++++++++++------ .../migrations/functions/backfill/app/apps.ts | 14 ---------- .../functions/backfill/app/automations.ts | 16 +++++------ .../functions/backfill/app/datasources.ts | 12 ++++---- .../functions/backfill/app/layouts.ts | 12 ++++---- .../functions/backfill/app/queries.ts | 20 ++++++------- .../functions/backfill/app/roles.ts | 12 ++++---- .../functions/backfill/app/screens.ts | 12 ++++---- .../functions/backfill/app/tables.ts | 28 +++++++++---------- .../migrations/functions/backfill/global.ts | 24 ++++++++++++++-- .../functions/backfill/global/configs.ts | 25 +++++++++-------- .../functions/backfill/global/rows.ts | 20 ++++++++++--- .../functions/backfill/global/users.ts | 17 +++++------ packages/server/tsconfig.build.json | 3 +- packages/server/tsconfig.json | 3 -- packages/types/src/documents/document.ts | 2 +- packages/types/src/documents/global/user.ts | 5 ++-- packages/types/tsconfig.json | 1 + packages/worker/tsconfig.json | 3 +- 36 files changed, 226 insertions(+), 178 deletions(-) delete mode 100644 packages/server/src/migrations/functions/backfill/app/apps.ts diff --git a/packages/backend-core/src/events/identification.ts b/packages/backend-core/src/events/identification.ts index d6aca63e68..edf1b658bc 100644 --- a/packages/backend-core/src/events/identification.ts +++ b/packages/backend-core/src/events/identification.ts @@ -79,7 +79,10 @@ const getHostingFromEnv = () => { return env.SELF_HOSTED ? Hosting.SELF : Hosting.CLOUD } -export const identifyTenant = async (tenantId: string) => { +export const identifyTenant = async ( + tenantId: string, + timestamp?: string | number +) => { const global = await getGlobalIdentifiers(tenantId) const identity: TenantIdentity = { @@ -88,10 +91,10 @@ export const identifyTenant = async (tenantId: string) => { hosting: getHostingFromEnv(), type: IdentityType.TENANT, } - await identify(identity) + await identify(identity, timestamp) } -export const identifyUser = async (user: User) => { +export const identifyUser = async (user: User, timestamp?: string | number) => { const id = user._id as string const tenantId = user.tenantId const hosting = env.SELF_HOSTED ? Hosting.SELF : Hosting.CLOUD @@ -110,7 +113,7 @@ export const identifyUser = async (user: User) => { providerType, } - await identify(identity) + await identify(identity, timestamp) } export const identifyAccount = async (account: Account) => { @@ -141,6 +144,6 @@ export const identifyAccount = async (account: Account) => { await identify(identity) } -const identify = async (identity: Identity) => { - await analyticsProcessor.identify(identity) +const identify = async (identity: Identity, timestamp?: string | number) => { + await analyticsProcessor.identify(identity, timestamp) } diff --git a/packages/backend-core/src/events/processors/AnalyticsProcessor.ts b/packages/backend-core/src/events/processors/AnalyticsProcessor.ts index 63b7d3ed97..eaa5ae1135 100644 --- a/packages/backend-core/src/events/processors/AnalyticsProcessor.ts +++ b/packages/backend-core/src/events/processors/AnalyticsProcessor.ts @@ -17,7 +17,7 @@ export default class AnalyticsProcessor implements EventProcessor { event: Event, identity: Identity, properties: any, - timestamp?: string + timestamp?: string | number ): Promise { if (!(await analytics.enabled())) { return @@ -27,12 +27,12 @@ export default class AnalyticsProcessor implements EventProcessor { } } - async identify(identity: Identity) { + async identify(identity: Identity, timestamp?: string | number) { if (!(await analytics.enabled())) { return } if (this.posthog) { - this.posthog.identify(identity) + this.posthog.identify(identity, timestamp) } } diff --git a/packages/backend-core/src/events/processors/PosthogProcessor.ts b/packages/backend-core/src/events/processors/PosthogProcessor.ts index dbbb034428..54b5133dfb 100644 --- a/packages/backend-core/src/events/processors/PosthogProcessor.ts +++ b/packages/backend-core/src/events/processors/PosthogProcessor.ts @@ -25,8 +25,12 @@ export default class PosthogProcessor implements EventProcessor { this.posthog.capture(payload) } - async identify(identity: Identity) { - this.posthog.identify({ distinctId: identity.id, properties: identity }) + async identify(identity: Identity, timestamp?: string | number) { + const payload: any = { distinctId: identity.id, properties: identity } + if (timestamp) { + payload.timestamp = new Date(timestamp) + } + this.posthog.identify(payload) } shutdown() { diff --git a/packages/backend-core/src/events/publishers/app.ts b/packages/backend-core/src/events/publishers/app.ts index 00df1f18bc..b297b2a33e 100644 --- a/packages/backend-core/src/events/publishers/app.ts +++ b/packages/backend-core/src/events/publishers/app.ts @@ -15,9 +15,9 @@ import { AppExportedEvent, } from "@budibase/types" -export const created = async (app: App) => { +export const created = async (app: App, timestamp?: string | number) => { const properties: AppCreatedEvent = {} - await publishEvent(Event.APP_CREATED, properties) + await publishEvent(Event.APP_CREATED, properties, timestamp) } export async function updated(app: App) { @@ -30,9 +30,9 @@ export async function deleted(app: App) { await publishEvent(Event.APP_DELETED, properties) } -export async function published(app: App) { +export async function published(app: App, timestamp?: string | number) { const properties: AppPublishedEvent = {} - await publishEvent(Event.APP_PUBLISHED, properties) + await publishEvent(Event.APP_PUBLISHED, properties, timestamp) } export async function unpublished(app: App) { diff --git a/packages/backend-core/src/events/publishers/auth.ts b/packages/backend-core/src/events/publishers/auth.ts index 32af58258d..a1f15470f9 100644 --- a/packages/backend-core/src/events/publishers/auth.ts +++ b/packages/backend-core/src/events/publishers/auth.ts @@ -23,11 +23,11 @@ export async function logout() { await publishEvent(Event.AUTH_LOGOUT, properties) } -export async function SSOCreated(type: SSOType) { +export async function SSOCreated(type: SSOType, timestamp?: string | number) { const properties: SSOCreatedEvent = { type, } - await publishEvent(Event.AUTH_SSO_CREATED, properties) + await publishEvent(Event.AUTH_SSO_CREATED, properties, timestamp) } export async function SSOUpdated(type: SSOType) { @@ -37,11 +37,11 @@ export async function SSOUpdated(type: SSOType) { await publishEvent(Event.AUTH_SSO_UPDATED, properties) } -export async function SSOActivated(type: SSOType) { +export async function SSOActivated(type: SSOType, timestamp?: string | number) { const properties: SSOActivatedEvent = { type, } - await publishEvent(Event.AUTH_SSO_ACTIVATED, properties) + await publishEvent(Event.AUTH_SSO_ACTIVATED, properties, timestamp) } export async function SSODeactivated(type: SSOType) { diff --git a/packages/backend-core/src/events/publishers/automation.ts b/packages/backend-core/src/events/publishers/automation.ts index 2a20a90929..ba4477618b 100644 --- a/packages/backend-core/src/events/publishers/automation.ts +++ b/packages/backend-core/src/events/publishers/automation.ts @@ -12,9 +12,9 @@ import { AutomationTriggerUpdatedEvent, } from "@budibase/types" -export async function created(automation: Automation) { +export async function created(automation: Automation, timestamp?: string) { const properties: AutomationCreatedEvent = {} - await publishEvent(Event.AUTOMATION_CREATED, properties) + await publishEvent(Event.AUTOMATION_CREATED, properties, timestamp) } export async function deleted(automation: Automation) { @@ -29,10 +29,11 @@ export async function tested(automation: Automation) { export async function stepCreated( automation: Automation, - step: AutomationStep + step: AutomationStep, + timestamp?: string ) { const properties: AutomationStepCreatedEvent = {} - await publishEvent(Event.AUTOMATION_STEP_CREATED, properties) + await publishEvent(Event.AUTOMATION_STEP_CREATED, properties, timestamp) } export async function stepDeleted( diff --git a/packages/backend-core/src/events/publishers/datasource.ts b/packages/backend-core/src/events/publishers/datasource.ts index e5392d3bb8..95f47f7947 100644 --- a/packages/backend-core/src/events/publishers/datasource.ts +++ b/packages/backend-core/src/events/publishers/datasource.ts @@ -7,9 +7,9 @@ import { DatasourceDeletedEvent, } from "@budibase/types" -export async function created(datasource: Datasource) { +export async function created(datasource: Datasource, timestamp?: string) { const properties: DatasourceCreatedEvent = {} - await publishEvent(Event.DATASOURCE_CREATED, properties) + await publishEvent(Event.DATASOURCE_CREATED, properties, timestamp) } export async function updated(datasource: Datasource) { diff --git a/packages/backend-core/src/events/publishers/email.ts b/packages/backend-core/src/events/publishers/email.ts index 76745453f9..102cd2dedb 100644 --- a/packages/backend-core/src/events/publishers/email.ts +++ b/packages/backend-core/src/events/publishers/email.ts @@ -6,9 +6,12 @@ import { SMTPUpdatedEvent, } from "@budibase/types" -export async function SMTPCreated(config: SMTPConfig) { +export async function SMTPCreated( + config: SMTPConfig, + timestamp?: string | number +) { const properties: SMTPCreatedEvent = {} - await publishEvent(Event.EMAIL_SMTP_CREATED, properties) + await publishEvent(Event.EMAIL_SMTP_CREATED, properties, timestamp) } export async function SMTPUpdated(config: SMTPConfig) { diff --git a/packages/backend-core/src/events/publishers/layout.ts b/packages/backend-core/src/events/publishers/layout.ts index 109f23571a..c7d4f20225 100644 --- a/packages/backend-core/src/events/publishers/layout.ts +++ b/packages/backend-core/src/events/publishers/layout.ts @@ -6,9 +6,9 @@ import { LayoutDeletedEvent, } from "@budibase/types" -export async function created(layout: Layout) { +export async function created(layout: Layout, timestamp?: string) { const properties: LayoutCreatedEvent = {} - await publishEvent(Event.LAYOUT_CREATED, properties) + await publishEvent(Event.LAYOUT_CREATED, properties, timestamp) } export async function deleted(layout: Layout) { diff --git a/packages/backend-core/src/events/publishers/org.ts b/packages/backend-core/src/events/publishers/org.ts index ae2670d4da..075ab1592b 100644 --- a/packages/backend-core/src/events/publishers/org.ts +++ b/packages/backend-core/src/events/publishers/org.ts @@ -1,19 +1,19 @@ import { publishEvent } from "../events" import { Event, VersionCheckedEvent } from "@budibase/types" -export async function nameUpdated() { +export async function nameUpdated(timestamp?: string | number) { const properties = {} - await publishEvent(Event.ORG_NAME_UPDATED, properties) + await publishEvent(Event.ORG_NAME_UPDATED, properties, timestamp) } -export async function logoUpdated() { +export async function logoUpdated(timestamp?: string | number) { const properties = {} - await publishEvent(Event.ORG_LOGO_UPDATED, properties) + await publishEvent(Event.ORG_LOGO_UPDATED, properties, timestamp) } -export async function platformURLUpdated() { +export async function platformURLUpdated(timestamp?: string | number) { const properties = {} - await publishEvent(Event.ORG_PLATFORM_URL_UPDATED, properties) + await publishEvent(Event.ORG_PLATFORM_URL_UPDATED, properties, timestamp) } export async function versionChecked(version: number) { diff --git a/packages/backend-core/src/events/publishers/query.ts b/packages/backend-core/src/events/publishers/query.ts index e012d5c830..e659f5c2be 100644 --- a/packages/backend-core/src/events/publishers/query.ts +++ b/packages/backend-core/src/events/publishers/query.ts @@ -12,9 +12,13 @@ import { /* eslint-disable */ -export const created = async (datasource: Datasource, query: Query) => { +export const created = async ( + datasource: Datasource, + query: Query, + timestamp?: string +) => { const properties: QueryCreatedEvent = {} - await publishEvent(Event.QUERY_CREATED, properties) + await publishEvent(Event.QUERY_CREATED, properties, timestamp) } export const updated = async (datasource: Datasource, query: Query) => { diff --git a/packages/backend-core/src/events/publishers/role.ts b/packages/backend-core/src/events/publishers/role.ts index d257eadf2b..0002499814 100644 --- a/packages/backend-core/src/events/publishers/role.ts +++ b/packages/backend-core/src/events/publishers/role.ts @@ -12,9 +12,9 @@ import { /* eslint-disable */ -export async function created(role: Role) { +export async function created(role: Role, timestamp?: string) { const properties: RoleCreatedEvent = {} - await publishEvent(Event.ROLE_CREATED, properties) + await publishEvent(Event.ROLE_CREATED, properties, timestamp) } export async function updated(role: Role) { @@ -27,9 +27,9 @@ export async function deleted(role: Role) { await publishEvent(Event.ROLE_DELETED, properties) } -export async function assigned(user: User, role: string) { +export async function assigned(user: User, role: string, timestamp?: number) { const properties: RoleAssignedEvent = {} - await publishEvent(Event.ROLE_ASSIGNED, properties) + await publishEvent(Event.ROLE_ASSIGNED, properties, timestamp) } export async function unassigned(user: User, role: string) { diff --git a/packages/backend-core/src/events/publishers/rows.ts b/packages/backend-core/src/events/publishers/rows.ts index 0877b77b7a..4affd5d989 100644 --- a/packages/backend-core/src/events/publishers/rows.ts +++ b/packages/backend-core/src/events/publishers/rows.ts @@ -9,11 +9,11 @@ import { /* eslint-disable */ -export const created = async (count: number) => { +export const created = async (count: number, timestamp?: string) => { const properties: RowsCreatedEvent = { count, } - await publishEvent(Event.ROWS_CREATED, properties) + await publishEvent(Event.ROWS_CREATED, properties, timestamp) } export const imported = async ( diff --git a/packages/backend-core/src/events/publishers/screen.ts b/packages/backend-core/src/events/publishers/screen.ts index efb7d9538f..038f7bec3a 100644 --- a/packages/backend-core/src/events/publishers/screen.ts +++ b/packages/backend-core/src/events/publishers/screen.ts @@ -6,9 +6,9 @@ import { ScreenDeletedEvent, } from "@budibase/types" -export async function created(screen: Screen) { +export async function created(screen: Screen, timestamp?: string) { const properties: ScreenCreatedEvent = {} - await publishEvent(Event.SCREEN_CREATED, properties) + await publishEvent(Event.SCREEN_CREATED, properties, timestamp) } export async function deleted(screen: Screen) { diff --git a/packages/backend-core/src/events/publishers/table.ts b/packages/backend-core/src/events/publishers/table.ts index cfa4b28835..c1f050b25e 100644 --- a/packages/backend-core/src/events/publishers/table.ts +++ b/packages/backend-core/src/events/publishers/table.ts @@ -13,9 +13,9 @@ import { /* eslint-disable */ -export async function created(table: Table) { +export async function created(table: Table, timestamp?: string) { const properties: TableCreatedEvent = {} - await publishEvent(Event.TABLE_CREATED, properties) + await publishEvent(Event.TABLE_CREATED, properties, timestamp) } export async function updated(table: Table) { diff --git a/packages/backend-core/src/events/publishers/user.ts b/packages/backend-core/src/events/publishers/user.ts index eeea5234cb..c5c90fb4e1 100644 --- a/packages/backend-core/src/events/publishers/user.ts +++ b/packages/backend-core/src/events/publishers/user.ts @@ -17,9 +17,9 @@ import { /* eslint-disable */ -export async function created(user: User) { +export async function created(user: User, timestamp?: number) { const properties: UserCreatedEvent = {} - await publishEvent(Event.USER_CREATED, properties) + await publishEvent(Event.USER_CREATED, properties, timestamp) } export async function updated(user: User) { @@ -34,9 +34,13 @@ export async function deleted(user: User) { // PERMISSIONS -export async function permissionAdminAssigned(user: User) { +export async function permissionAdminAssigned(user: User, timestamp?: number) { const properties: UserPermissionAssignedEvent = {} - await publishEvent(Event.USER_PERMISSION_ADMIN_ASSIGNED, properties) + await publishEvent( + Event.USER_PERMISSION_ADMIN_ASSIGNED, + properties, + timestamp + ) } export async function permissionAdminRemoved(user: User) { @@ -44,9 +48,16 @@ export async function permissionAdminRemoved(user: User) { await publishEvent(Event.USER_PERMISSION_ADMIN_REMOVED, properties) } -export async function permissionBuilderAssigned(user: User) { +export async function permissionBuilderAssigned( + user: User, + timestamp?: number +) { const properties: UserPermissionAssignedEvent = {} - await publishEvent(Event.USER_PERMISSION_BUILDER_ASSIGNED, properties) + await publishEvent( + Event.USER_PERMISSION_BUILDER_ASSIGNED, + properties, + timestamp + ) } export async function permissionBuilderRemoved(user: User) { diff --git a/packages/backend-core/src/events/publishers/view.ts b/packages/backend-core/src/events/publishers/view.ts index ff0c8a7f0d..e6f82579cd 100644 --- a/packages/backend-core/src/events/publishers/view.ts +++ b/packages/backend-core/src/events/publishers/view.ts @@ -19,9 +19,9 @@ import { /* eslint-disable */ -export async function created(view: View) { +export async function created(view: View, timestamp?: string) { const properties: ViewCreatedEvent = {} - await publishEvent(Event.VIEW_CREATED, properties) + await publishEvent(Event.VIEW_CREATED, properties, timestamp) } export async function updated(view: View) { @@ -39,9 +39,9 @@ export async function exported(table: Table, format: TableExportFormat) { await publishEvent(Event.VIEW_EXPORTED, properties) } -export async function filterCreated() { +export async function filterCreated(timestamp?: string) { const properties: ViewFilterCreatedEvent = {} - await publishEvent(Event.VIEW_FILTER_CREATED, properties) + await publishEvent(Event.VIEW_FILTER_CREATED, properties, timestamp) } export async function filterUpdated() { @@ -54,9 +54,12 @@ export async function filterDeleted() { await publishEvent(Event.VIEW_FILTER_DELETED, properties) } -export async function calculationCreated(calculation: ViewCalculation) { +export async function calculationCreated( + calculation: ViewCalculation, + timestamp?: string +) { const properties: ViewCalculationCreatedEvent = {} - await publishEvent(Event.VIEW_CALCULATION_CREATED, properties) + await publishEvent(Event.VIEW_CALCULATION_CREATED, properties, timestamp) } export async function calculationUpdated() { diff --git a/packages/server/src/migrations/functions/backfill/app.ts b/packages/server/src/migrations/functions/backfill/app.ts index ec905d6efb..60ff953aa0 100644 --- a/packages/server/src/migrations/functions/backfill/app.ts +++ b/packages/server/src/migrations/functions/backfill/app.ts @@ -1,4 +1,3 @@ -import * as apps from "./app/apps" import * as automations from "./app/automations" import * as datasources from "./app/datasources" import * as layouts from "./app/layouts" @@ -7,6 +6,8 @@ import * as roles from "./app/roles" import * as tables from "./app/tables" import * as screens from "./app/screens" import * as global from "./global" +import { App } from "@budibase/types" +import { db as dbUtils, events } from "@budibase/backend-core" /** * Date: @@ -24,12 +25,21 @@ export const run = async (appDb: any) => { return } - await apps.backfill(appDb) - await automations.backfill(appDb) - await datasources.backfill(appDb) - await layouts.backfill(appDb) - await queries.backfill(appDb) - await roles.backfill(appDb) - await tables.backfill(appDb) - await screens.backfill(appDb) + const app: App = await appDb.get(dbUtils.DocumentTypes.APP_METADATA) + const timestamp = app.createdAt as string + + if (dbUtils.isDevAppID(app.appId)) { + await events.app.created(app, timestamp) + await automations.backfill(appDb, timestamp) + await datasources.backfill(appDb, timestamp) + await layouts.backfill(appDb, timestamp) + await queries.backfill(appDb, timestamp) + await roles.backfill(appDb, timestamp) + await tables.backfill(appDb, timestamp) + await screens.backfill(appDb, timestamp) + } + + if (dbUtils.isProdAppID(app.appId)) { + await events.app.published(app, timestamp) + } } diff --git a/packages/server/src/migrations/functions/backfill/app/apps.ts b/packages/server/src/migrations/functions/backfill/app/apps.ts deleted file mode 100644 index 35536198aa..0000000000 --- a/packages/server/src/migrations/functions/backfill/app/apps.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { events, db } from "@budibase/backend-core" -import { App } from "@budibase/types" - -export const backfill = async (appDb: any) => { - const app: App = await appDb.get(db.DocumentTypes.APP_METADATA) - - if (db.isDevAppID(app.appId)) { - await events.app.created(app) - } - - if (db.isProdAppID(app.appId)) { - await events.app.published(app) - } -} diff --git a/packages/server/src/migrations/functions/backfill/app/automations.ts b/packages/server/src/migrations/functions/backfill/app/automations.ts index ccdc1cbdb0..f455e10fc8 100644 --- a/packages/server/src/migrations/functions/backfill/app/automations.ts +++ b/packages/server/src/migrations/functions/backfill/app/automations.ts @@ -1,4 +1,4 @@ -import { events, db } from "@budibase/backend-core" +import { events } from "@budibase/backend-core" import { getAutomationParams } from "../../../../db/utils" import { Automation } from "@budibase/types" @@ -11,16 +11,14 @@ const getAutomations = async (appDb: any): Promise => { return response.rows.map((row: any) => row.doc) } -export const backfill = async (appDb: any) => { - if (db.isDevAppID(appDb.name)) { - const automations = await getAutomations(appDb) +export const backfill = async (appDb: any, timestamp: string) => { + const automations = await getAutomations(appDb) - for (const automation of automations) { - await events.automation.created(automation) + for (const automation of automations) { + await events.automation.created(automation, timestamp) - for (const step of automation.definition.steps) { - await events.automation.stepCreated(automation, step) - } + for (const step of automation.definition.steps) { + await events.automation.stepCreated(automation, step, timestamp) } } } diff --git a/packages/server/src/migrations/functions/backfill/app/datasources.ts b/packages/server/src/migrations/functions/backfill/app/datasources.ts index c1ad9d02e3..1919231a88 100644 --- a/packages/server/src/migrations/functions/backfill/app/datasources.ts +++ b/packages/server/src/migrations/functions/backfill/app/datasources.ts @@ -1,4 +1,4 @@ -import { events, db } from "@budibase/backend-core" +import { events } from "@budibase/backend-core" import { getDatasourceParams } from "../../../../db/utils" import { Datasource } from "@budibase/types" @@ -11,12 +11,10 @@ const getDatasources = async (appDb: any): Promise => { return response.rows.map((row: any) => row.doc) } -export const backfill = async (appDb: any) => { - if (db.isDevAppID(appDb.name)) { - const datasources: Datasource[] = await getDatasources(appDb) +export const backfill = async (appDb: any, timestamp: string) => { + const datasources: Datasource[] = await getDatasources(appDb) - for (const datasource of datasources) { - await events.datasource.created(datasource) - } + for (const datasource of datasources) { + await events.datasource.created(datasource, timestamp) } } diff --git a/packages/server/src/migrations/functions/backfill/app/layouts.ts b/packages/server/src/migrations/functions/backfill/app/layouts.ts index fb0db1b5a6..a02eeb7123 100644 --- a/packages/server/src/migrations/functions/backfill/app/layouts.ts +++ b/packages/server/src/migrations/functions/backfill/app/layouts.ts @@ -1,4 +1,4 @@ -import { events, db } from "@budibase/backend-core" +import { events } from "@budibase/backend-core" import { getLayoutParams } from "../../../../db/utils" import { Layout } from "@budibase/types" @@ -11,12 +11,10 @@ const getLayouts = async (appDb: any): Promise => { return response.rows.map((row: any) => row.doc) } -export const backfill = async (appDb: any) => { - if (db.isDevAppID(appDb.name)) { - const layouts: Layout[] = await getLayouts(appDb) +export const backfill = async (appDb: any, timestamp: string) => { + const layouts: Layout[] = await getLayouts(appDb) - for (const layout of layouts) { - await events.layout.created(layout) - } + for (const layout of layouts) { + await events.layout.created(layout, timestamp) } } diff --git a/packages/server/src/migrations/functions/backfill/app/queries.ts b/packages/server/src/migrations/functions/backfill/app/queries.ts index dfc236ec8b..9e83e6a371 100644 --- a/packages/server/src/migrations/functions/backfill/app/queries.ts +++ b/packages/server/src/migrations/functions/backfill/app/queries.ts @@ -1,4 +1,4 @@ -import { events, db } from "@budibase/backend-core" +import { events } from "@budibase/backend-core" import { getQueryParams } from "../../../../db/utils" import { Query, Datasource } from "@budibase/types" @@ -18,16 +18,14 @@ const getDatasource = async ( return appDb.get(datasourceId) } -export const backfill = async (appDb: any) => { - if (db.isDevAppID(appDb.name)) { - const queries: Query[] = await getQueries(appDb) +export const backfill = async (appDb: any, timestamp: string) => { + const queries: Query[] = await getQueries(appDb) - for (const query of queries) { - const datasource: Datasource = await getDatasource( - appDb, - query.datasourceId - ) - await events.query.created(datasource, query) - } + for (const query of queries) { + const datasource: Datasource = await getDatasource( + appDb, + query.datasourceId + ) + await events.query.created(datasource, query, timestamp) } } diff --git a/packages/server/src/migrations/functions/backfill/app/roles.ts b/packages/server/src/migrations/functions/backfill/app/roles.ts index 4672030b55..4fa57b4165 100644 --- a/packages/server/src/migrations/functions/backfill/app/roles.ts +++ b/packages/server/src/migrations/functions/backfill/app/roles.ts @@ -1,4 +1,4 @@ -import { events, db } from "@budibase/backend-core" +import { events } from "@budibase/backend-core" import { getRoleParams } from "../../../../db/utils" import { Role } from "@budibase/types" @@ -11,12 +11,10 @@ const getRoles = async (appDb: any): Promise => { return response.rows.map((row: any) => row.doc) } -export const backfill = async (appDb: any) => { - if (db.isDevAppID(appDb.name)) { - const roles = await getRoles(appDb) +export const backfill = async (appDb: any, timestamp: string) => { + const roles = await getRoles(appDb) - for (const role of roles) { - await events.role.created(role) - } + for (const role of roles) { + await events.role.created(role, timestamp) } } diff --git a/packages/server/src/migrations/functions/backfill/app/screens.ts b/packages/server/src/migrations/functions/backfill/app/screens.ts index 340187ae40..b1d9791c2d 100644 --- a/packages/server/src/migrations/functions/backfill/app/screens.ts +++ b/packages/server/src/migrations/functions/backfill/app/screens.ts @@ -1,4 +1,4 @@ -import { events, db } from "@budibase/backend-core" +import { events } from "@budibase/backend-core" import { getScreenParams } from "../../../../db/utils" import { Screen } from "@budibase/types" @@ -11,12 +11,10 @@ const getScreens = async (appDb: any): Promise => { return response.rows.map((row: any) => row.doc) } -export const backfill = async (appDb: any) => { - if (db.isDevAppID(appDb.name)) { - const screens = await getScreens(appDb) +export const backfill = async (appDb: any, timestamp: string) => { + const screens = await getScreens(appDb) - for (const screen of screens) { - await events.screen.created(screen) - } + for (const screen of screens) { + await events.screen.created(screen, timestamp) } } diff --git a/packages/server/src/migrations/functions/backfill/app/tables.ts b/packages/server/src/migrations/functions/backfill/app/tables.ts index c7401e921d..23d0dff702 100644 --- a/packages/server/src/migrations/functions/backfill/app/tables.ts +++ b/packages/server/src/migrations/functions/backfill/app/tables.ts @@ -1,4 +1,4 @@ -import { events, db } from "@budibase/backend-core" +import { events } from "@budibase/backend-core" import { getTableParams } from "../../../../db/utils" import { Table } from "@budibase/types" @@ -11,24 +11,22 @@ const getTables = async (appDb: any): Promise => { return response.rows.map((row: any) => row.doc) } -export const backfill = async (appDb: any) => { - if (db.isDevAppID(appDb.name)) { - const tables = await getTables(appDb) +export const backfill = async (appDb: any, timestamp: string) => { + const tables = await getTables(appDb) - for (const table of tables) { - await events.table.created(table) + for (const table of tables) { + await events.table.created(table, timestamp) - if (table.views) { - for (const view of Object.values(table.views)) { - await events.view.created(view) + if (table.views) { + for (const view of Object.values(table.views)) { + await events.view.created(view, timestamp) - if (view.calculation) { - await events.view.calculationCreated(view.calculation) - } + if (view.calculation) { + await events.view.calculationCreated(view.calculation, timestamp) + } - if (view.filters?.length) { - await events.view.filterCreated() - } + if (view.filters?.length) { + await events.view.filterCreated(timestamp) } } } diff --git a/packages/server/src/migrations/functions/backfill/global.ts b/packages/server/src/migrations/functions/backfill/global.ts index 44aa29a561..d723844d5a 100644 --- a/packages/server/src/migrations/functions/backfill/global.ts +++ b/packages/server/src/migrations/functions/backfill/global.ts @@ -13,11 +13,14 @@ import { tenancy, events, migrations } from "@budibase/backend-core" export const run = async (db: any) => { const tenantId = tenancy.getTenantId() - await events.identification.identifyTenant(tenantId) + const installTimestamp = (await getInstallTimestamp(db)) as number + await events.identification.identifyTenant(tenantId, installTimestamp) + await configs.backfill(db, installTimestamp) + + // users and rows provide their own timestamp await users.backfill(db) await rows.backfill() - await configs.backfill(db) } export const isComplete = async (): Promise => { @@ -25,3 +28,20 @@ export const isComplete = async (): Promise => { const migrationsDoc = await migrations.getMigrationsDoc(globalDb) return !!migrationsDoc.event_global_backfill } + +const getInstallTimestamp = async (db: any): Promise => { + const allUsers = await users.getUsers(db) + + // get the olders user timestamp + const timestamps = allUsers + .map(user => user.createdAt) + .filter(timestamp => !!timestamp) + .sort( + (a, b) => + new Date(a as number).getTime() - new Date(b as number).getTime() + ) + + if (timestamps.length) { + return timestamps[0] + } +} diff --git a/packages/server/src/migrations/functions/backfill/global/configs.ts b/packages/server/src/migrations/functions/backfill/global/configs.ts index 4705e49a38..94ac838e20 100644 --- a/packages/server/src/migrations/functions/backfill/global/configs.ts +++ b/packages/server/src/migrations/functions/backfill/global/configs.ts @@ -1,4 +1,4 @@ -import { events, db } from "@budibase/backend-core" +import { events, db as dbUtils } from "@budibase/backend-core" import { Config, isSMTPConfig, @@ -10,7 +10,7 @@ import env from "./../../../../environment" const getConfigs = async (globalDb: any): Promise => { const response = await globalDb.allDocs( - db.getConfigParams( + dbUtils.getConfigParams( {}, { include_docs: true, @@ -20,34 +20,37 @@ const getConfigs = async (globalDb: any): Promise => { return response.rows.map((row: any) => row.doc) } -export const backfill = async (globalDb: any) => { +export const backfill = async ( + globalDb: any, + timestamp: string | number | undefined +) => { const configs = await getConfigs(globalDb) for (const config of configs) { if (isSMTPConfig(config)) { - await events.email.SMTPCreated(config) + await events.email.SMTPCreated(config, timestamp) } if (isGoogleConfig(config)) { - await events.auth.SSOCreated("google") + await events.auth.SSOCreated("google", timestamp) if (config.config.activated) { - await events.auth.SSOActivated("google") + await events.auth.SSOActivated("google", timestamp) } } if (isOIDCConfig(config)) { - await events.auth.SSOCreated("oidc") + await events.auth.SSOCreated("oidc", timestamp) if (config.config.configs[0].activated) { - await events.auth.SSOActivated("oidc") + await events.auth.SSOActivated("oidc", timestamp) } } if (isSettingsConfig(config)) { const company = config.config.company if (company && company !== "Budibase") { - await events.org.nameUpdated() + await events.org.nameUpdated(timestamp) } const logoUrl = config.config.logoUrl if (logoUrl) { - await events.org.logoUpdated() + await events.org.logoUpdated(timestamp) } const platformUrl = config.config.platformUrl @@ -56,7 +59,7 @@ export const backfill = async (globalDb: any) => { platformUrl !== "http://localhost:10000" && env.SELF_HOSTED ) { - await events.org.platformURLUpdated() + await events.org.platformURLUpdated(timestamp) } } } diff --git a/packages/server/src/migrations/functions/backfill/global/rows.ts b/packages/server/src/migrations/functions/backfill/global/rows.ts index 4aaa47c1ee..b336668d64 100644 --- a/packages/server/src/migrations/functions/backfill/global/rows.ts +++ b/packages/server/src/migrations/functions/backfill/global/rows.ts @@ -1,16 +1,28 @@ -import { events, db } from "@budibase/backend-core" -import { Row } from "@budibase/types" +import { events, db as dbUtils } from "@budibase/backend-core" +import { Row, App } from "@budibase/types" import { getUniqueRows } from "../../../../utilities/usageQuota/rows" // Rows is a special circumstance where we get rows across all apps // therefore migration is performed at the global level +const getOldestCreatedAt = (allApps: App[]): string | undefined => { + const timestamps = allApps + .filter(app => !!app.createdAt) + .map(app => app.createdAt as string) + .sort((a, b) => new Date(a).getTime() - new Date(b).getTime()) + + if (timestamps.length) { + return timestamps[0] + } +} + export const backfill = async () => { - const allApps = await db.getAllApps({ all: true }) + const allApps: App[] = await dbUtils.getAllApps({ dev: true }) + const timestamp = getOldestCreatedAt(allApps) const appIds = allApps ? allApps.map((app: { appId: any }) => app.appId) : [] const rows: Row[] = await getUniqueRows(appIds) const rowCount = rows ? rows.length : 0 if (rowCount) { - await events.rows.created(rowCount) + await events.rows.created(rowCount, timestamp) } } diff --git a/packages/server/src/migrations/functions/backfill/global/users.ts b/packages/server/src/migrations/functions/backfill/global/users.ts index 1816c970db..c7c1b40df2 100644 --- a/packages/server/src/migrations/functions/backfill/global/users.ts +++ b/packages/server/src/migrations/functions/backfill/global/users.ts @@ -1,12 +1,12 @@ -import { events, db } from "@budibase/backend-core" +import { events, db as dbUtils } from "@budibase/backend-core" import { User } from "@budibase/types" // manually define user doc params - normally server doesn't read users from the db const getUserParams = (props: any) => { - return db.getDocParams(db.DocumentTypes.USER, null, props) + return dbUtils.getDocParams(dbUtils.DocumentTypes.USER, null, props) } -const getUsers = async (globalDb: any): Promise => { +export const getUsers = async (globalDb: any): Promise => { const response = await globalDb.allDocs( getUserParams({ include_docs: true, @@ -19,20 +19,21 @@ export const backfill = async (globalDb: any) => { const users = await getUsers(globalDb) for (const user of users) { - await events.identification.identifyUser(user) - await events.user.created(user) + const timestamp = user.createdAt as number + await events.identification.identifyUser(user, timestamp) + await events.user.created(user, timestamp) if (user.admin?.global) { - await events.user.permissionAdminAssigned(user) + await events.user.permissionAdminAssigned(user, timestamp) } if (user.builder?.global) { - await events.user.permissionBuilderAssigned(user) + await events.user.permissionBuilderAssigned(user, timestamp) } if (user.roles) { for (const [appId, role] of Object.entries(user.roles)) { - await events.role.assigned(user, role) + await events.role.assigned(user, role, timestamp) } } } diff --git a/packages/server/tsconfig.build.json b/packages/server/tsconfig.build.json index 836a726805..0f606f3a1d 100644 --- a/packages/server/tsconfig.build.json +++ b/packages/server/tsconfig.build.json @@ -2,7 +2,8 @@ // Used for building with tsc "extends": "./tsconfig.json", "references": [ - { "path": "../backend-core/tsconfig.build.json" } + { "path": "../backend-core/tsconfig.build.json" }, + { "path": "../types" } ], "exclude": [ "node_modules", diff --git a/packages/server/tsconfig.json b/packages/server/tsconfig.json index 4412d07b1d..d0fed6e824 100644 --- a/packages/server/tsconfig.json +++ b/packages/server/tsconfig.json @@ -16,9 +16,6 @@ "./src/**/*", "./src/module.d.ts" ], - "references": [ - { "path": "../backend-core/tsconfig.build.json" } - ], "exclude": [ "node_modules", "**/*.json", diff --git a/packages/types/src/documents/document.ts b/packages/types/src/documents/document.ts index b1b41394f4..fea05918d5 100644 --- a/packages/types/src/documents/document.ts +++ b/packages/types/src/documents/document.ts @@ -1,6 +1,6 @@ export interface Document { _id?: string _rev?: string - createdAt?: string + createdAt?: string | number updatedAt?: string } diff --git a/packages/types/src/documents/global/user.ts b/packages/types/src/documents/global/user.ts index 61a942fdb8..2de9ff7719 100644 --- a/packages/types/src/documents/global/user.ts +++ b/packages/types/src/documents/global/user.ts @@ -1,6 +1,8 @@ import { Document } from "../document" export interface User extends Document { + tenantId: string + email: string roles: UserRoles builder?: { global: boolean @@ -9,10 +11,9 @@ export interface User extends Document { global: boolean } providerType?: string - tenantId: string - email: string password?: string status?: string + createdAt?: number // override the default createdAt behaviour - users sdk historically set this to Date.now() } export interface UserRoles { diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json index 86a066fec8..1571b1ea3c 100644 --- a/packages/types/tsconfig.json +++ b/packages/types/tsconfig.json @@ -10,6 +10,7 @@ "resolveJsonModule": true, "incremental": true, "types": [ "node"], + "composite": true }, "include": [ "**/*.ts", diff --git a/packages/worker/tsconfig.json b/packages/worker/tsconfig.json index 080766172a..bb5c2ca8f8 100644 --- a/packages/worker/tsconfig.json +++ b/packages/worker/tsconfig.json @@ -13,7 +13,8 @@ "types": [ "node", "jest"], }, "references": [ - { "path": "../backend-core/tsconfig.build.json" } + { "path": "../backend-core/tsconfig.build.json" }, + { "path": "../types" } ], "include": [ "./src/**/*" From ba1c4ead90fef82524914a8bdbcff17513052756 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Thu, 26 May 2022 10:13:26 +0100 Subject: [PATCH 47/75] app and account properties, add account details to all user and tenant identities --- packages/backend-core/src/cloud/accounts.js | 39 ------------ packages/backend-core/src/cloud/accounts.ts | 63 +++++++++++++++++++ .../backend-core/src/events/identification.ts | 44 ++++++++++--- .../src/events/processors/LoggingProcessor.ts | 10 ++- .../src/events/publishers/account.ts | 20 ++++-- .../backend-core/src/events/publishers/app.ts | 61 ++++++++++++++---- packages/backend-core/src/index.ts | 2 +- .../server/src/api/controllers/application.ts | 19 +++--- packages/server/src/api/controllers/backup.js | 10 ++- .../migrations/functions/backfill/global.ts | 16 ++++- .../functions/backfill/global/users.ts | 9 ++- packages/types/src/documents/app/app.ts | 1 + packages/types/src/events/account.ts | 13 ++++ packages/types/src/events/app.ts | 51 +++++++++++---- packages/types/src/events/identification.ts | 10 ++- packages/types/src/events/index.ts | 1 + .../src/api/controllers/global/users.ts | 15 +++-- packages/worker/src/sdk/users/users.ts | 14 +++-- 18 files changed, 287 insertions(+), 111 deletions(-) delete mode 100644 packages/backend-core/src/cloud/accounts.js create mode 100644 packages/backend-core/src/cloud/accounts.ts create mode 100644 packages/types/src/events/account.ts diff --git a/packages/backend-core/src/cloud/accounts.js b/packages/backend-core/src/cloud/accounts.js deleted file mode 100644 index 5730bc67a5..0000000000 --- a/packages/backend-core/src/cloud/accounts.js +++ /dev/null @@ -1,39 +0,0 @@ -const API = require("./api") -const env = require("../environment") -const { Headers } = require("../constants") - -const api = new API(env.ACCOUNT_PORTAL_URL) - -exports.getAccount = async email => { - const payload = { - email, - } - const response = await api.post(`/api/accounts/search`, { - body: payload, - headers: { - [Headers.API_KEY]: env.ACCOUNT_PORTAL_API_KEY, - }, - }) - const json = await response.json() - - if (response.status !== 200) { - throw new Error(`Error getting account by email ${email}`, json) - } - - return json[0] -} - -exports.getStatus = async () => { - const response = await api.get(`/api/status`, { - headers: { - [Headers.API_KEY]: env.ACCOUNT_PORTAL_API_KEY, - }, - }) - const json = await response.json() - - if (response.status !== 200) { - throw new Error(`Error getting status`) - } - - return json -} diff --git a/packages/backend-core/src/cloud/accounts.ts b/packages/backend-core/src/cloud/accounts.ts new file mode 100644 index 0000000000..cca7469060 --- /dev/null +++ b/packages/backend-core/src/cloud/accounts.ts @@ -0,0 +1,63 @@ +import API from "./api" +import env from "../environment" +import { Headers } from "../constants" +import { CloudAccount } from "@budibase/types" + +const api = new API(env.ACCOUNT_PORTAL_URL) + +export const getAccount = async ( + email: string +): Promise => { + const payload = { + email, + } + const response = await api.post(`/api/accounts/search`, { + body: payload, + headers: { + [Headers.API_KEY]: env.ACCOUNT_PORTAL_API_KEY, + }, + }) + + if (response.status !== 200) { + throw new Error(`Error getting account by email ${email}`) + } + + const json: CloudAccount[] = await response.json() + return json[0] +} + +export const getAccountByTenantId = async ( + tenantId: string +): Promise => { + const payload = { + tenantId, + } + const response = await api.post(`/api/accounts/search`, { + body: payload, + headers: { + [Headers.API_KEY]: env.ACCOUNT_PORTAL_API_KEY, + }, + }) + + if (response.status !== 200) { + throw new Error(`Error getting account by tenantId ${tenantId}`) + } + + const json: CloudAccount[] = await response.json() + return json[0] +} + +export const getStatus = async () => { + const response = await api.get(`/api/status`, { + headers: { + [Headers.API_KEY]: env.ACCOUNT_PORTAL_API_KEY, + }, + }) + const json = await response.json() + + if (response.status !== 200) { + throw new Error(`Error getting status`) + } + + return json +} diff --git a/packages/backend-core/src/events/identification.ts b/packages/backend-core/src/events/identification.ts index edf1b658bc..6961815067 100644 --- a/packages/backend-core/src/events/identification.ts +++ b/packages/backend-core/src/events/identification.ts @@ -7,12 +7,13 @@ import { Identity, IdentityType, Account, - AccountIdentity, BudibaseIdentity, isCloudAccount, isSSOAccount, TenantIdentity, SettingsConfig, + CloudAccount, + UserIdentity, } from "@budibase/types" import { analyticsProcessor } from "./processors" import * as dbUtils from "../db/utils" @@ -81,20 +82,32 @@ const getHostingFromEnv = () => { export const identifyTenant = async ( tenantId: string, + account: CloudAccount | undefined, timestamp?: string | number ) => { const global = await getGlobalIdentifiers(tenantId) + const id = global.id + const hosting = getHostingFromEnv() + const type = IdentityType.TENANT + const profession = account?.profession + const companySize = account?.size const identity: TenantIdentity = { - id: global.id, + id, tenantId: global.tenantId, - hosting: getHostingFromEnv(), - type: IdentityType.TENANT, + hosting, + type, + profession, + companySize, } await identify(identity, timestamp) } -export const identifyUser = async (user: User, timestamp?: string | number) => { +export const identifyUser = async ( + user: User, + account: CloudAccount | undefined, + timestamp?: string | number +) => { const id = user._id as string const tenantId = user.tenantId const hosting = env.SELF_HOSTED ? Hosting.SELF : Hosting.CLOUD @@ -102,6 +115,10 @@ export const identifyUser = async (user: User, timestamp?: string | number) => { let builder = user.builder?.global let admin = user.admin?.global let providerType = user.providerType + const accountHolder = account?.budibaseUserId === user._id + const verified = account ? account.verified : false + const profession = account?.profession + const companySize = account?.size const identity: BudibaseIdentity = { id, @@ -111,6 +128,10 @@ export const identifyUser = async (user: User, timestamp?: string | number) => { builder, admin, providerType, + accountHolder, + verified, + profession, + companySize, } await identify(identity, timestamp) @@ -122,6 +143,10 @@ export const identifyAccount = async (account: Account) => { const hosting = account.hosting let type = IdentityType.USER let providerType = isSSOAccount(account) ? account.providerType : undefined + const verified = account.verified + const profession = account.profession + const companySize = account.size + const accountHolder = true if (isCloudAccount(account)) { if (account.budibaseUserId) { @@ -130,15 +155,16 @@ export const identifyAccount = async (account: Account) => { } } - const identity: AccountIdentity = { + const identity: UserIdentity = { id, tenantId, hosting, type, providerType, - verified: account.verified, - profession: account.profession, - companySize: account.size, + verified, + profession, + companySize, + accountHolder, } await identify(identity) diff --git a/packages/backend-core/src/events/processors/LoggingProcessor.ts b/packages/backend-core/src/events/processors/LoggingProcessor.ts index 102a50f8d9..e834243003 100644 --- a/packages/backend-core/src/events/processors/LoggingProcessor.ts +++ b/packages/backend-core/src/events/processors/LoggingProcessor.ts @@ -6,13 +6,19 @@ export default class LoggingProcessor implements EventProcessor { async processEvent( event: Event, identity: Identity, - properties: any + properties: any, + timestamp?: string ): Promise { if (env.SELF_HOSTED && !env.isDev()) { return } + let timestampString = "" + if (timestamp) { + timestampString = `[timestamp=${new Date(timestamp).toISOString()}]` + } + console.log( - `[audit] [tenant=${identity.tenantId}] [identityType=${identity.type}] [identity=${identity.id}] ${event}` + `[audit] [tenant=${identity.tenantId}] [identityType=${identity.type}] [identity=${identity.id}] ${timestampString} ${event} ` ) } diff --git a/packages/backend-core/src/events/publishers/account.ts b/packages/backend-core/src/events/publishers/account.ts index 66780a0b56..3f1a8a9161 100644 --- a/packages/backend-core/src/events/publishers/account.ts +++ b/packages/backend-core/src/events/publishers/account.ts @@ -1,17 +1,29 @@ import { publishEvent } from "../events" -import { Event, Account } from "@budibase/types" +import { + Event, + Account, + AccountCreatedEvent, + AccountDeletedEvent, + AccountVerifiedEvent, +} from "@budibase/types" export async function created(account: Account) { - const properties = {} + const properties: AccountCreatedEvent = { + tenantId: account.tenantId, + } await publishEvent(Event.ACCOUNT_CREATED, properties) } export async function deleted(account: Account) { - const properties = {} + const properties: AccountDeletedEvent = { + tenantId: account.tenantId, + } await publishEvent(Event.ACCOUNT_DELETED, properties) } export async function verified(account: Account) { - const properties = {} + const properties: AccountVerifiedEvent = { + tenantId: account.tenantId, + } await publishEvent(Event.ACCOUNT_VERIFIED, properties) } diff --git a/packages/backend-core/src/events/publishers/app.ts b/packages/backend-core/src/events/publishers/app.ts index b297b2a33e..dd77b0b8a2 100644 --- a/packages/backend-core/src/events/publishers/app.ts +++ b/packages/backend-core/src/events/publishers/app.ts @@ -16,58 +16,93 @@ import { } from "@budibase/types" export const created = async (app: App, timestamp?: string | number) => { - const properties: AppCreatedEvent = {} + const properties: AppCreatedEvent = { + appId: app.appId, + version: app.version, + } await publishEvent(Event.APP_CREATED, properties, timestamp) } export async function updated(app: App) { - const properties: AppUpdatedEvent = {} + const properties: AppUpdatedEvent = { + appId: app.appId, + version: app.version, + } await publishEvent(Event.APP_UPDATED, properties) } export async function deleted(app: App) { - const properties: AppDeletedEvent = {} + const properties: AppDeletedEvent = { + appId: app.appId, + } await publishEvent(Event.APP_DELETED, properties) } export async function published(app: App, timestamp?: string | number) { - const properties: AppPublishedEvent = {} + const properties: AppPublishedEvent = { + appId: app.appId, + } await publishEvent(Event.APP_PUBLISHED, properties, timestamp) } export async function unpublished(app: App) { - const properties: AppUnpublishedEvent = {} + const properties: AppUnpublishedEvent = { + appId: app.appId, + } await publishEvent(Event.APP_UNPUBLISHED, properties) } export async function fileImported(app: App) { - const properties: AppFileImportedEvent = {} + const properties: AppFileImportedEvent = { + appId: app.appId, + } await publishEvent(Event.APP_FILE_IMPORTED, properties) } -export async function templateImported(templateKey: string) { +export async function templateImported(app: App, templateKey: string) { const properties: AppTemplateImportedEvent = { + appId: app.appId, templateKey, } await publishEvent(Event.APP_TEMPLATE_IMPORTED, properties) } -export async function versionUpdated(app: App) { - const properties: AppVersionUpdatedEvent = {} +export async function versionUpdated( + app: App, + currentVersion: string, + updatedToVersion: string +) { + const properties: AppVersionUpdatedEvent = { + appId: app.appId, + currentVersion, + updatedToVersion, + } await publishEvent(Event.APP_VERSION_UPDATED, properties) } -export async function versionReverted(app: App) { - const properties: AppVersionRevertedEvent = {} +export async function versionReverted( + app: App, + currentVersion: string, + revertedToVersion: string +) { + const properties: AppVersionRevertedEvent = { + appId: app.appId, + currentVersion, + revertedToVersion, + } await publishEvent(Event.APP_VERSION_REVERTED, properties) } export async function reverted(app: App) { - const properties: AppRevertedEvent = {} + const properties: AppRevertedEvent = { + appId: app.appId, + } await publishEvent(Event.APP_REVERTED, properties) } export async function exported(app: App) { - const properties: AppExportedEvent = {} + const properties: AppExportedEvent = { + appId: app.appId, + } await publishEvent(Event.APP_EXPORTED, properties) } diff --git a/packages/backend-core/src/index.ts b/packages/backend-core/src/index.ts index d55260c8dd..c54ee1394e 100644 --- a/packages/backend-core/src/index.ts +++ b/packages/backend-core/src/index.ts @@ -3,8 +3,8 @@ import errors from "./errors" import * as events from "./events" import * as migrations from "./migrations" import * as users from "./users" +import * as accounts from "./cloud/accounts" import env from "./environment" -import accounts from "./cloud/accounts" import tenancy from "./tenancy" import featureFlags from "./featureFlags" import sessions from "./security/sessions" diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index 221df7f0dd..49417ae936 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -298,19 +298,19 @@ const creationEvents = async (request: any, app: App) => { const body = request.body if (body.useTemplate === "true") { // from template - if (body.templateKey) { - creationFns.push(app => events.app.templateImported(body.templateKey)) + if (body.templateKey && body.templateKey !== "undefined") { + creationFns.push(a => events.app.templateImported(a, body.templateKey)) } // from file else if (request.files?.templateFile) { - creationFns.push(events.app.fileImported) + creationFns.push(a => events.app.fileImported(a)) } // unknown else { console.error("Could not determine template creation event") } } - creationFns.push(events.app.created) + creationFns.push(a => events.app.created(a)) for (let fn of creationFns) { await fn(app) @@ -381,12 +381,13 @@ export const updateClient = async (ctx: any) => { } // Update versions in app package + const updatedToVersion = packageJson.version const appPackageUpdates = { - version: packageJson.version, + version: updatedToVersion, revertableVersion: currentVersion, } const app = await updateAppPackage(appPackageUpdates, ctx.params.appId) - await events.app.versionUpdated(app) + await events.app.versionUpdated(app, currentVersion, updatedToVersion) ctx.status = 200 ctx.body = app } @@ -405,12 +406,14 @@ export const revertClient = async (ctx: any) => { } // Update versions in app package + const currentVersion = application.version + const revertedToVersion = application.revertableVersion const appPackageUpdates = { - version: application.revertableVersion, + version: revertedToVersion, revertableVersion: null, } const app = await updateAppPackage(appPackageUpdates, ctx.params.appId) - await events.app.versionReverted(app) + await events.app.versionReverted(app, currentVersion, revertedToVersion) ctx.status = 200 ctx.body = app } diff --git a/packages/server/src/api/controllers/backup.js b/packages/server/src/api/controllers/backup.js index a6b7adf98a..6f08531c54 100644 --- a/packages/server/src/api/controllers/backup.js +++ b/packages/server/src/api/controllers/backup.js @@ -1,5 +1,6 @@ const { streamBackup } = require("../../utilities/fileSystem") -const { events } = require("@budibase/backend-core") +const { events, context } = require("@budibase/backend-core") +const { DocumentTypes } = require("../../db/utils") exports.exportAppDump = async function (ctx) { const { appId } = ctx.query @@ -7,5 +8,10 @@ exports.exportAppDump = async function (ctx) { const backupIdentifier = `${appName}-export-${new Date().getTime()}.txt` ctx.attachment(backupIdentifier) ctx.body = await streamBackup(appId) - await events.app.exported() + + await context.doInAppContext(appId, async () => { + const appDb = context.getAppDB() + const app = await appDb.get(DocumentTypes.APP_METADATA) + await events.app.exported(app) + }) } diff --git a/packages/server/src/migrations/functions/backfill/global.ts b/packages/server/src/migrations/functions/backfill/global.ts index d723844d5a..a9d74e1e64 100644 --- a/packages/server/src/migrations/functions/backfill/global.ts +++ b/packages/server/src/migrations/functions/backfill/global.ts @@ -1,7 +1,9 @@ import * as users from "./global/users" import * as rows from "./global/rows" import * as configs from "./global/configs" -import { tenancy, events, migrations } from "@budibase/backend-core" +import { tenancy, events, migrations, accounts } from "@budibase/backend-core" +import { CloudAccount } from "@budibase/types" +import env from "../../../environment" /** * Date: @@ -14,12 +16,20 @@ import { tenancy, events, migrations } from "@budibase/backend-core" export const run = async (db: any) => { const tenantId = tenancy.getTenantId() const installTimestamp = (await getInstallTimestamp(db)) as number + let account: CloudAccount | undefined + if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { + account = await accounts.getAccountByTenantId(tenantId) + } - await events.identification.identifyTenant(tenantId, installTimestamp) + await events.identification.identifyTenant( + tenantId, + account, + installTimestamp + ) await configs.backfill(db, installTimestamp) // users and rows provide their own timestamp - await users.backfill(db) + await users.backfill(db, account) await rows.backfill() } diff --git a/packages/server/src/migrations/functions/backfill/global/users.ts b/packages/server/src/migrations/functions/backfill/global/users.ts index c7c1b40df2..5ffbfd056c 100644 --- a/packages/server/src/migrations/functions/backfill/global/users.ts +++ b/packages/server/src/migrations/functions/backfill/global/users.ts @@ -1,5 +1,5 @@ import { events, db as dbUtils } from "@budibase/backend-core" -import { User } from "@budibase/types" +import { User, CloudAccount } from "@budibase/types" // manually define user doc params - normally server doesn't read users from the db const getUserParams = (props: any) => { @@ -15,12 +15,15 @@ export const getUsers = async (globalDb: any): Promise => { return response.rows.map((row: any) => row.doc) } -export const backfill = async (globalDb: any) => { +export const backfill = async ( + globalDb: any, + account: CloudAccount | undefined +) => { const users = await getUsers(globalDb) for (const user of users) { const timestamp = user.createdAt as number - await events.identification.identifyUser(user, timestamp) + await events.identification.identifyUser(user, account, timestamp) await events.user.created(user, timestamp) if (user.admin?.global) { diff --git a/packages/types/src/documents/app/app.ts b/packages/types/src/documents/app/app.ts index 344c4e36d3..fbad9190f5 100644 --- a/packages/types/src/documents/app/app.ts +++ b/packages/types/src/documents/app/app.ts @@ -11,6 +11,7 @@ export interface App extends Document { instance: AppInstance tenantId: string status: string + revertableVersion?: string } export interface AppInstance { diff --git a/packages/types/src/events/account.ts b/packages/types/src/events/account.ts new file mode 100644 index 0000000000..c41ab27cc0 --- /dev/null +++ b/packages/types/src/events/account.ts @@ -0,0 +1,13 @@ +export interface AccountCreatedEvent { + tenantId: string + registrationStep?: string +} + +export interface AccountDeletedEvent { + tenantId: string + registrationStep?: string +} + +export interface AccountVerifiedEvent { + tenantId: string +} diff --git a/packages/types/src/events/app.ts b/packages/types/src/events/app.ts index 429fb50114..d46f0be513 100644 --- a/packages/types/src/events/app.ts +++ b/packages/types/src/events/app.ts @@ -1,21 +1,50 @@ -export interface AppCreatedEvent {} +export interface AppCreatedEvent { + appId: string + version: string +} -export interface AppUpdatedEvent {} +export interface AppUpdatedEvent { + appId: string + version: string +} -export interface AppDeletedEvent {} +export interface AppDeletedEvent { + appId: string +} -export interface AppPublishedEvent {} +export interface AppPublishedEvent { + appId: string +} -export interface AppUnpublishedEvent {} +export interface AppUnpublishedEvent { + appId: string +} -export interface AppFileImportedEvent {} +export interface AppFileImportedEvent { + appId: string +} -export interface AppTemplateImportedEvent {} +export interface AppTemplateImportedEvent { + appId: string + templateKey: string +} -export interface AppVersionUpdatedEvent {} +export interface AppVersionUpdatedEvent { + appId: string + currentVersion: string + updatedToVersion: string +} -export interface AppVersionRevertedEvent {} +export interface AppVersionRevertedEvent { + appId: string + currentVersion: string + revertedToVersion: string +} -export interface AppRevertedEvent {} +export interface AppRevertedEvent { + appId: string +} -export interface AppExportedEvent {} +export interface AppExportedEvent { + appId: string +} diff --git a/packages/types/src/events/identification.ts b/packages/types/src/events/identification.ts index 1c20fd2ed6..73072134d9 100644 --- a/packages/types/src/events/identification.ts +++ b/packages/types/src/events/identification.ts @@ -13,11 +13,15 @@ export interface Identity { export interface TenantIdentity extends Identity { hosting: Hosting + profession?: string + companySize?: string } export interface UserIdentity extends TenantIdentity { hosting: Hosting type: IdentityType + verified: boolean + accountHolder: boolean providerType?: string } @@ -25,9 +29,3 @@ export interface BudibaseIdentity extends UserIdentity { builder?: boolean admin?: boolean } - -export interface AccountIdentity extends UserIdentity { - verified: boolean - profession: string | undefined - companySize: string | undefined -} diff --git a/packages/types/src/events/index.ts b/packages/types/src/events/index.ts index defd2c7b5b..678bb18485 100644 --- a/packages/types/src/events/index.ts +++ b/packages/types/src/events/index.ts @@ -16,3 +16,4 @@ export * from "./table" export * from "./user" export * from "./view" export * from "./identification" +export * from "./account" diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index d0a96b5362..42b98fd791 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -2,14 +2,13 @@ import { EmailTemplatePurpose } from "../../../constants" import { checkInviteCode } from "../../../utilities/redis" import { sendEmail } from "../../../utilities/email" import { users } from "../../../sdk" -import { User } from "@budibase/types" -import { events } from "@budibase/backend-core" -import { getGlobalDB } from "@budibase/backend-core/dist/src/context" +import env from "../../../environment" +import { User, CloudAccount } from "@budibase/types" +import { events, accounts, tenancy } from "@budibase/backend-core" const { errors, users: usersCore, - tenancy, db: dbUtils, } = require("@budibase/backend-core") @@ -66,7 +65,11 @@ export const adminUser = async (ctx: any) => { ctx.body = await tenancy.doInTenant(tenantId, async () => { return users.save(user, hashPassword, requirePassword) }) - await events.identification.identifyTenant(tenantId) + let account: CloudAccount | undefined + if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { + account = await accounts.getAccountByTenantId(tenantId) + } + await events.identification.identifyTenant(tenantId, account) } catch (err: any) { ctx.throw(err.status || 400, err) } @@ -141,7 +144,7 @@ export const inviteAccept = async (ctx: any) => { email, ...info, }) - const db = getGlobalDB() + const db = tenancy.getGlobalDB() const user = await db.get(saved._id) await events.user.inviteAccepted(user) return saved diff --git a/packages/worker/src/sdk/users/users.ts b/packages/worker/src/sdk/users/users.ts index b9218b1dea..bbcb80290c 100644 --- a/packages/worker/src/sdk/users/users.ts +++ b/packages/worker/src/sdk/users/users.ts @@ -2,11 +2,10 @@ import env from "../../environment" import { quotas } from "@budibase/pro" import * as apps from "../../utilities/appService" import * as eventHelpers from "./events" -import { User } from "@budibase/types" +import { User, CloudAccount } from "@budibase/types" const { tenancy, - accounts, utils, db: dbUtils, constants, @@ -17,7 +16,7 @@ const { HTTPError, } = require("@budibase/backend-core") -import { events } from "@budibase/backend-core" +import { events, accounts } from "@budibase/backend-core" /** * Retrieves all users from the current tenancy. @@ -133,7 +132,14 @@ export const save = async ( user._rev = response.rev await eventHelpers.handleSaveEvents(user, dbUser) - await events.identification.identifyUser(user) + + // identify + let tenantAccount: CloudAccount | undefined + if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { + tenantAccount = await accounts.getAccountByTenantId(tenantId) + } + await events.identification.identifyUser(user, tenantAccount) + await tenancy.tryAddTenant(tenantId, _id, email) await cache.user.invalidateUser(response.id) // let server know to sync user From 3c1b13083c6905e7943add2fdeec47ecbee52c38 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Thu, 26 May 2022 23:57:14 +0100 Subject: [PATCH 48/75] Installation identities, upgrade / downgrade events, filling in more event properties --- packages/backend-core/src/db/constants.js | 1 + .../backend-core/src/events/identification.ts | 76 ++++++++++----- .../src/events/processors/LoggingProcessor.ts | 15 +++ .../src/events/processors/Processors.ts | 9 ++ .../src/events/processors/types.ts | 1 + .../src/events/publishers/auth.ts | 8 +- .../src/events/publishers/automation.ts | 58 +++++++++--- .../src/events/publishers/datasource.ts | 15 ++- .../src/events/publishers/index.ts | 1 + .../src/events/publishers/layout.ts | 8 +- .../backend-core/src/events/publishers/org.ts | 10 +- .../src/events/publishers/version.ts | 26 ++++++ packages/backend-core/src/migrations/index.js | 20 ++-- packages/server/package.json | 2 + packages/server/src/api/controllers/dev.js | 2 +- packages/server/src/app.ts | 4 + packages/server/src/installation.ts | 92 +++++++++++++++++++ .../migrations/functions/backfill/global.ts | 11 ++- .../migrations/functions/backfill/index.ts | 1 + .../functions/backfill/installation.ts | 22 +++++ packages/server/src/migrations/index.ts | 6 ++ packages/server/yarn.lock | 12 +++ packages/types/src/core/sessions.ts | 15 ++- .../types/src/documents/app/automation.ts | 11 ++- .../types/src/documents/app/datasource.ts | 4 +- packages/types/src/documents/platform/info.ts | 8 ++ packages/types/src/events/auth.ts | 21 ++++- packages/types/src/events/automation.ts | 46 ++++++++-- packages/types/src/events/datasource.ts | 15 ++- packages/types/src/events/event.ts | 4 +- packages/types/src/events/identification.ts | 10 +- packages/types/src/events/index.ts | 2 +- packages/types/src/events/layout.ts | 8 +- packages/types/src/events/org.ts | 1 - packages/types/src/events/version.ts | 8 ++ 35 files changed, 463 insertions(+), 90 deletions(-) create mode 100644 packages/backend-core/src/events/publishers/version.ts create mode 100644 packages/server/src/installation.ts create mode 100644 packages/server/src/migrations/functions/backfill/installation.ts delete mode 100644 packages/types/src/events/org.ts create mode 100644 packages/types/src/events/version.ts diff --git a/packages/backend-core/src/db/constants.js b/packages/backend-core/src/db/constants.js index 271d4f412d..10c6e174d7 100644 --- a/packages/backend-core/src/db/constants.js +++ b/packages/backend-core/src/db/constants.js @@ -31,6 +31,7 @@ exports.StaticDatabases = { name: "global-info", docs: { tenants: "tenants", + install: "install", }, }, } diff --git a/packages/backend-core/src/events/identification.ts b/packages/backend-core/src/events/identification.ts index 6961815067..8eabfa2618 100644 --- a/packages/backend-core/src/events/identification.ts +++ b/packages/backend-core/src/events/identification.ts @@ -14,15 +14,21 @@ import { SettingsConfig, CloudAccount, UserIdentity, + InstallationIdentity, + Installation, + isInstallation, } from "@budibase/types" -import { analyticsProcessor } from "./processors" +import { processors } from "./processors" import * as dbUtils from "../db/utils" import { Configs } from "../constants" import * as hashing from "../hashing" +const pkg = require("../../package.json") + export const getCurrentIdentity = async (): Promise => { const user: SessionUser | undefined = context.getUser() - let tenantId = context.getTenantId() + + const tenantId = await getGlobalTenantId(context.getTenantId()) let id: string let type: IdentityType @@ -30,12 +36,14 @@ export const getCurrentIdentity = async (): Promise => { id = user._id type = IdentityType.USER } else { - const global = await getGlobalIdentifiers(tenantId) - id = global.id - tenantId = global.tenantId + id = tenantId type = IdentityType.TENANT } + if (user && isInstallation(user)) { + type = IdentityType.INSTALLATION + } + return { id, tenantId, @@ -43,36 +51,29 @@ export const getCurrentIdentity = async (): Promise => { } } -const getGlobalId = async (): Promise => { +const getGlobalId = async (tenantId: string): Promise => { const db = context.getGlobalDB() const config: SettingsConfig = await dbUtils.getScopedFullConfig(db, { type: Configs.SETTINGS, }) + + let globalId: string if (config.config.globalId) { return config.config.globalId } else { - const globalId = `global_${hashing.newid()}` + globalId = `${hashing.newid()}_${tenantId}` config.config.globalId = globalId await db.put(config) return globalId } } -const getGlobalIdentifiers = async ( - tenantId: string -): Promise<{ id: string; tenantId: string }> => { +const getGlobalTenantId = async (tenantId: string): Promise => { if (env.SELF_HOSTED) { - const globalId = await getGlobalId() - return { - id: globalId, - tenantId: `${globalId}-${tenantId}`, - } + return getGlobalId(tenantId) } else { // tenant id's in the cloud are already unique - return { - id: tenantId, - tenantId: tenantId, - } + return tenantId } } @@ -80,13 +81,34 @@ const getHostingFromEnv = () => { return env.SELF_HOSTED ? Hosting.SELF : Hosting.CLOUD } +export const identifyInstallation = async ( + install: Installation, + timestamp: string | number +) => { + const id = install.installId + // the default tenant id, so we can match installations to other events + const tenantId = await getGlobalTenantId(context.getTenantId()) + const version: string = pkg.version as string + const type = IdentityType.INSTALLATION + const hosting = getHostingFromEnv() + + const identity: InstallationIdentity = { + id, + tenantId, + type, + version, + hosting, + } + await identify(identity, timestamp) +} + export const identifyTenant = async ( tenantId: string, account: CloudAccount | undefined, timestamp?: string | number ) => { - const global = await getGlobalIdentifiers(tenantId) - const id = global.id + const globalTenantId = await getGlobalTenantId(tenantId) + const id = globalTenantId const hosting = getHostingFromEnv() const type = IdentityType.TENANT const profession = account?.profession @@ -94,7 +116,7 @@ export const identifyTenant = async ( const identity: TenantIdentity = { id, - tenantId: global.tenantId, + tenantId: globalTenantId, hosting, type, profession, @@ -116,7 +138,8 @@ export const identifyUser = async ( let admin = user.admin?.global let providerType = user.providerType const accountHolder = account?.budibaseUserId === user._id - const verified = account ? account.verified : false + const verified = + account && account?.budibaseUserId === user._id ? account.verified : false const profession = account?.profession const companySize = account?.size @@ -170,6 +193,9 @@ export const identifyAccount = async (account: Account) => { await identify(identity) } -const identify = async (identity: Identity, timestamp?: string | number) => { - await analyticsProcessor.identify(identity, timestamp) +export const identify = async ( + identity: Identity, + timestamp?: string | number +) => { + await processors.identify(identity, timestamp) } diff --git a/packages/backend-core/src/events/processors/LoggingProcessor.ts b/packages/backend-core/src/events/processors/LoggingProcessor.ts index e834243003..47b91b7dea 100644 --- a/packages/backend-core/src/events/processors/LoggingProcessor.ts +++ b/packages/backend-core/src/events/processors/LoggingProcessor.ts @@ -22,6 +22,21 @@ export default class LoggingProcessor implements EventProcessor { ) } + async identify(identity: Identity, timestamp?: string | number) { + if (env.SELF_HOSTED && !env.isDev()) { + return + } + + let timestampString = "" + if (timestamp) { + timestampString = `[timestamp=${new Date(timestamp).toISOString()}]` + } + + console.log( + `[audit] [${JSON.stringify(identity)}] ${timestampString} identified` + ) + } + shutdown(): void { // no-op } diff --git a/packages/backend-core/src/events/processors/Processors.ts b/packages/backend-core/src/events/processors/Processors.ts index d2ee44a4a6..4263b7e06b 100644 --- a/packages/backend-core/src/events/processors/Processors.ts +++ b/packages/backend-core/src/events/processors/Processors.ts @@ -20,6 +20,15 @@ export default class Processor implements EventProcessor { } } + async identify( + identity: Identity, + timestamp?: string | number + ): Promise { + for (const eventProcessor of this.processors) { + await eventProcessor.identify(identity, timestamp) + } + } + shutdown() { for (const eventProcessor of this.processors) { eventProcessor.shutdown() diff --git a/packages/backend-core/src/events/processors/types.ts b/packages/backend-core/src/events/processors/types.ts index 9b48b2beee..b8574cbd3d 100644 --- a/packages/backend-core/src/events/processors/types.ts +++ b/packages/backend-core/src/events/processors/types.ts @@ -12,5 +12,6 @@ export interface EventProcessor { properties: any, timestamp?: string | number ): Promise + identify(identity: Identity, timestamp?: string | number): Promise shutdown(): void } diff --git a/packages/backend-core/src/events/publishers/auth.ts b/packages/backend-core/src/events/publishers/auth.ts index a1f15470f9..93378501f3 100644 --- a/packages/backend-core/src/events/publishers/auth.ts +++ b/packages/backend-core/src/events/publishers/auth.ts @@ -10,16 +10,22 @@ import { SSOType, SSOUpdatedEvent, } from "@budibase/types" +import { identification } from ".." export async function login(source: LoginSource) { + const identity = await identification.getCurrentIdentity() const properties: LoginEvent = { + userId: identity.id, source, } await publishEvent(Event.AUTH_LOGIN, properties) } export async function logout() { - const properties: LogoutEvent = {} + const identity = await identification.getCurrentIdentity() + const properties: LogoutEvent = { + userId: identity.id, + } await publishEvent(Event.AUTH_LOGOUT, properties) } diff --git a/packages/backend-core/src/events/publishers/automation.ts b/packages/backend-core/src/events/publishers/automation.ts index ba4477618b..daaa06f571 100644 --- a/packages/backend-core/src/events/publishers/automation.ts +++ b/packages/backend-core/src/events/publishers/automation.ts @@ -3,7 +3,6 @@ import { Automation, Event, AutomationStep, - AutomationTrigger, AutomationCreatedEvent, AutomationDeletedEvent, AutomationTestedEvent, @@ -13,17 +12,42 @@ import { } from "@budibase/types" export async function created(automation: Automation, timestamp?: string) { - const properties: AutomationCreatedEvent = {} + const properties: AutomationCreatedEvent = { + appId: automation.appId, + automationId: automation._id as string, + triggerId: automation.definition?.trigger?.id, + triggerType: automation.definition?.trigger?.stepId, + } await publishEvent(Event.AUTOMATION_CREATED, properties, timestamp) } +export async function triggerUpdated(automation: Automation) { + const properties: AutomationTriggerUpdatedEvent = { + appId: automation.appId, + automationId: automation._id as string, + triggerId: automation.definition?.trigger?.id, + triggerType: automation.definition?.trigger?.stepId, + } + await publishEvent(Event.AUTOMATION_TRIGGER_UPDATED, properties) +} + export async function deleted(automation: Automation) { - const properties: AutomationDeletedEvent = {} + const properties: AutomationDeletedEvent = { + appId: automation.appId, + automationId: automation._id as string, + triggerId: automation.definition?.trigger?.id, + triggerType: automation.definition?.trigger?.stepId, + } await publishEvent(Event.AUTOMATION_DELETED, properties) } export async function tested(automation: Automation) { - const properties: AutomationTestedEvent = {} + const properties: AutomationTestedEvent = { + appId: automation.appId, + automationId: automation._id as string, + triggerId: automation.definition?.trigger?.id, + triggerType: automation.definition?.trigger?.stepId, + } await publishEvent(Event.AUTOMATION_TESTED, properties) } @@ -32,7 +56,14 @@ export async function stepCreated( step: AutomationStep, timestamp?: string ) { - const properties: AutomationStepCreatedEvent = {} + const properties: AutomationStepCreatedEvent = { + appId: automation.appId, + automationId: automation._id as string, + triggerId: automation.definition?.trigger?.id, + triggerType: automation.definition?.trigger?.stepId, + stepId: step.id, + stepType: step.stepId, + } await publishEvent(Event.AUTOMATION_STEP_CREATED, properties, timestamp) } @@ -40,14 +71,13 @@ export async function stepDeleted( automation: Automation, step: AutomationStep ) { - const properties: AutomationStepDeletedEvent = {} + const properties: AutomationStepDeletedEvent = { + appId: automation.appId, + automationId: automation._id as string, + triggerId: automation.definition?.trigger?.id, + triggerType: automation.definition?.trigger?.stepId, + stepId: step.id, + stepType: step.stepId, + } await publishEvent(Event.AUTOMATION_STEP_DELETED, properties) } - -export async function triggerUpdated( - automation: Automation, - trigger: AutomationTrigger -) { - const properties: AutomationTriggerUpdatedEvent = {} - await publishEvent(Event.AUTOMATION_TRIGGER_UPDATED, properties) -} diff --git a/packages/backend-core/src/events/publishers/datasource.ts b/packages/backend-core/src/events/publishers/datasource.ts index 95f47f7947..4cff0c9a18 100644 --- a/packages/backend-core/src/events/publishers/datasource.ts +++ b/packages/backend-core/src/events/publishers/datasource.ts @@ -8,16 +8,25 @@ import { } from "@budibase/types" export async function created(datasource: Datasource, timestamp?: string) { - const properties: DatasourceCreatedEvent = {} + const properties: DatasourceCreatedEvent = { + datasourceId: datasource._id as string, + source: datasource.source, + } await publishEvent(Event.DATASOURCE_CREATED, properties, timestamp) } export async function updated(datasource: Datasource) { - const properties: DatasourceUpdatedEvent = {} + const properties: DatasourceUpdatedEvent = { + datasourceId: datasource._id as string, + source: datasource.source, + } await publishEvent(Event.DATASOURCE_UPDATED, properties) } export async function deleted(datasource: Datasource) { - const properties: DatasourceDeletedEvent = {} + const properties: DatasourceDeletedEvent = { + datasourceId: datasource._id as string, + source: datasource.source, + } await publishEvent(Event.DATASOURCE_DELETED, properties) } diff --git a/packages/backend-core/src/events/publishers/index.ts b/packages/backend-core/src/events/publishers/index.ts index fd60b9dc4f..99069aede8 100644 --- a/packages/backend-core/src/events/publishers/index.ts +++ b/packages/backend-core/src/events/publishers/index.ts @@ -15,3 +15,4 @@ export * as table from "./table" export * as serve from "./serve" export * as user from "./user" export * as view from "./view" +export * as version from "./version" diff --git a/packages/backend-core/src/events/publishers/layout.ts b/packages/backend-core/src/events/publishers/layout.ts index c7d4f20225..7a334b059f 100644 --- a/packages/backend-core/src/events/publishers/layout.ts +++ b/packages/backend-core/src/events/publishers/layout.ts @@ -7,11 +7,15 @@ import { } from "@budibase/types" export async function created(layout: Layout, timestamp?: string) { - const properties: LayoutCreatedEvent = {} + const properties: LayoutCreatedEvent = { + layoutId: layout._id as string, + } await publishEvent(Event.LAYOUT_CREATED, properties, timestamp) } export async function deleted(layout: Layout) { - const properties: LayoutDeletedEvent = {} + const properties: LayoutDeletedEvent = { + layoutId: layout._id as string, + } await publishEvent(Event.LAYOUT_DELETED, properties) } diff --git a/packages/backend-core/src/events/publishers/org.ts b/packages/backend-core/src/events/publishers/org.ts index 075ab1592b..4567357db8 100644 --- a/packages/backend-core/src/events/publishers/org.ts +++ b/packages/backend-core/src/events/publishers/org.ts @@ -1,5 +1,5 @@ import { publishEvent } from "../events" -import { Event, VersionCheckedEvent } from "@budibase/types" +import { Event } from "@budibase/types" export async function nameUpdated(timestamp?: string | number) { const properties = {} @@ -16,14 +16,8 @@ export async function platformURLUpdated(timestamp?: string | number) { await publishEvent(Event.ORG_PLATFORM_URL_UPDATED, properties, timestamp) } -export async function versionChecked(version: number) { - const properties: VersionCheckedEvent = { - version, - } - await publishEvent(Event.UPDATE_VERSION_CHECKED, properties) -} - // TODO + export async function analyticsOptOut() { const properties = {} await publishEvent(Event.ANALYTICS_OPT_OUT, properties) diff --git a/packages/backend-core/src/events/publishers/version.ts b/packages/backend-core/src/events/publishers/version.ts new file mode 100644 index 0000000000..1c96ed629f --- /dev/null +++ b/packages/backend-core/src/events/publishers/version.ts @@ -0,0 +1,26 @@ +import { publishEvent } from "../events" +import { Event, VersionCheckedEvent, VersionChangeEvent } from "@budibase/types" + +export async function checked(version: string) { + const properties: VersionCheckedEvent = { + currentVersion: version, + } + await publishEvent(Event.VERSION_CHECKED, properties) +} + +export async function upgraded(from: string, to: string) { + const properties: VersionChangeEvent = { + from, + to, + } + + await publishEvent(Event.VERSION_UPGRADED, properties) +} + +export async function downgraded(from: string, to: string) { + const properties: VersionChangeEvent = { + from, + to, + } + await publishEvent(Event.VERSION_DOWNGRADED, properties) +} diff --git a/packages/backend-core/src/migrations/index.js b/packages/backend-core/src/migrations/index.js index 26be4c90bc..6bfedf03c5 100644 --- a/packages/backend-core/src/migrations/index.js +++ b/packages/backend-core/src/migrations/index.js @@ -1,6 +1,6 @@ const { DEFAULT_TENANT_ID } = require("../constants") const { doWithDB } = require("../db") -const { DocumentTypes } = require("../db/constants") +const { DocumentTypes, StaticDatabases } = require("../db/constants") const { getAllApps } = require("../db/utils") const environment = require("../environment") const { @@ -11,8 +11,9 @@ const { } = require("../tenancy") exports.MIGRATION_TYPES = { - GLOBAL: "global", // run once, recorded in global db, global db is provided as an argument + GLOBAL: "global", // run once per tenant, recorded in global db, global db is provided as an argument APP: "app", // run per app, recorded in each app db, app db is provided as an argument + INSTALLATION: "installation", // run once, recorded in global info db, global info db is provided as an argument } exports.getMigrationsDoc = async db => { @@ -22,14 +23,19 @@ exports.getMigrationsDoc = async db => { } catch (err) { if (err.status && err.status === 404) { return { _id: DocumentTypes.MIGRATIONS } + } else { + console.error(err) + throw err } - console.error(err) } } exports.runMigration = async (migration, options = {}) => { - const tenantId = getTenantId() const migrationType = migration.type + let tenantId + if (migrationType !== exports.MIGRATION_TYPES.INSTALLATION) { + tenantId = getTenantId() + } const migrationName = migration.name const silent = migration.silent @@ -46,10 +52,10 @@ exports.runMigration = async (migration, options = {}) => { } else if (migrationType === exports.MIGRATION_TYPES.APP) { const apps = await getAllApps(migration.opts) dbNames = apps.map(app => app.appId) + } else if (migrationType === exports.MIGRATION_TYPES.INSTALLATION) { + dbNames = [StaticDatabases.PLATFORM_INFO.name] } else { - throw new Error( - `[Tenant: ${tenantId}] Unrecognised migration type [${migrationType}]` - ) + throw new Error(`Unrecognised migration type [${migrationType}]`) } const length = dbNames.length diff --git a/packages/server/package.json b/packages/server/package.json index 36ea8391ee..9e336d0280 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -129,6 +129,7 @@ "pouchdb-find": "^7.2.2", "pouchdb-replication-stream": "1.2.9", "redis": "4", + "semver": "^7.0.0", "server-destroy": "1.0.1", "svelte": "^3.38.2", "swagger-parser": "^10.0.3", @@ -159,6 +160,7 @@ "@types/node": "^15.12.4", "@types/oracledb": "^5.2.1", "@types/redis": "^4.0.11", + "@types/semver": "^7.0.0", "@typescript-eslint/parser": "5.12.0", "apidoc": "^0.50.2", "babel-jest": "^27.0.2", diff --git a/packages/server/src/api/controllers/dev.js b/packages/server/src/api/controllers/dev.js index e78fe7ddce..03ac140eb2 100644 --- a/packages/server/src/api/controllers/dev.js +++ b/packages/server/src/api/controllers/dev.js @@ -131,5 +131,5 @@ exports.getBudibaseVersion = async ctx => { ctx.body = { version, } - await events.org.versionChecked(version) + await events.version.versionChecked(version) } diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts index bc9910b370..127d0f8853 100644 --- a/packages/server/src/app.ts +++ b/packages/server/src/app.ts @@ -17,6 +17,7 @@ const bullboard = require("./automations/bullboard") import redis from "./utilities/redis" import * as migrations from "./migrations" import { events } from "@budibase/backend-core" +import * as installation from "./installation" const app = new Koa() @@ -108,3 +109,6 @@ if (!env.HTTP_MIGRATIONS && !env.isTest()) { shutdown() }) } + +// check for version updates +installation.checkInstallVersion() diff --git a/packages/server/src/installation.ts b/packages/server/src/installation.ts new file mode 100644 index 0000000000..3832d6f474 --- /dev/null +++ b/packages/server/src/installation.ts @@ -0,0 +1,92 @@ +import { + db as dbUtils, + events, + utils, + context, + tenancy, +} from "@budibase/backend-core" +import { Installation } from "@budibase/types" +import semver from "semver" + +const pkg = require("../package.json") + +export const getInstall = async (): Promise => { + return dbUtils.doWithDB( + dbUtils.StaticDatabases.PLATFORM_INFO.name, + async (platformDb: any) => { + let install: Installation + try { + install = await platformDb.get( + dbUtils.StaticDatabases.PLATFORM_INFO.docs.install + ) + } catch (e: any) { + if (e.status === 404) { + install = { + _id: dbUtils.StaticDatabases.PLATFORM_INFO.docs.install, + installId: utils.newid(), + version: pkg.version, + } + const resp = await platformDb.put(install) + install._rev = resp.rev + } else { + throw e + } + } + return install + }, + {} + ) +} + +const updateVersion = async (version: string): Promise => { + try { + await dbUtils.doWithDB( + dbUtils.StaticDatabases.PLATFORM_INFO.name, + async (platformDb: any) => { + const install = await getInstall() + install.version = version + await platformDb.put(install) + }, + {} + ) + } catch (e: any) { + if (e.status === 409) { + // do nothing - version has already been updated + // likely in clustered environment + return false + } + throw e + } + return true +} + +export const checkInstallVersion = async (): Promise => { + const install = await getInstall() + + const currentVersion = install.version + const newVersion = pkg.version + + if (currentVersion !== newVersion) { + const isUpgrade = semver.gt(newVersion, currentVersion) + const isDowngrade = semver.lt(newVersion, currentVersion) + + const success = await updateVersion(newVersion) + + if (success) { + await context.doInUserContext( + { + _id: install.installId, + isInstall: true, + tenantId: tenancy.DEFAULT_TENANT_ID, + }, + async () => { + if (isUpgrade) { + await events.version.upgraded(currentVersion, newVersion) + } else if (isDowngrade) { + await events.version.downgraded(currentVersion, newVersion) + } + } + ) + } + } +} diff --git a/packages/server/src/migrations/functions/backfill/global.ts b/packages/server/src/migrations/functions/backfill/global.ts index a9d74e1e64..99d08c46ed 100644 --- a/packages/server/src/migrations/functions/backfill/global.ts +++ b/packages/server/src/migrations/functions/backfill/global.ts @@ -2,8 +2,9 @@ import * as users from "./global/users" import * as rows from "./global/rows" import * as configs from "./global/configs" import { tenancy, events, migrations, accounts } from "@budibase/backend-core" -import { CloudAccount } from "@budibase/types" +import { CloudAccount, Installation } from "@budibase/types" import env from "../../../environment" +import * as installation from "../../../installation" /** * Date: @@ -39,10 +40,12 @@ export const isComplete = async (): Promise => { return !!migrationsDoc.event_global_backfill } -const getInstallTimestamp = async (db: any): Promise => { - const allUsers = await users.getUsers(db) +export const getInstallTimestamp = async ( + globalDb: any +): Promise => { + const allUsers = await users.getUsers(globalDb) - // get the olders user timestamp + // get the oldest user timestamp const timestamps = allUsers .map(user => user.createdAt) .filter(timestamp => !!timestamp) diff --git a/packages/server/src/migrations/functions/backfill/index.ts b/packages/server/src/migrations/functions/backfill/index.ts index fb9859a581..dee9b82e1a 100644 --- a/packages/server/src/migrations/functions/backfill/index.ts +++ b/packages/server/src/migrations/functions/backfill/index.ts @@ -1,2 +1,3 @@ export * as app from "./app" export * as global from "./global" +export * as installation from "./installation" diff --git a/packages/server/src/migrations/functions/backfill/installation.ts b/packages/server/src/migrations/functions/backfill/installation.ts new file mode 100644 index 0000000000..bf3b413fa7 --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/installation.ts @@ -0,0 +1,22 @@ +import { events, tenancy } from "@budibase/backend-core" +import { Installation } from "@budibase/types" +import * as installation from "../../../installation" +import * as global from "./global" + +/** + * Date: + * May 2022 + * + * Description: + * Backfill installation events. + */ + +export const run = async () => { + // need to use the default tenant to try to get the installation time + await tenancy.doInTenant(tenancy.DEFAULT_TENANT_ID, async () => { + const db = tenancy.getGlobalDB() + const installTimestamp = (await global.getInstallTimestamp(db)) as number + const install: Installation = await installation.getInstall() + await events.identification.identifyInstallation(install, installTimestamp) + }) +} diff --git a/packages/server/src/migrations/index.ts b/packages/server/src/migrations/index.ts index 92e17ed24e..1418a05f18 100644 --- a/packages/server/src/migrations/index.ts +++ b/packages/server/src/migrations/index.ts @@ -73,6 +73,12 @@ export const MIGRATIONS: Migration[] = [ fn: backfill.global.run, silent: !!env.SELF_HOSTED, // reduce noisy logging }, + { + type: migrations.MIGRATION_TYPES.INSTALLATION, + name: "event_installation_backfill", + fn: backfill.installation.run, + silent: !!env.SELF_HOSTED, // reduce noisy logging + }, ] export const migrate = async (options?: MigrationOptions) => { diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 4548fd2bd0..7236ec8d84 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -2754,6 +2754,11 @@ "@types/tough-cookie" "*" form-data "^2.5.0" +"@types/semver@^7.0.0": + version "7.3.9" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.9.tgz#152c6c20a7688c30b967ec1841d31ace569863fc" + integrity sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ== + "@types/serve-static@*": version "1.13.10" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" @@ -11645,6 +11650,13 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.0.0: + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + dependencies: + lru-cache "^6.0.0" + seq-queue@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" diff --git a/packages/types/src/core/sessions.ts b/packages/types/src/core/sessions.ts index a47fd90223..56db6e4f54 100644 --- a/packages/types/src/core/sessions.ts +++ b/packages/types/src/core/sessions.ts @@ -33,4 +33,17 @@ export const isUserSession = ( return !user.account || user.account?.hosting === Hosting.CLOUD } -export type SessionUser = AccountUserSession | BudibaseUserSession +// not technically a session, but used to identify the installation +export interface InstallationSession { + _id: string + isInstallation: boolean +} + +export const isInstallation = (user: any): user is InstallationSession => { + return !!user.isInstallation +} + +export type SessionUser = + | AccountUserSession + | BudibaseUserSession + | InstallationSession diff --git a/packages/types/src/documents/app/automation.ts b/packages/types/src/documents/app/automation.ts index 478ef0b7de..ae6c3918a6 100644 --- a/packages/types/src/documents/app/automation.ts +++ b/packages/types/src/documents/app/automation.ts @@ -5,8 +5,15 @@ export interface Automation extends Document { steps: AutomationStep[] trigger: AutomationTrigger } + appId: string } -export interface AutomationStep {} +export interface AutomationStep { + id: string + stepId: string +} -export interface AutomationTrigger {} +export interface AutomationTrigger { + id: string + stepId: string +} diff --git a/packages/types/src/documents/app/datasource.ts b/packages/types/src/documents/app/datasource.ts index 63499cd02b..3a8704a0a9 100644 --- a/packages/types/src/documents/app/datasource.ts +++ b/packages/types/src/documents/app/datasource.ts @@ -1,3 +1,5 @@ import { Document } from "../document" -export interface Datasource extends Document {} +export interface Datasource extends Document { + source: string +} diff --git a/packages/types/src/documents/platform/info.ts b/packages/types/src/documents/platform/info.ts index 782ce5dd09..ce493bc26f 100644 --- a/packages/types/src/documents/platform/info.ts +++ b/packages/types/src/documents/platform/info.ts @@ -1 +1,9 @@ +import { Document } from "../document" + export interface GlobalInfo {} + +export interface Installation extends Document { + _id: string + installId: string + version: string +} diff --git a/packages/types/src/events/auth.ts b/packages/types/src/events/auth.ts index e5dfb224bc..c0e046a2ca 100644 --- a/packages/types/src/events/auth.ts +++ b/packages/types/src/events/auth.ts @@ -2,15 +2,26 @@ export type LoginSource = "local" | "google" | "oidc" export type SSOType = "oidc" | "google" export interface LoginEvent { + userId: string source: LoginSource } -export interface LogoutEvent {} +export interface LogoutEvent { + userId: string +} -export interface SSOCreatedEvent {} +export interface SSOCreatedEvent { + type: SSOType +} -export interface SSOUpdatedEvent {} +export interface SSOUpdatedEvent { + type: SSOType +} -export interface SSOActivatedEvent {} +export interface SSOActivatedEvent { + type: SSOType +} -export interface SSODeactivatedEvent {} +export interface SSODeactivatedEvent { + type: SSOType +} diff --git a/packages/types/src/events/automation.ts b/packages/types/src/events/automation.ts index 699d130618..4bd40bee92 100644 --- a/packages/types/src/events/automation.ts +++ b/packages/types/src/events/automation.ts @@ -1,11 +1,45 @@ -export interface AutomationCreatedEvent {} +export interface AutomationCreatedEvent { + appId: string + automationId: string + triggerId: string + triggerType: string +} -export interface AutomationDeletedEvent {} +export interface AutomationTriggerUpdatedEvent { + appId: string + automationId: string + triggerId: string + triggerType: string +} -export interface AutomationTestedEvent {} +export interface AutomationDeletedEvent { + appId: string + automationId: string + triggerId: string + triggerType: string +} -export interface AutomationStepCreatedEvent {} +export interface AutomationTestedEvent { + appId: string + automationId: string + triggerId: string + triggerType: string +} -export interface AutomationStepDeletedEvent {} +export interface AutomationStepCreatedEvent { + appId: string + automationId: string + triggerId: string + triggerType: string + stepId: string + stepType: string +} -export interface AutomationTriggerUpdatedEvent {} +export interface AutomationStepDeletedEvent { + appId: string + automationId: string + triggerId: string + triggerType: string + stepId: string + stepType: string +} diff --git a/packages/types/src/events/datasource.ts b/packages/types/src/events/datasource.ts index 2eca9e30f5..cead646477 100644 --- a/packages/types/src/events/datasource.ts +++ b/packages/types/src/events/datasource.ts @@ -1,5 +1,14 @@ -export interface DatasourceCreatedEvent {} +export interface DatasourceCreatedEvent { + datasourceId: string + source: string +} -export interface DatasourceUpdatedEvent {} +export interface DatasourceUpdatedEvent { + datasourceId: string + source: string +} -export interface DatasourceDeletedEvent {} +export interface DatasourceDeletedEvent { + datasourceId: string + source: string +} diff --git a/packages/types/src/events/event.ts b/packages/types/src/events/event.ts index e4d6b40d6e..910a30f925 100644 --- a/packages/types/src/events/event.ts +++ b/packages/types/src/events/event.ts @@ -38,7 +38,9 @@ export enum Event { ORG_PLATFORM_URL_UPDATED = "org:platformurl:updated", // ORG / UPDATE - UPDATE_VERSION_CHECKED = "version:checked", + VERSION_CHECKED = "version:checked", + VERSION_UPGRADED = "version:upgraded", + VERSION_DOWNGRADED = "version:downgraded", // ORG / ANALYTICS ANALYTICS_OPT_OUT = "analytics:opt:out", diff --git a/packages/types/src/events/identification.ts b/packages/types/src/events/identification.ts index 73072134d9..29d0aa333e 100644 --- a/packages/types/src/events/identification.ts +++ b/packages/types/src/events/identification.ts @@ -1,8 +1,9 @@ import { Hosting } from "../core" export enum IdentityType { - USER = "user", // cloud and self hosted users - TENANT = "tenant", // cloud and self hosted tenants + USER = "user", + TENANT = "tenant", + INSTALLATION = "installation", } export interface Identity { @@ -11,6 +12,11 @@ export interface Identity { type: IdentityType } +export interface InstallationIdentity extends Identity { + version: string + hosting: Hosting +} + export interface TenantIdentity extends Identity { hosting: Hosting profession?: string diff --git a/packages/types/src/events/index.ts b/packages/types/src/events/index.ts index 678bb18485..c55711205c 100644 --- a/packages/types/src/events/index.ts +++ b/packages/types/src/events/index.ts @@ -6,7 +6,7 @@ export * from "./datasource" export * from "./event" export * from "./layout" export * from "./license" -export * from "./org" +export * from "./version" export * from "./query" export * from "./role" export * from "./rows" diff --git a/packages/types/src/events/layout.ts b/packages/types/src/events/layout.ts index fbfac2c411..b0b5be4f6d 100644 --- a/packages/types/src/events/layout.ts +++ b/packages/types/src/events/layout.ts @@ -1,3 +1,7 @@ -export interface LayoutCreatedEvent {} +export interface LayoutCreatedEvent { + layoutId: string +} -export interface LayoutDeletedEvent {} +export interface LayoutDeletedEvent { + layoutId: string +} diff --git a/packages/types/src/events/org.ts b/packages/types/src/events/org.ts deleted file mode 100644 index 8d0243366e..0000000000 --- a/packages/types/src/events/org.ts +++ /dev/null @@ -1 +0,0 @@ -export interface VersionCheckedEvent {} diff --git a/packages/types/src/events/version.ts b/packages/types/src/events/version.ts new file mode 100644 index 0000000000..07873212c1 --- /dev/null +++ b/packages/types/src/events/version.ts @@ -0,0 +1,8 @@ +export interface VersionCheckedEvent { + currentVersion: string +} + +export interface VersionChangeEvent { + from: string + to: string +} From c0d6fa34a49265420a8bfed8f09f78d8dd427660 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Sat, 28 May 2022 21:38:22 +0100 Subject: [PATCH 49/75] Identity tenant and installation groups, property updates --- packages/backend-core/context.js | 5 +- packages/backend-core/package.json | 2 + packages/backend-core/src/context/identity.ts | 50 +++ packages/backend-core/src/context/index.js | 40 +- packages/backend-core/src/environment.ts | 1 + packages/backend-core/src/events/analytics.ts | 1 + .../backend-core/src/events/identification.ts | 363 ++++++++++-------- .../events/processors/AnalyticsProcessor.ts | 23 +- .../src/events/processors/LoggingProcessor.ts | 39 +- .../src/events/processors/PosthogProcessor.ts | 32 +- .../src/events/processors/Processors.ts | 11 +- .../src/events/processors/types.ts | 3 +- .../src/events/publishers/layout.ts | 4 +- .../src/events/publishers/serve.ts | 12 +- packages/backend-core/src/index.ts | 2 + .../src/installation.ts | 34 +- .../src/middleware/authenticated.js | 6 +- .../src/tests/utilities/mocks/events.js | 4 +- packages/backend-core/yarn.lock | 7 +- packages/server/package.json | 2 - packages/server/src/api/controllers/dev.js | 2 +- packages/server/src/api/controllers/layout.js | 4 +- .../src/api/controllers/static/index.js | 3 +- .../server/src/api/routes/tests/dev.spec.js | 4 +- packages/server/src/app.ts | 3 +- .../migrations/functions/backfill/global.ts | 5 +- .../functions/backfill/installation.ts | 8 +- packages/server/yarn.lock | 12 - packages/types/src/core/context.ts | 19 + packages/types/src/core/identification.ts | 49 +++ packages/types/src/core/index.ts | 3 +- packages/types/src/core/sessions.ts | 49 --- packages/types/src/events/auth.ts | 2 +- packages/types/src/events/event.ts | 2 +- packages/types/src/events/identification.ts | 37 -- packages/types/src/events/index.ts | 1 - packages/types/src/events/serve.ts | 10 +- .../worker/src/api/controllers/global/auth.ts | 12 +- .../src/api/controllers/global/users.ts | 2 +- scripts/link-dependencies.sh | 9 + 40 files changed, 529 insertions(+), 348 deletions(-) create mode 100644 packages/backend-core/src/context/identity.ts rename packages/{server => backend-core}/src/installation.ts (71%) create mode 100644 packages/types/src/core/context.ts create mode 100644 packages/types/src/core/identification.ts delete mode 100644 packages/types/src/core/sessions.ts delete mode 100644 packages/types/src/events/identification.ts diff --git a/packages/backend-core/context.js b/packages/backend-core/context.js index 0d86c530a7..aaa0f56f92 100644 --- a/packages/backend-core/context.js +++ b/packages/backend-core/context.js @@ -5,10 +5,11 @@ const { getAppId, updateAppId, doInAppContext, - doInUserContext, doInTenant, } = require("./src/context") +const identity = require("./src/context/identity") + module.exports = { getAppDB, getDevAppDB, @@ -16,6 +17,6 @@ module.exports = { getAppId, updateAppId, doInAppContext, - doInUserContext, doInTenant, + identity, } diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 3f8b0bef81..a717fd1211 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -32,6 +32,7 @@ "pouchdb-find": "^7.2.2", "pouchdb-replication-stream": "^1.2.9", "sanitize-s3-objectkey": "^0.0.1", + "semver": "^7.0.0", "tar-fs": "^2.1.1", "uuid": "^8.3.2", "zlib": "^1.0.5" @@ -52,6 +53,7 @@ "@types/node-fetch": "^2.6.1", "@types/tar-fs": "^2.0.1", "@types/uuid": "^8.3.4", + "@types/semver": "^7.0.0", "ioredis-mock": "^5.5.5", "jest": "^27.0.3", "koa": "2.7.0", diff --git a/packages/backend-core/src/context/identity.ts b/packages/backend-core/src/context/identity.ts new file mode 100644 index 0000000000..37e1ecf40a --- /dev/null +++ b/packages/backend-core/src/context/identity.ts @@ -0,0 +1,50 @@ +import { + IdentityContext, + IdentityType, + User, + UserContext, + isCloudAccount, + Account, + AccountUserContext, +} from "@budibase/types" +import * as context from "." + +export const getIdentity = (): IdentityContext | undefined => { + return context.getIdentity() +} + +export const doInIdentityContext = (identity: IdentityContext, task: any) => { + return context.doInIdentityContext(identity, task) +} + +export const doInUserContext = (user: User, task: any) => { + const userContext: UserContext = { + ...user, + _id: user._id as string, + type: IdentityType.USER, + } + return doInIdentityContext(userContext, task) +} + +export const doInAccountContext = (account: Account, task: any) => { + const _id = getAccountUserId(account) + const tenantId = account.tenantId + const accountContext: AccountUserContext = { + _id, + type: IdentityType.USER, + tenantId, + account, + } + return doInIdentityContext(accountContext, task) +} + +export const getAccountUserId = (account: Account) => { + let userId: string + if (isCloudAccount(account)) { + userId = account.budibaseUserId + } else { + // use account id as user id for self hosting + userId = account.accountId + } + return userId +} diff --git a/packages/backend-core/src/context/index.js b/packages/backend-core/src/context/index.js index 7c7c5c45e6..59dc0cda79 100644 --- a/packages/backend-core/src/context/index.js +++ b/packages/backend-core/src/context/index.js @@ -15,7 +15,7 @@ const ContextKeys = { TENANT_ID: "tenantId", GLOBAL_DB: "globalDb", APP_ID: "appId", - USER: "user", + IDENTITY: "identity", // whatever the request app DB was CURRENT_DB: "currentDb", // get the prod app DB from the request @@ -138,19 +138,19 @@ exports.doInAppContext = (appId, task) => { throw new Error("appId is required") } - const user = exports.getUser() + const identity = exports.getIdentity() // the internal function is so that we can re-use an existing // context - don't want to close DB on a parent context - async function internal(opts = { existing: false, user: undefined }) { + async function internal(opts = { existing: false }) { // set the app tenant id if (!opts.existing) { setAppTenantId(appId) } // set the app ID cls.setOnContext(ContextKeys.APP_ID, appId) - // preserve the user - exports.setUser(user) + // preserve the identity + exports.setIdentity(identity) try { // invoke the task return await task() @@ -175,17 +175,17 @@ exports.doInAppContext = (appId, task) => { } } -exports.doInUserContext = (user, task) => { - if (!user) { - throw new Error("user is required") +exports.doInIdentityContext = (identity, task) => { + if (!identity) { + throw new Error("identity is required") } async function internal(opts = { existing: false }) { if (!opts.existing) { - cls.setOnContext(ContextKeys.USER, user) - // set the tenant so that doInTenant will preserve user - if (user.tenantId) { - exports.updateTenantId(user.tenantId) + cls.setOnContext(ContextKeys.IDENTITY, identity) + // set the tenant so that doInTenant will preserve identity + if (identity.tenantId) { + exports.updateTenantId(identity.tenantId) } } @@ -195,16 +195,16 @@ exports.doInUserContext = (user, task) => { } finally { const using = cls.getFromContext(ContextKeys.IN_USE) if (!using || using <= 1) { - exports.setUser(null) + exports.setIdentity(null) } else { cls.setOnContext(using - 1) } } } - const existing = cls.getFromContext(ContextKeys.USER) + const existing = cls.getFromContext(ContextKeys.IDENTITY) const using = cls.getFromContext(ContextKeys.IN_USE) - if (using && existing && existing._id === user._id) { + if (using && existing && existing._id === identity._id) { cls.setOnContext(ContextKeys.IN_USE, using + 1) return internal({ existing: true }) } else { @@ -215,15 +215,15 @@ exports.doInUserContext = (user, task) => { } } -exports.setUser = user => { - cls.setOnContext(ContextKeys.USER, user) +exports.setIdentity = identity => { + cls.setOnContext(ContextKeys.IDENTITY, identity) } -exports.getUser = () => { +exports.getIdentity = () => { try { - return cls.getFromContext(ContextKeys.USER) + return cls.getFromContext(ContextKeys.IDENTITY) } catch (e) { - // do nothing - user is not in context + // do nothing - identity is not in context } } diff --git a/packages/backend-core/src/environment.ts b/packages/backend-core/src/environment.ts index 417fcd7866..9cb1d24423 100644 --- a/packages/backend-core/src/environment.ts +++ b/packages/backend-core/src/environment.ts @@ -52,6 +52,7 @@ const env: any = { process.env.GLOBAL_CLOUD_BUCKET_NAME || "prod-budi-tenant-uploads", USE_COUCH: process.env.USE_COUCH || true, DISABLE_DEVELOPER_LICENSE: process.env.DISABLE_DEVELOPER_LICENSE, + SERVICE: process.env.SERVICE || "budibase", _set(key: any, value: any) { process.env[key] = value module.exports[key] = value diff --git a/packages/backend-core/src/events/analytics.ts b/packages/backend-core/src/events/analytics.ts index e2f219f554..58c49714f7 100644 --- a/packages/backend-core/src/events/analytics.ts +++ b/packages/backend-core/src/events/analytics.ts @@ -3,6 +3,7 @@ import * as tenancy from "../tenancy" import * as dbUtils from "../db/utils" import { Configs } from "../constants" +// TODO: cache in redis export const enabled = async () => { // cloud - always use the environment variable if (!env.SELF_HOSTED) { diff --git a/packages/backend-core/src/events/identification.ts b/packages/backend-core/src/events/identification.ts index 8eabfa2618..6157b28916 100644 --- a/packages/backend-core/src/events/identification.ts +++ b/packages/backend-core/src/events/identification.ts @@ -1,56 +1,239 @@ import * as context from "../context" +import * as identityCtx from "../context/identity" import env from "../environment" import { Hosting, User, - SessionUser, Identity, IdentityType, Account, - BudibaseIdentity, isCloudAccount, isSSOAccount, - TenantIdentity, + TenantGroup, SettingsConfig, CloudAccount, UserIdentity, - InstallationIdentity, - Installation, - isInstallation, + InstallationGroup, + isSelfHostAccount, + UserContext, + Group, } from "@budibase/types" import { processors } from "./processors" import * as dbUtils from "../db/utils" import { Configs } from "../constants" import * as hashing from "../hashing" +import * as installation from "../installation" const pkg = require("../../package.json") +/** + * An identity can be: + * - account user (Self host) + * - budibase user + * - tenant + * - installation + */ export const getCurrentIdentity = async (): Promise => { - const user: SessionUser | undefined = context.getUser() + let identityContext = identityCtx.getIdentity() - const tenantId = await getGlobalTenantId(context.getTenantId()) - let id: string - let type: IdentityType + let identityType - if (user) { - id = user._id - type = IdentityType.USER + if (!identityContext) { + identityType = IdentityType.TENANT } else { - id = tenantId - type = IdentityType.TENANT + identityType = identityContext.type } - if (user && isInstallation(user)) { - type = IdentityType.INSTALLATION - } + if (identityType === IdentityType.INSTALLATION) { + const installationId = await getInstallationId() + return { + id: formatDistinctId(installationId, identityType), + type: identityType, + installationId, + } + } else if (identityType === IdentityType.TENANT) { + const installationId = await getInstallationId() + const tenantId = await getCurrentTenantId() - return { - id, - tenantId, - type, + return { + id: formatDistinctId(tenantId, identityType), + type: identityType, + installationId, + tenantId, + } + } else if (identityType === IdentityType.USER) { + const userContext = identityContext as UserContext + const tenantId = await getCurrentTenantId() + let installationId: string | undefined + + // self host account users won't have installation + if (!userContext.account || !isSelfHostAccount(userContext.account)) { + installationId = await getInstallationId() + } + + return { + id: userContext._id, + type: identityType, + installationId, + tenantId, + } + } else { + throw new Error("Unknown identity type") } } +export const identifyInstallationGroup = async ( + installId: string, + timestamp?: string | number +): Promise => { + const id = installId + const type = IdentityType.INSTALLATION + const hosting = getHostingFromEnv() + const version = pkg.version + + const group: InstallationGroup = { + id, + type, + hosting, + version, + } + + await identifyGroup(group, timestamp) + // need to create a normal identity for the group to be able to query it globally + // match the posthog syntax to link this identity to the empty auto generated one + await identify({ ...group, id: `$${type}_${id}` }, timestamp) +} + +export const identifyTenantGroup = async ( + tenantId: string, + account: Account | undefined, + timestamp?: string | number +): Promise => { + const id = await getGlobalTenantId(tenantId) + const type = IdentityType.TENANT + + let hosting: Hosting + let profession: string | undefined + let companySize: string | undefined + + if (account) { + profession = account.profession + companySize = account.size + hosting = account.hosting + } else { + hosting = getHostingFromEnv() + } + + const group: TenantGroup = { + id, + type, + hosting, + profession, + companySize, + } + + await identifyGroup(group, timestamp) + // need to create a normal identity for the group to be able to query it globally + // match the posthog syntax to link this identity to the auto generated one + await identify({ ...group, id: `$${type}_${id}` }, timestamp) +} + +export const identifyUser = async ( + user: User, + account: CloudAccount | undefined, + timestamp?: string | number +) => { + const id = user._id as string + const tenantId = await getGlobalTenantId(user.tenantId) + const type = IdentityType.USER + let builder = user.builder?.global || false + let admin = user.admin?.global || false + let providerType = user.providerType + const accountHolder = account?.budibaseUserId === user._id || false + const verified = + account && account?.budibaseUserId === user._id ? account.verified : false + const installationId = await getInstallationId() + + const identity: UserIdentity = { + id, + type, + installationId, + tenantId, + verified, + accountHolder, + providerType, + builder, + admin, + } + + await identify(identity, timestamp) +} + +export const identifyAccount = async (account: Account) => { + let id = account.accountId + const tenantId = account.tenantId + let type = IdentityType.USER + let providerType = isSSOAccount(account) ? account.providerType : undefined + const verified = account.verified + const accountHolder = true + + if (isCloudAccount(account)) { + if (account.budibaseUserId) { + // use the budibase user as the id if set + id = account.budibaseUserId + } + } + + const identity: UserIdentity = { + id, + type, + tenantId, + providerType, + verified, + accountHolder, + } + + await identify(identity) +} + +export const identify = async ( + identity: Identity, + timestamp?: string | number +) => { + await processors.identify(identity, timestamp) +} + +export const identifyGroup = async ( + group: Group, + timestamp?: string | number +) => { + await processors.identifyGroup(group, timestamp) +} + +const getHostingFromEnv = () => { + return env.SELF_HOSTED ? Hosting.SELF : Hosting.CLOUD +} + +export const getCurrentTenantId = () => getGlobalTenantId(context.getTenantId()) + +export const getInstallationId = async () => { + if (isAccountPortal()) { + return "account-portal" + } + const install = await installation.getInstall() + return install.installId +} + +const getGlobalTenantId = async (tenantId: string): Promise => { + if (env.SELF_HOSTED) { + return getGlobalId(tenantId) + } else { + // tenant id's in the cloud are already unique + return tenantId + } +} + +// TODO: cache in redis const getGlobalId = async (tenantId: string): Promise => { const db = context.getGlobalDB() const config: SettingsConfig = await dbUtils.getScopedFullConfig(db, { @@ -68,134 +251,14 @@ const getGlobalId = async (tenantId: string): Promise => { } } -const getGlobalTenantId = async (tenantId: string): Promise => { - if (env.SELF_HOSTED) { - return getGlobalId(tenantId) +const isAccountPortal = () => { + return env.SERVICE === "account-portal" +} + +const formatDistinctId = (id: string, type: IdentityType) => { + if (type === IdentityType.INSTALLATION || type === IdentityType.TENANT) { + return `$${type}_${id}` } else { - // tenant id's in the cloud are already unique - return tenantId + return id } } - -const getHostingFromEnv = () => { - return env.SELF_HOSTED ? Hosting.SELF : Hosting.CLOUD -} - -export const identifyInstallation = async ( - install: Installation, - timestamp: string | number -) => { - const id = install.installId - // the default tenant id, so we can match installations to other events - const tenantId = await getGlobalTenantId(context.getTenantId()) - const version: string = pkg.version as string - const type = IdentityType.INSTALLATION - const hosting = getHostingFromEnv() - - const identity: InstallationIdentity = { - id, - tenantId, - type, - version, - hosting, - } - await identify(identity, timestamp) -} - -export const identifyTenant = async ( - tenantId: string, - account: CloudAccount | undefined, - timestamp?: string | number -) => { - const globalTenantId = await getGlobalTenantId(tenantId) - const id = globalTenantId - const hosting = getHostingFromEnv() - const type = IdentityType.TENANT - const profession = account?.profession - const companySize = account?.size - - const identity: TenantIdentity = { - id, - tenantId: globalTenantId, - hosting, - type, - profession, - companySize, - } - await identify(identity, timestamp) -} - -export const identifyUser = async ( - user: User, - account: CloudAccount | undefined, - timestamp?: string | number -) => { - const id = user._id as string - const tenantId = user.tenantId - const hosting = env.SELF_HOSTED ? Hosting.SELF : Hosting.CLOUD - const type = IdentityType.USER - let builder = user.builder?.global - let admin = user.admin?.global - let providerType = user.providerType - const accountHolder = account?.budibaseUserId === user._id - const verified = - account && account?.budibaseUserId === user._id ? account.verified : false - const profession = account?.profession - const companySize = account?.size - - const identity: BudibaseIdentity = { - id, - tenantId, - hosting, - type, - builder, - admin, - providerType, - accountHolder, - verified, - profession, - companySize, - } - - await identify(identity, timestamp) -} - -export const identifyAccount = async (account: Account) => { - let id = account.accountId - const tenantId = account.tenantId - const hosting = account.hosting - let type = IdentityType.USER - let providerType = isSSOAccount(account) ? account.providerType : undefined - const verified = account.verified - const profession = account.profession - const companySize = account.size - const accountHolder = true - - if (isCloudAccount(account)) { - if (account.budibaseUserId) { - // use the budibase user as the id if set - id = account.budibaseUserId - } - } - - const identity: UserIdentity = { - id, - tenantId, - hosting, - type, - providerType, - verified, - profession, - companySize, - accountHolder, - } - - await identify(identity) -} - -export const identify = async ( - identity: Identity, - timestamp?: string | number -) => { - await processors.identify(identity, timestamp) -} diff --git a/packages/backend-core/src/events/processors/AnalyticsProcessor.ts b/packages/backend-core/src/events/processors/AnalyticsProcessor.ts index eaa5ae1135..30928abc8d 100644 --- a/packages/backend-core/src/events/processors/AnalyticsProcessor.ts +++ b/packages/backend-core/src/events/processors/AnalyticsProcessor.ts @@ -1,9 +1,15 @@ -import { Event, Identity } from "@budibase/types" +import { Event, Identity, Group, IdentityType } from "@budibase/types" import { EventProcessor } from "./types" import env from "../../environment" import * as analytics from "../analytics" import PosthogProcessor from "./PosthogProcessor" +/** + * Events that are always captured. + */ +const EVENT_WHITELIST = [Event.VERSION_UPGRADED, Event.VERSION_DOWNGRADED] +const IDENTITY_WHITELIST = [IdentityType.INSTALLATION, IdentityType.TENANT] + export default class AnalyticsProcessor implements EventProcessor { posthog: PosthogProcessor | undefined @@ -19,7 +25,7 @@ export default class AnalyticsProcessor implements EventProcessor { properties: any, timestamp?: string | number ): Promise { - if (!(await analytics.enabled())) { + if (!EVENT_WHITELIST.includes(event) && !(await analytics.enabled())) { return } if (this.posthog) { @@ -28,7 +34,11 @@ export default class AnalyticsProcessor implements EventProcessor { } async identify(identity: Identity, timestamp?: string | number) { - if (!(await analytics.enabled())) { + // Group indentifications (tenant and installation) always on + if ( + !IDENTITY_WHITELIST.includes(identity.type) && + !(await analytics.enabled()) + ) { return } if (this.posthog) { @@ -36,6 +46,13 @@ export default class AnalyticsProcessor implements EventProcessor { } } + async identifyGroup(group: Group, timestamp?: string | number) { + // Group indentifications (tenant and installation) always on + if (this.posthog) { + this.posthog.identifyGroup(group, timestamp) + } + } + shutdown() { if (this.posthog) { this.posthog.shutdown() diff --git a/packages/backend-core/src/events/processors/LoggingProcessor.ts b/packages/backend-core/src/events/processors/LoggingProcessor.ts index 47b91b7dea..a517fba09c 100644 --- a/packages/backend-core/src/events/processors/LoggingProcessor.ts +++ b/packages/backend-core/src/events/processors/LoggingProcessor.ts @@ -1,7 +1,17 @@ -import { Event, Identity } from "@budibase/types" +import { Event, Identity, Group } from "@budibase/types" import { EventProcessor } from "./types" import env from "../../environment" +const getTimestampString = (timestamp?: string | number) => { + let timestampString = "" + if (timestamp) { + timestampString = `[timestamp=${new Date(timestamp).toISOString()}]` + } + return timestampString +} + +const skipLogging = env.SELF_HOSTED && !env.isDev() + export default class LoggingProcessor implements EventProcessor { async processEvent( event: Event, @@ -9,34 +19,35 @@ export default class LoggingProcessor implements EventProcessor { properties: any, timestamp?: string ): Promise { - if (env.SELF_HOSTED && !env.isDev()) { + if (skipLogging) { return } - let timestampString = "" - if (timestamp) { - timestampString = `[timestamp=${new Date(timestamp).toISOString()}]` - } - + let timestampString = getTimestampString(timestamp) console.log( `[audit] [tenant=${identity.tenantId}] [identityType=${identity.type}] [identity=${identity.id}] ${timestampString} ${event} ` ) } async identify(identity: Identity, timestamp?: string | number) { - if (env.SELF_HOSTED && !env.isDev()) { + if (skipLogging) { return } - - let timestampString = "" - if (timestamp) { - timestampString = `[timestamp=${new Date(timestamp).toISOString()}]` - } - + let timestampString = getTimestampString(timestamp) console.log( `[audit] [${JSON.stringify(identity)}] ${timestampString} identified` ) } + async identifyGroup(group: Group, timestamp?: string | number) { + if (skipLogging) { + return + } + let timestampString = getTimestampString(timestamp) + console.log( + `[audit] [${JSON.stringify(group)}] ${timestampString} group identified` + ) + } + shutdown(): void { // no-op } diff --git a/packages/backend-core/src/events/processors/PosthogProcessor.ts b/packages/backend-core/src/events/processors/PosthogProcessor.ts index 54b5133dfb..4c1b4b1195 100644 --- a/packages/backend-core/src/events/processors/PosthogProcessor.ts +++ b/packages/backend-core/src/events/processors/PosthogProcessor.ts @@ -1,6 +1,8 @@ import PostHog from "posthog-node" -import { Event, Identity } from "@budibase/types" +import { Event, Identity, Group } from "@budibase/types" import { EventProcessor } from "./types" +import env from "../../environment" +const pkg = require("../../../package.json") export default class PosthogProcessor implements EventProcessor { posthog: PostHog @@ -18,10 +20,24 @@ export default class PosthogProcessor implements EventProcessor { properties: any, timestamp?: string | number ): Promise { + properties.version = pkg.version + properties.service = env.SERVICE const payload: any = { distinctId: identity.id, event, properties } if (timestamp) { payload.timestamp = new Date(timestamp) } + + // add groups to the event + if (identity.installationId || identity.tenantId) { + payload.groups = {} + if (identity.installationId) { + payload.groups.installation = identity.installationId + } + if (identity.tenantId) { + payload.groups.tenant = identity.tenantId + } + } + this.posthog.capture(payload) } @@ -33,6 +49,20 @@ export default class PosthogProcessor implements EventProcessor { this.posthog.identify(payload) } + async identifyGroup(group: Group, timestamp?: string | number) { + const payload: any = { + distinctId: group.id, + groupType: group.type, + groupKey: group.id, + properties: group, + } + + if (timestamp) { + payload.timestamp = new Date(timestamp) + } + this.posthog.groupIdentify(payload) + } + shutdown() { this.posthog.shutdown() } diff --git a/packages/backend-core/src/events/processors/Processors.ts b/packages/backend-core/src/events/processors/Processors.ts index 4263b7e06b..4baedd909f 100644 --- a/packages/backend-core/src/events/processors/Processors.ts +++ b/packages/backend-core/src/events/processors/Processors.ts @@ -1,4 +1,4 @@ -import { Event, Identity } from "@budibase/types" +import { Event, Identity, Group } from "@budibase/types" import { EventProcessor } from "./types" export default class Processor implements EventProcessor { @@ -29,6 +29,15 @@ export default class Processor implements EventProcessor { } } + async identifyGroup( + identity: Group, + timestamp?: string | number + ): Promise { + for (const eventProcessor of this.processors) { + await eventProcessor.identifyGroup(identity, timestamp) + } + } + shutdown() { for (const eventProcessor of this.processors) { eventProcessor.shutdown() diff --git a/packages/backend-core/src/events/processors/types.ts b/packages/backend-core/src/events/processors/types.ts index b8574cbd3d..f4066fe248 100644 --- a/packages/backend-core/src/events/processors/types.ts +++ b/packages/backend-core/src/events/processors/types.ts @@ -1,4 +1,4 @@ -import { Event, Identity } from "@budibase/types" +import { Event, Identity, Group } from "@budibase/types" export enum EventProcessorType { POSTHOG = "posthog", @@ -13,5 +13,6 @@ export interface EventProcessor { timestamp?: string | number ): Promise identify(identity: Identity, timestamp?: string | number): Promise + identifyGroup(group: Group, timestamp?: string | number): Promise shutdown(): void } diff --git a/packages/backend-core/src/events/publishers/layout.ts b/packages/backend-core/src/events/publishers/layout.ts index 7a334b059f..82e9f613ca 100644 --- a/packages/backend-core/src/events/publishers/layout.ts +++ b/packages/backend-core/src/events/publishers/layout.ts @@ -13,9 +13,9 @@ export async function created(layout: Layout, timestamp?: string) { await publishEvent(Event.LAYOUT_CREATED, properties, timestamp) } -export async function deleted(layout: Layout) { +export async function deleted(layoutId: string) { const properties: LayoutDeletedEvent = { - layoutId: layout._id as string, + layoutId, } await publishEvent(Event.LAYOUT_DELETED, properties) } diff --git a/packages/backend-core/src/events/publishers/serve.ts b/packages/backend-core/src/events/publishers/serve.ts index 62aaf4fb4c..8505df2987 100644 --- a/packages/backend-core/src/events/publishers/serve.ts +++ b/packages/backend-core/src/events/publishers/serve.ts @@ -9,17 +9,23 @@ import { /* eslint-disable */ -export async function servedBuilder(version: number) { +export async function servedBuilder() { const properties: BuilderServedEvent = {} await publishEvent(Event.SERVED_BUILDER, properties) } export async function servedApp(app: App) { - const properties: AppServedEvent = {} + const properties: AppServedEvent = { + appId: app.appId, + appVersion: app.version, + } await publishEvent(Event.SERVED_APP, properties) } export async function servedAppPreview(app: App) { - const properties: AppPreviewServedEvent = {} + const properties: AppPreviewServedEvent = { + appId: app.appId, + appVersion: app.version, + } await publishEvent(Event.SERVED_APP_PREVIEW, properties) } diff --git a/packages/backend-core/src/index.ts b/packages/backend-core/src/index.ts index c54ee1394e..b3687cc171 100644 --- a/packages/backend-core/src/index.ts +++ b/packages/backend-core/src/index.ts @@ -4,6 +4,7 @@ import * as events from "./events" import * as migrations from "./migrations" import * as users from "./users" import * as accounts from "./cloud/accounts" +import * as installation from "./installation" import env from "./environment" import tenancy from "./tenancy" import featureFlags from "./featureFlags" @@ -46,4 +47,5 @@ export = { events, sessions, deprovisioning, + installation, } diff --git a/packages/server/src/installation.ts b/packages/backend-core/src/installation.ts similarity index 71% rename from packages/server/src/installation.ts rename to packages/backend-core/src/installation.ts index 3832d6f474..25e4a311ed 100644 --- a/packages/server/src/installation.ts +++ b/packages/backend-core/src/installation.ts @@ -1,29 +1,27 @@ -import { - db as dbUtils, - events, - utils, - context, - tenancy, -} from "@budibase/backend-core" -import { Installation } from "@budibase/types" +import * as hashing from "./hashing" +import * as events from "./events" +import { StaticDatabases } from "./db/constants" +import { doWithDB } from "./db" +import { Installation, IdentityType } from "@budibase/types" +import * as context from "./context" import semver from "semver" const pkg = require("../package.json") export const getInstall = async (): Promise => { - return dbUtils.doWithDB( - dbUtils.StaticDatabases.PLATFORM_INFO.name, + return doWithDB( + StaticDatabases.PLATFORM_INFO.name, async (platformDb: any) => { let install: Installation try { install = await platformDb.get( - dbUtils.StaticDatabases.PLATFORM_INFO.docs.install + StaticDatabases.PLATFORM_INFO.docs.install ) } catch (e: any) { if (e.status === 404) { install = { - _id: dbUtils.StaticDatabases.PLATFORM_INFO.docs.install, - installId: utils.newid(), + _id: StaticDatabases.PLATFORM_INFO.docs.install, + installId: hashing.newid(), version: pkg.version, } const resp = await platformDb.put(install) @@ -40,8 +38,8 @@ export const getInstall = async (): Promise => { const updateVersion = async (version: string): Promise => { try { - await dbUtils.doWithDB( - dbUtils.StaticDatabases.PLATFORM_INFO.name, + await doWithDB( + StaticDatabases.PLATFORM_INFO.name, async (platformDb: any) => { const install = await getInstall() install.version = version @@ -73,11 +71,10 @@ export const checkInstallVersion = async (): Promise => { const success = await updateVersion(newVersion) if (success) { - await context.doInUserContext( + await context.doInIdentityContext( { _id: install.installId, - isInstall: true, - tenantId: tenancy.DEFAULT_TENANT_ID, + type: IdentityType.INSTALLATION, }, async () => { if (isUpgrade) { @@ -87,6 +84,7 @@ export const checkInstallVersion = async (): Promise => { } } ) + await events.identification.identifyInstallationGroup(install.installId) } } } diff --git a/packages/backend-core/src/middleware/authenticated.js b/packages/backend-core/src/middleware/authenticated.js index 3fef6eadac..66e0d6c755 100644 --- a/packages/backend-core/src/middleware/authenticated.js +++ b/packages/backend-core/src/middleware/authenticated.js @@ -7,7 +7,7 @@ const env = require("../environment") const { SEPARATOR, ViewNames, queryGlobalView } = require("../../db") const { getGlobalDB, doInTenant } = require("../tenancy") const { decrypt } = require("../security/encryption") -const context = require("../context") +const identity = require("../context/identity") function finalise( ctx, @@ -135,7 +135,7 @@ module.exports = ( finalise(ctx, { authenticated, user, internal, version, publicEndpoint }) if (user && user.email) { - return context.doInUserContext(user, next) + return identity.doInUserContext(user, next) } else { return next() } @@ -147,7 +147,7 @@ module.exports = ( // allow configuring for public access if ((opts && opts.publicAllowed) || publicEndpoint) { finalise(ctx, { authenticated: false, version, publicEndpoint }) - return context.doInUserContext({ _id: "public_user" }, next) + return next() } else { ctx.throw(err.status || 403, err) } diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index 86bfda2dbe..746deb3428 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -56,9 +56,11 @@ jest.mock("../../../events", () => { nameUpdated: jest.fn(), logoUpdated: jest.fn(), platformURLUpdated: jest.fn(), - versionChecked: jest.fn(), analyticsOptOut: jest.fn(), }, + version: { + checked: jest.fn(), + }, query: { created: jest.fn(), updated: jest.fn(), diff --git a/packages/backend-core/yarn.lock b/packages/backend-core/yarn.lock index 9669545887..638fc47117 100644 --- a/packages/backend-core/yarn.lock +++ b/packages/backend-core/yarn.lock @@ -971,6 +971,11 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== +"@types/semver@^7.0.0": + version "7.3.9" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.9.tgz#152c6c20a7688c30b967ec1841d31ace569863fc" + integrity sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ== + "@types/serve-static@*": version "1.13.10" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" @@ -5012,7 +5017,7 @@ semver-diff@^3.1.1: dependencies: semver "^6.3.0" -semver@7.x, semver@^7.3.4: +semver@7.x, semver@^7.0.0, semver@^7.3.4: version "7.3.7" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== diff --git a/packages/server/package.json b/packages/server/package.json index 9e336d0280..36ea8391ee 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -129,7 +129,6 @@ "pouchdb-find": "^7.2.2", "pouchdb-replication-stream": "1.2.9", "redis": "4", - "semver": "^7.0.0", "server-destroy": "1.0.1", "svelte": "^3.38.2", "swagger-parser": "^10.0.3", @@ -160,7 +159,6 @@ "@types/node": "^15.12.4", "@types/oracledb": "^5.2.1", "@types/redis": "^4.0.11", - "@types/semver": "^7.0.0", "@typescript-eslint/parser": "5.12.0", "apidoc": "^0.50.2", "babel-jest": "^27.0.2", diff --git a/packages/server/src/api/controllers/dev.js b/packages/server/src/api/controllers/dev.js index 03ac140eb2..dd2bf45b21 100644 --- a/packages/server/src/api/controllers/dev.js +++ b/packages/server/src/api/controllers/dev.js @@ -131,5 +131,5 @@ exports.getBudibaseVersion = async ctx => { ctx.body = { version, } - await events.version.versionChecked(version) + await events.version.checked(version) } diff --git a/packages/server/src/api/controllers/layout.js b/packages/server/src/api/controllers/layout.js index 044d4a1acb..c7d3216cfc 100644 --- a/packages/server/src/api/controllers/layout.js +++ b/packages/server/src/api/controllers/layout.js @@ -20,7 +20,7 @@ exports.save = async function (ctx) { layout._id = layout._id || generateLayoutID() const response = await db.put(layout) - await events.layout.created() + await events.layout.created(layout) layout._rev = response.rev ctx.body = layout @@ -48,7 +48,7 @@ exports.destroy = async function (ctx) { } await db.remove(layoutId, layoutRev) - await events.layout.deleted() + await events.layout.deleted(layoutId) ctx.body = { message: "Layout deleted successfully" } ctx.status = 200 } diff --git a/packages/server/src/api/controllers/static/index.js b/packages/server/src/api/controllers/static/index.js index 07ad691faa..d21e38f879 100644 --- a/packages/server/src/api/controllers/static/index.js +++ b/packages/server/src/api/controllers/static/index.js @@ -19,7 +19,6 @@ const { getAppDB, getAppId } = require("@budibase/backend-core/context") const AWS = require("aws-sdk") const AWS_REGION = env.AWS_REGION ? env.AWS_REGION : "eu-west-1" const { events } = require("@budibase/backend-core") -const version = require("../../../../package.json").version async function prepareUpload({ s3Key, bucket, metadata, file }) { const response = await upload({ @@ -43,7 +42,7 @@ async function prepareUpload({ s3Key, bucket, metadata, file }) { exports.serveBuilder = async function (ctx) { let builderPath = resolve(TOP_LEVEL_PATH, "builder") await send(ctx, ctx.file, { root: builderPath }) - await events.serve.servedBuilder(version) + await events.serve.servedBuilder() } exports.uploadFile = async function (ctx) { diff --git a/packages/server/src/api/routes/tests/dev.spec.js b/packages/server/src/api/routes/tests/dev.spec.js index 0138ce2a6c..8dee3fd418 100644 --- a/packages/server/src/api/routes/tests/dev.spec.js +++ b/packages/server/src/api/routes/tests/dev.spec.js @@ -33,8 +33,8 @@ describe("/dev", () => { .expect(200) expect(res.body.version).toBe(version) - expect(events.org.versionChecked).toBeCalledTimes(1) - expect(events.org.versionChecked).toBeCalledWith(version) + expect(events.version.checked).toBeCalledTimes(1) + expect(events.version.checked).toBeCalledWith(version) }) }) }) \ No newline at end of file diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts index 127d0f8853..63dc3f1a6f 100644 --- a/packages/server/src/app.ts +++ b/packages/server/src/app.ts @@ -16,8 +16,7 @@ const fileSystem = require("./utilities/fileSystem") const bullboard = require("./automations/bullboard") import redis from "./utilities/redis" import * as migrations from "./migrations" -import { events } from "@budibase/backend-core" -import * as installation from "./installation" +import { events, installation } from "@budibase/backend-core" const app = new Koa() diff --git a/packages/server/src/migrations/functions/backfill/global.ts b/packages/server/src/migrations/functions/backfill/global.ts index 99d08c46ed..a03ae67f41 100644 --- a/packages/server/src/migrations/functions/backfill/global.ts +++ b/packages/server/src/migrations/functions/backfill/global.ts @@ -2,9 +2,8 @@ import * as users from "./global/users" import * as rows from "./global/rows" import * as configs from "./global/configs" import { tenancy, events, migrations, accounts } from "@budibase/backend-core" -import { CloudAccount, Installation } from "@budibase/types" +import { CloudAccount } from "@budibase/types" import env from "../../../environment" -import * as installation from "../../../installation" /** * Date: @@ -22,7 +21,7 @@ export const run = async (db: any) => { account = await accounts.getAccountByTenantId(tenantId) } - await events.identification.identifyTenant( + await events.identification.identifyTenantGroup( tenantId, account, installTimestamp diff --git a/packages/server/src/migrations/functions/backfill/installation.ts b/packages/server/src/migrations/functions/backfill/installation.ts index bf3b413fa7..b807d650e8 100644 --- a/packages/server/src/migrations/functions/backfill/installation.ts +++ b/packages/server/src/migrations/functions/backfill/installation.ts @@ -1,6 +1,5 @@ -import { events, tenancy } from "@budibase/backend-core" +import { events, tenancy, installation } from "@budibase/backend-core" import { Installation } from "@budibase/types" -import * as installation from "../../../installation" import * as global from "./global" /** @@ -17,6 +16,9 @@ export const run = async () => { const db = tenancy.getGlobalDB() const installTimestamp = (await global.getInstallTimestamp(db)) as number const install: Installation = await installation.getInstall() - await events.identification.identifyInstallation(install, installTimestamp) + await events.identification.identifyInstallationGroup( + install.installId, + installTimestamp + ) }) } diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 7236ec8d84..4548fd2bd0 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -2754,11 +2754,6 @@ "@types/tough-cookie" "*" form-data "^2.5.0" -"@types/semver@^7.0.0": - version "7.3.9" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.9.tgz#152c6c20a7688c30b967ec1841d31ace569863fc" - integrity sha512-L/TMpyURfBkf+o/526Zb6kd/tchUP3iBDEPjqjb+U2MAJhVRxxrmr2fwpe08E7QsV7YLcpq0tUaQ9O9x97ZIxQ== - "@types/serve-static@*": version "1.13.10" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" @@ -11650,13 +11645,6 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.0.0: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== - dependencies: - lru-cache "^6.0.0" - seq-queue@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" diff --git a/packages/types/src/core/context.ts b/packages/types/src/core/context.ts new file mode 100644 index 0000000000..bf433d5e5f --- /dev/null +++ b/packages/types/src/core/context.ts @@ -0,0 +1,19 @@ +import { User, Account } from "../documents" +import { IdentityType } from "./identification" + +export interface BaseContext { + _id: string + type: IdentityType +} + +export interface AccountUserContext extends BaseContext { + tenantId: string + account: Account +} + +export interface UserContext extends BaseContext, User { + _id: string + account?: Account +} + +export type IdentityContext = BaseContext | AccountUserContext | UserContext diff --git a/packages/types/src/core/identification.ts b/packages/types/src/core/identification.ts new file mode 100644 index 0000000000..b7ce0fbaf4 --- /dev/null +++ b/packages/types/src/core/identification.ts @@ -0,0 +1,49 @@ +import { Hosting } from "." + +// GROUPS + +export enum GroupType { + TENANT = "tenant", + INSTALLATION = "installation", +} + +export interface Group { + id: string + type: IdentityType +} + +export interface TenantGroup extends Group { + // account level information is associated with the tenant group + // as we don't have this at the user level + profession?: string // only available in cloud + companySize?: string // only available in cloud + hosting: Hosting // need hosting at the tenant level for cloud self host accounts +} + +export interface InstallationGroup extends Group { + version: string + hosting: Hosting +} + +// IDENTITIES + +export enum IdentityType { + USER = "user", + TENANT = "tenant", + INSTALLATION = "installation", +} + +export interface Identity { + id: string + type: IdentityType + installationId?: string + tenantId?: string +} + +export interface UserIdentity extends Identity { + verified: boolean + accountHolder: boolean + providerType?: string + builder?: boolean + admin?: boolean +} diff --git a/packages/types/src/core/index.ts b/packages/types/src/core/index.ts index 23d772467a..bed86d4f9f 100644 --- a/packages/types/src/core/index.ts +++ b/packages/types/src/core/index.ts @@ -1,2 +1,3 @@ export * from "./hosting" -export * from "./sessions" +export * from "./context" +export * from "./identification" diff --git a/packages/types/src/core/sessions.ts b/packages/types/src/core/sessions.ts deleted file mode 100644 index 56db6e4f54..0000000000 --- a/packages/types/src/core/sessions.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { User, Account } from "../documents" -import { Hosting } from "./hosting" - -/** - * Account portal user session. Used for self hosted accounts only. - */ -export interface AccountUserSession { - _id: string - email: string - tenantId: string - accountPortalAccess: boolean - account: Account -} - -/** - * Budibase user session. - */ -export interface BudibaseUserSession extends User { - _id: string // overwrite potentially undefined - account?: Account - accountPortalAccess?: boolean -} - -export const isAccountSession = ( - user: AccountUserSession | BudibaseUserSession -): user is AccountUserSession => { - return user.account?.hosting === Hosting.SELF -} - -export const isUserSession = ( - user: AccountUserSession | BudibaseUserSession -): user is BudibaseUserSession => { - return !user.account || user.account?.hosting === Hosting.CLOUD -} - -// not technically a session, but used to identify the installation -export interface InstallationSession { - _id: string - isInstallation: boolean -} - -export const isInstallation = (user: any): user is InstallationSession => { - return !!user.isInstallation -} - -export type SessionUser = - | AccountUserSession - | BudibaseUserSession - | InstallationSession diff --git a/packages/types/src/events/auth.ts b/packages/types/src/events/auth.ts index c0e046a2ca..c7e6cbb4ef 100644 --- a/packages/types/src/events/auth.ts +++ b/packages/types/src/events/auth.ts @@ -1,4 +1,4 @@ -export type LoginSource = "local" | "google" | "oidc" +export type LoginSource = "local" | "google" | "oidc" | "google-internal" export type SSOType = "oidc" | "google" export interface LoginEvent { diff --git a/packages/types/src/events/event.ts b/packages/types/src/events/event.ts index 910a30f925..d5528ea114 100644 --- a/packages/types/src/events/event.ts +++ b/packages/types/src/events/event.ts @@ -8,7 +8,7 @@ export enum Event { USER_PERMISSION_ADMIN_ASSIGNED = "user:admin:assigned", USER_PERMISSION_ADMIN_REMOVED = "user:admin:removed", USER_PERMISSION_BUILDER_ASSIGNED = "user:builder:assigned", - USER_PERMISSION_BUILDER_REMOVED = "userbuilder:removed", + USER_PERMISSION_BUILDER_REMOVED = "user:builder:removed", // USER / INVITE USER_INVITED = "user:invited", diff --git a/packages/types/src/events/identification.ts b/packages/types/src/events/identification.ts deleted file mode 100644 index 29d0aa333e..0000000000 --- a/packages/types/src/events/identification.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Hosting } from "../core" - -export enum IdentityType { - USER = "user", - TENANT = "tenant", - INSTALLATION = "installation", -} - -export interface Identity { - id: string - tenantId: string - type: IdentityType -} - -export interface InstallationIdentity extends Identity { - version: string - hosting: Hosting -} - -export interface TenantIdentity extends Identity { - hosting: Hosting - profession?: string - companySize?: string -} - -export interface UserIdentity extends TenantIdentity { - hosting: Hosting - type: IdentityType - verified: boolean - accountHolder: boolean - providerType?: string -} - -export interface BudibaseIdentity extends UserIdentity { - builder?: boolean - admin?: boolean -} diff --git a/packages/types/src/events/index.ts b/packages/types/src/events/index.ts index c55711205c..44f6f297a6 100644 --- a/packages/types/src/events/index.ts +++ b/packages/types/src/events/index.ts @@ -15,5 +15,4 @@ export * from "./serve" export * from "./table" export * from "./user" export * from "./view" -export * from "./identification" export * from "./account" diff --git a/packages/types/src/events/serve.ts b/packages/types/src/events/serve.ts index f3fd21dd15..8ac9ae2c6b 100644 --- a/packages/types/src/events/serve.ts +++ b/packages/types/src/events/serve.ts @@ -1,5 +1,11 @@ export interface BuilderServedEvent {} -export interface AppServedEvent {} +export interface AppServedEvent { + appId: string + appVersion: string +} -export interface AppPreviewServedEvent {} +export interface AppPreviewServedEvent { + appId: string + appVersion: string +} diff --git a/packages/worker/src/api/controllers/global/auth.ts b/packages/worker/src/api/controllers/global/auth.ts index 71d0afbb52..35d705c2fd 100644 --- a/packages/worker/src/api/controllers/global/auth.ts +++ b/packages/worker/src/api/controllers/global/auth.ts @@ -70,9 +70,9 @@ async function authInternal(ctx: any, user: any, err = null, info = null) { export const authenticate = async (ctx: any, next: any) => { return passport.authenticate( "local", - async (err: any, user: any, info: any) => { + async (err: any, user: User, info: any) => { await authInternal(ctx, user, err, info) - await context.doInUserContext(user, async () => { + await context.identity.doInUserContext(user, async () => { await events.auth.login("local") }) ctx.status = 200 @@ -213,10 +213,10 @@ export const googleAuth = async (ctx: any, next: any) => { return passport.authenticate( strategy, { successRedirect: "/", failureRedirect: "/error" }, - async (err: any, user: any, info: any) => { + async (err: any, user: User, info: any) => { await authInternal(ctx, user, err, info) - await context.doInUserContext(user, async () => { - await events.auth.login("google") + await context.identity.doInUserContext(user, async () => { + await events.auth.login("google-internal") }) ctx.redirect("/") } @@ -261,7 +261,7 @@ export const oidcAuth = async (ctx: any, next: any) => { { successRedirect: "/", failureRedirect: "/error" }, async (err: any, user: any, info: any) => { await authInternal(ctx, user, err, info) - await context.doInUserContext(user, async () => { + await context.identity.doInUserContext(user, async () => { await events.auth.login("oidc") }) ctx.redirect("/") diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index 42b98fd791..5a86ace16b 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -69,7 +69,7 @@ export const adminUser = async (ctx: any) => { if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { account = await accounts.getAccountByTenantId(tenantId) } - await events.identification.identifyTenant(tenantId, account) + await events.identification.identifyTenantGroup(tenantId, account) } catch (err: any) { ctx.throw(err.status || 400, err) } diff --git a/scripts/link-dependencies.sh b/scripts/link-dependencies.sh index 8c4294fa08..cf9c536b1d 100755 --- a/scripts/link-dependencies.sh +++ b/scripts/link-dependencies.sh @@ -8,6 +8,12 @@ cd packages/string-templates yarn link cd - +echo "Linking types" +cd packages/types +yarn link +cd - + + if [ -d "../budibase-pro" ]; then cd ../budibase-pro yarn bootstrap @@ -38,6 +44,9 @@ if [ -d "../account-portal" ]; then echo "Linking string-templates to account-portal" yarn link "@budibase/string-templates" + echo "Linking types to account-portal" + yarn link "@budibase/types" + if [ -d "../../../budibase-pro" ]; then echo "Linking pro to account-portal" yarn link "@budibase/pro" From a9ab8b262931459474d088a955e6eb4e41019f89 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Mon, 30 May 2022 21:46:08 +0100 Subject: [PATCH 50/75] Lots of failure handling and caching --- package.json | 2 +- packages/backend-core/package.json | 1 + packages/backend-core/src/cache/generic.js | 7 +- packages/backend-core/src/db/utils.ts | 1 + packages/backend-core/src/events/analytics.ts | 27 +- .../backend-core/src/events/identification.ts | 49 ++-- .../src/events/publishers/automation.ts | 8 + .../src/events/publishers/backfill.ts | 67 +++++ .../src/events/publishers/index.ts | 1 + .../src/events/publishers/query.ts | 8 + packages/backend-core/src/installation.ts | 8 + .../src/tests/utilities/mocks/events.js | 235 ++++++++---------- packages/backend-core/tsconfig.build.json | 3 + packages/backend-core/yarn.lock | 181 +------------- packages/server/scripts/jestSetup.js | 13 +- .../server/src/api/controllers/automation.js | 4 +- .../server/src/api/controllers/datasource.js | 2 +- .../query/import/tests/index.spec.js | 8 +- packages/server/src/app.ts | 28 ++- .../src/migrations/functions/backfill/app.ts | 119 +++++++-- .../functions/backfill/app/automations.ts | 2 + .../functions/backfill/app/datasources.ts | 2 + .../functions/backfill/app/layouts.ts | 2 + .../functions/backfill/app/queries.ts | 2 + .../functions/backfill/app/roles.ts | 2 + .../functions/backfill/app/screens.ts | 2 + .../functions/backfill/app/tables.ts | 2 + .../migrations/functions/backfill/global.ts | 142 +++++++++-- .../functions/backfill/global/quotas.ts | 53 ++++ .../functions/backfill/global/rows.ts | 28 --- .../functions/backfill/global/users.ts | 2 + .../functions/backfill/installation.ts | 40 ++- packages/types/src/documents/global/config.ts | 2 +- packages/types/src/events/automation.ts | 4 + packages/types/src/events/backfill.ts | 35 +++ packages/types/src/events/event.ts | 12 +- packages/types/src/events/index.ts | 1 + packages/types/src/events/query.ts | 4 + .../src/api/controllers/global/configs.js | 1 + 39 files changed, 669 insertions(+), 441 deletions(-) create mode 100644 packages/backend-core/src/events/publishers/backfill.ts create mode 100644 packages/server/src/migrations/functions/backfill/global/quotas.ts delete mode 100644 packages/server/src/migrations/functions/backfill/global/rows.ts create mode 100644 packages/types/src/events/backfill.ts diff --git a/package.json b/package.json index 7a410482db..bf9fd72e3c 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "setup": "node ./hosting/scripts/setup.js && yarn && yarn bootstrap && yarn build && yarn dev", "bootstrap": "lerna link && lerna bootstrap && ./scripts/link-dependencies.sh", "build": "lerna run build", - "build:watch": "lerna run build:watch --stream --parallel", + "build:watch": "lerna run build:watch --ignore @budibase/backend-core --stream --parallel", "release": "lerna publish patch --yes --force-publish && yarn release:pro", "release:develop": "lerna publish prerelease --yes --force-publish --dist-tag develop && yarn release:pro:develop", "release:pro": "bash scripts/pro/release.sh", diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 8f3a8a9f52..6c5063eb01 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -8,6 +8,7 @@ "license": "GPL-3.0", "scripts": { "build": "rimraf dist/ && tsc -p tsconfig.build.json", + "build:watch": "rimraf dist/ && tsc --build tsconfig.build.json --watch --preserveWatchOutput", "test": "jest", "test:watch": "jest --watchAll" }, diff --git a/packages/backend-core/src/cache/generic.js b/packages/backend-core/src/cache/generic.js index b23568f8b9..9ddde7f99a 100644 --- a/packages/backend-core/src/cache/generic.js +++ b/packages/backend-core/src/cache/generic.js @@ -4,6 +4,9 @@ const { getTenantId } = require("../context") exports.CacheKeys = { CHECKLIST: "checklist", + INSTALLATION: "installation", + ANALYTICS_ENABLED: "analyticsEnabled", + UNIQUE_TENANT_ID: "uniqueTenantId", } exports.TTL = { @@ -17,8 +20,8 @@ function generateTenantKey(key) { return `${key}:${tenantId}` } -exports.withCache = async (key, ttl, fetchFn) => { - key = generateTenantKey(key) +exports.withCache = async (key, ttl, fetchFn, opts = { useTenancy: true }) => { + key = opts.useTenancy ? generateTenantKey(key) : key const client = await redis.getCacheClient() const cachedValue = await client.get(key) if (cachedValue) { diff --git a/packages/backend-core/src/db/utils.ts b/packages/backend-core/src/db/utils.ts index 9bf1abd18c..7e95b7df97 100644 --- a/packages/backend-core/src/db/utils.ts +++ b/packages/backend-core/src/db/utils.ts @@ -391,6 +391,7 @@ export const getScopedFullConfig = async function ( // defaults scopedConfig = { doc: { + _id: generateConfigID({ type, user, workspace }), config: { platformUrl: await getPlatformUrl(), analyticsEnabled: await events.analytics.enabled(), diff --git a/packages/backend-core/src/events/analytics.ts b/packages/backend-core/src/events/analytics.ts index 58c49714f7..a7e2067fd6 100644 --- a/packages/backend-core/src/events/analytics.ts +++ b/packages/backend-core/src/events/analytics.ts @@ -2,8 +2,8 @@ import env from "../environment" import * as tenancy from "../tenancy" import * as dbUtils from "../db/utils" import { Configs } from "../constants" +import { withCache, TTL, CacheKeys } from "../cache/generic" -// TODO: cache in redis export const enabled = async () => { // cloud - always use the environment variable if (!env.SELF_HOSTED) { @@ -11,13 +11,24 @@ export const enabled = async () => { } // self host - prefer the settings doc - // check for explicit true/false values to support - // backwards compatibility where setting may not exist - const settings = await getSettingsDoc() - if (settings?.config?.analyticsEnabled === false) { - return false - } else if (settings?.config?.analyticsEnabled === true) { - return true + // use cache as events have high throughput + const enabledInDB = await withCache( + CacheKeys.ANALYTICS_ENABLED, + TTL.ONE_DAY, + async () => { + const settings = await getSettingsDoc() + + // need to do explicit checks in case the field is not set + if (settings?.config?.analyticsEnabled === false) { + return false + } else if (settings?.config?.analyticsEnabled === true) { + return true + } + } + ) + + if (enabledInDB !== undefined) { + return enabledInDB } // fallback to the environment variable diff --git a/packages/backend-core/src/events/identification.ts b/packages/backend-core/src/events/identification.ts index 6157b28916..b8800bfd12 100644 --- a/packages/backend-core/src/events/identification.ts +++ b/packages/backend-core/src/events/identification.ts @@ -23,6 +23,7 @@ import * as dbUtils from "../db/utils" import { Configs } from "../constants" import * as hashing from "../hashing" import * as installation from "../installation" +import { withCache, TTL, CacheKeys } from "../cache/generic" const pkg = require("../../package.json") @@ -53,7 +54,7 @@ export const getCurrentIdentity = async (): Promise => { } } else if (identityType === IdentityType.TENANT) { const installationId = await getInstallationId() - const tenantId = await getCurrentTenantId() + const tenantId = await getEventTenantId(context.getTenantId()) return { id: formatDistinctId(tenantId, identityType), @@ -63,7 +64,7 @@ export const getCurrentIdentity = async (): Promise => { } } else if (identityType === IdentityType.USER) { const userContext = identityContext as UserContext - const tenantId = await getCurrentTenantId() + const tenantId = await getEventTenantId(context.getTenantId()) let installationId: string | undefined // self host account users won't have installation @@ -109,7 +110,7 @@ export const identifyTenantGroup = async ( account: Account | undefined, timestamp?: string | number ): Promise => { - const id = await getGlobalTenantId(tenantId) + const id = await getEventTenantId(tenantId) const type = IdentityType.TENANT let hosting: Hosting @@ -144,7 +145,7 @@ export const identifyUser = async ( timestamp?: string | number ) => { const id = user._id as string - const tenantId = await getGlobalTenantId(user.tenantId) + const tenantId = await getEventTenantId(user.tenantId) const type = IdentityType.USER let builder = user.builder?.global || false let admin = user.admin?.global || false @@ -214,8 +215,6 @@ const getHostingFromEnv = () => { return env.SELF_HOSTED ? Hosting.SELF : Hosting.CLOUD } -export const getCurrentTenantId = () => getGlobalTenantId(context.getTenantId()) - export const getInstallationId = async () => { if (isAccountPortal()) { return "account-portal" @@ -224,31 +223,35 @@ export const getInstallationId = async () => { return install.installId } -const getGlobalTenantId = async (tenantId: string): Promise => { +const getEventTenantId = async (tenantId: string): Promise => { if (env.SELF_HOSTED) { - return getGlobalId(tenantId) + return getUniqueTenantId(tenantId) } else { // tenant id's in the cloud are already unique return tenantId } } -// TODO: cache in redis -const getGlobalId = async (tenantId: string): Promise => { - const db = context.getGlobalDB() - const config: SettingsConfig = await dbUtils.getScopedFullConfig(db, { - type: Configs.SETTINGS, - }) +const getUniqueTenantId = async (tenantId: string): Promise => { + // make sure this tenantId always matches the tenantId in context + return context.doInTenant(tenantId, () => { + return withCache(CacheKeys.UNIQUE_TENANT_ID, TTL.ONE_DAY, async () => { + const db = context.getGlobalDB() + const config: SettingsConfig = await dbUtils.getScopedFullConfig(db, { + type: Configs.SETTINGS, + }) - let globalId: string - if (config.config.globalId) { - return config.config.globalId - } else { - globalId = `${hashing.newid()}_${tenantId}` - config.config.globalId = globalId - await db.put(config) - return globalId - } + let uniqueTenantId: string + if (config.config.uniqueTenantId) { + return config.config.uniqueTenantId + } else { + uniqueTenantId = `${hashing.newid()}_${tenantId}` + config.config.uniqueTenantId = uniqueTenantId + await db.put(config) + return uniqueTenantId + } + }) + }) } const isAccountPortal = () => { diff --git a/packages/backend-core/src/events/publishers/automation.ts b/packages/backend-core/src/events/publishers/automation.ts index daaa06f571..e1df28ead8 100644 --- a/packages/backend-core/src/events/publishers/automation.ts +++ b/packages/backend-core/src/events/publishers/automation.ts @@ -9,6 +9,7 @@ import { AutomationStepCreatedEvent, AutomationStepDeletedEvent, AutomationTriggerUpdatedEvent, + AutomationsRunEvent, } from "@budibase/types" export async function created(automation: Automation, timestamp?: string) { @@ -51,6 +52,13 @@ export async function tested(automation: Automation) { await publishEvent(Event.AUTOMATION_TESTED, properties) } +export const run = async (count: number, timestamp?: string | number) => { + const properties: AutomationsRunEvent = { + count, + } + await publishEvent(Event.AUTOMATIONS_RUN, properties, timestamp) +} + export async function stepCreated( automation: Automation, step: AutomationStep, diff --git a/packages/backend-core/src/events/publishers/backfill.ts b/packages/backend-core/src/events/publishers/backfill.ts new file mode 100644 index 0000000000..c16b3cc9dc --- /dev/null +++ b/packages/backend-core/src/events/publishers/backfill.ts @@ -0,0 +1,67 @@ +import { publishEvent } from "../events" +import { + Event, + AppBackfillSucceededEvent, + AppBackfillFailedEvent, + TenantBackfillSucceededEvent, + TenantBackfillFailedEvent, + InstallationBackfillSucceededEvent, + InstallationBackfillFailedEvent, +} from "@budibase/types" +const env = require("../../environment") + +const shouldSkip = !env.SELF_HOSTED && !env.isDev() + +export async function appSucceeded(properties: AppBackfillSucceededEvent) { + if (shouldSkip) { + return + } + await publishEvent(Event.APP_BACKFILL_SUCCEEDED, properties) +} + +export async function appFailed(error: any) { + if (shouldSkip) { + return + } + const properties: AppBackfillFailedEvent = { + error: JSON.stringify(error, Object.getOwnPropertyNames(error)), + } + await publishEvent(Event.APP_BACKFILL_FAILED, properties) +} + +export async function tenantSucceeded( + properties: TenantBackfillSucceededEvent +) { + if (shouldSkip) { + return + } + await publishEvent(Event.TENANT_BACKFILL_SUCCEEDED, properties) +} + +export async function tenantFailed(error: any) { + if (shouldSkip) { + return + } + const properties: TenantBackfillFailedEvent = { + error: JSON.stringify(error, Object.getOwnPropertyNames(error)), + } + await publishEvent(Event.TENANT_BACKFILL_FAILED, properties) +} + +export async function installationSucceeded() { + if (shouldSkip) { + return + } + const properties: InstallationBackfillSucceededEvent = {} + await publishEvent(Event.INSTALLATION_BACKFILL_SUCCEEDED, properties) +} + +export async function installationFailed(error: any) { + if (shouldSkip) { + return + } + const properties: InstallationBackfillFailedEvent = { + error: JSON.stringify(error, Object.getOwnPropertyNames(error)), + } + await publishEvent(Event.INSTALLATION_BACKFILL_FAILED, properties) +} diff --git a/packages/backend-core/src/events/publishers/index.ts b/packages/backend-core/src/events/publishers/index.ts index 99069aede8..48b2387c5a 100644 --- a/packages/backend-core/src/events/publishers/index.ts +++ b/packages/backend-core/src/events/publishers/index.ts @@ -16,3 +16,4 @@ export * as serve from "./serve" export * as user from "./user" export * as view from "./view" export * as version from "./version" +export * as backfill from "./backfill" diff --git a/packages/backend-core/src/events/publishers/query.ts b/packages/backend-core/src/events/publishers/query.ts index e659f5c2be..1a567bb051 100644 --- a/packages/backend-core/src/events/publishers/query.ts +++ b/packages/backend-core/src/events/publishers/query.ts @@ -8,6 +8,7 @@ import { QueryDeletedEvent, QueryImportedEvent, QueryPreviewedEvent, + QueriesRunEvent, } from "@budibase/types" /* eslint-disable */ @@ -40,6 +41,13 @@ export const imported = async ( await publishEvent(Event.QUERY_IMPORT, properties) } +export const run = async (count: number, timestamp?: string | number) => { + const properties: QueriesRunEvent = { + count, + } + await publishEvent(Event.QUERIES_RUN, properties, timestamp) +} + export const previewed = async (datasource: Datasource) => { const properties: QueryPreviewedEvent = {} await publishEvent(Event.QUERY_PREVIEWED, properties) diff --git a/packages/backend-core/src/installation.ts b/packages/backend-core/src/installation.ts index 25e4a311ed..372877ec36 100644 --- a/packages/backend-core/src/installation.ts +++ b/packages/backend-core/src/installation.ts @@ -5,10 +5,17 @@ import { doWithDB } from "./db" import { Installation, IdentityType } from "@budibase/types" import * as context from "./context" import semver from "semver" +import { bustCache, withCache, TTL, CacheKeys } from "./cache/generic" const pkg = require("../package.json") export const getInstall = async (): Promise => { + return withCache(CacheKeys.INSTALLATION, TTL.ONE_DAY, getInstallFromDB, { + useTenancy: false, + }) +} + +const getInstallFromDB = async (): Promise => { return doWithDB( StaticDatabases.PLATFORM_INFO.name, async (platformDb: any) => { @@ -44,6 +51,7 @@ const updateVersion = async (version: string): Promise => { const install = await getInstall() install.version = version await platformDb.put(install) + await bustCache(CacheKeys.INSTALLATION) }, {} ) diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index 2a40f89b8a..c1964d7ca7 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -1,135 +1,106 @@ -jest.mock("../../../events", () => { - return { - identification: { - identifyTenantGroup: jest.fn(), - identifyUser: jest.fn(), - }, - analytics: { - enabled: () => false, - }, - shutdown: () => {}, - account: { - created: jest.fn(), - deleted: jest.fn(), - verified: jest.fn(), - }, - app: { - created: jest.fn(), - updated: jest.fn(), - deleted: jest.fn(), - published: jest.fn(), - unpublished: jest.fn(), - templateImported: jest.fn(), - fileImported: jest.fn(), - versionUpdated: jest.fn(), - versionReverted: jest.fn(), - reverted: jest.fn(), - exported: jest.fn(), - }, - auth: { - login: jest.fn(), - logout: jest.fn(), - SSOCreated: jest.fn(), - SSOUpdated: jest.fn(), - SSOActivated: jest.fn(), - SSODeactivated: jest.fn(), - }, - automation: { - created: jest.fn(), - deleted: jest.fn(), - tested: jest.fn(), - // run: jest.fn(), - stepCreated: jest.fn(), - stepDeleted: jest.fn(), - triggerUpdated: jest.fn(), - }, - datasource: { - created: jest.fn(), - updated: jest.fn(), - deleted: jest.fn(), - }, - email: { - SMTPCreated: jest.fn(), - SMTPUpdated: jest.fn(), - }, - layout: { - created: jest.fn(), - deleted: jest.fn(), - }, - org: { - nameUpdated: jest.fn(), - logoUpdated: jest.fn(), - platformURLUpdated: jest.fn(), - analyticsOptOut: jest.fn(), - }, - version: { - checked: jest.fn(), - }, - query: { - created: jest.fn(), - updated: jest.fn(), - deleted: jest.fn(), - imported: jest.fn(), - previewed: jest.fn(), - }, - role: { - created: jest.fn(), - updated: jest.fn(), - deleted: jest.fn(), - assigned: jest.fn(), - unassigned: jest.fn(), - }, - rows: { - imported: jest.fn(), - created: jest.fn(), - }, - screen: { - created: jest.fn(), - deleted: jest.fn(), - }, - user: { - created: jest.fn(), - updated: jest.fn(), - deleted: jest.fn(), - permissionAdminAssigned: jest.fn(), - permissionAdminRemoved: jest.fn(), - permissionBuilderAssigned: jest.fn(), - permissionBuilderRemoved: jest.fn(), - invited: jest.fn(), - inviteAccepted: jest.fn(), - passwordForceReset: jest.fn(), - passwordUpdated: jest.fn(), - passwordResetRequested: jest.fn(), - passwordReset: jest.fn(), - }, - serve: { - servedBuilder: jest.fn(), - servedApp: jest.fn(), - servedAppPreview: jest.fn(), - }, - table: { - created: jest.fn(), - updated: jest.fn(), - deleted: jest.fn(), - exported: jest.fn(), - imported: jest.fn(), - permissionUpdated: jest.fn(), - }, - view: { - created: jest.fn(), - updated: jest.fn(), - deleted: jest.fn(), - exported: jest.fn(), - filterCreated: jest.fn(), - filterUpdated: jest.fn(), - filterDeleted: jest.fn(), - calculationCreated: jest.fn(), - calculationUpdated: jest.fn(), - calculationDeleted: jest.fn(), - }, - } -}) - const events = require("../../../events") +jest.spyOn(events.identification, "identifyTenantGroup") +jest.spyOn(events.identification, "identifyUser") + +jest.spyOn(events.account, "created") +jest.spyOn(events.account, "deleted") +jest.spyOn(events.account, "verified") + +jest.spyOn(events.app, "created") +jest.spyOn(events.app, "updated") +jest.spyOn(events.app, "deleted") +jest.spyOn(events.app, "published") +jest.spyOn(events.app, "unpublished") +jest.spyOn(events.app, "templateImported") +jest.spyOn(events.app, "fileImported") +jest.spyOn(events.app, "versionUpdated") +jest.spyOn(events.app, "versionReverted") +jest.spyOn(events.app, "reverted") +jest.spyOn(events.app, "exported") + +jest.spyOn(events.auth, "login") +jest.spyOn(events.auth, "logout") +jest.spyOn(events.auth, "SSOCreated") +jest.spyOn(events.auth, "SSOUpdated") +jest.spyOn(events.auth, "SSOActivated") +jest.spyOn(events.auth, "SSODeactivated") + +jest.spyOn(events.automation, "created") +jest.spyOn(events.automation, "deleted") +jest.spyOn(events.automation, "tested") +jest.spyOn(events.automation, "stepCreated") +jest.spyOn(events.automation, "stepDeleted") +jest.spyOn(events.automation, "triggerUpdated") + +jest.spyOn(events.datasource, "created") +jest.spyOn(events.datasource, "updated") +jest.spyOn(events.datasource, "deleted") + +jest.spyOn(events.email, "SMTPCreated") +jest.spyOn(events.email, "SMTPUpdated") + +jest.spyOn(events.layout, "created") +jest.spyOn(events.layout, "deleted") + +jest.spyOn(events.org, "nameUpdated") +jest.spyOn(events.org, "logoUpdated") +jest.spyOn(events.org, "platformURLUpdated") +jest.spyOn(events.org, "analyticsOptOut") + +jest.spyOn(events.version, "checked") + +jest.spyOn(events.query, "created") +jest.spyOn(events.query, "updated") +jest.spyOn(events.query, "deleted") +jest.spyOn(events.query, "imported") +jest.spyOn(events.query, "previewed") + +jest.spyOn(events.role, "created") +jest.spyOn(events.role, "updated") +jest.spyOn(events.role, "deleted") +jest.spyOn(events.role, "assigned") +jest.spyOn(events.role, "unassigned") + +jest.spyOn(events.rows, "imported") +jest.spyOn(events.rows, "created") + +jest.spyOn(events.screen, "created") +jest.spyOn(events.screen, "deleted") + +jest.spyOn(events.user, "created") +jest.spyOn(events.user, "updated") +jest.spyOn(events.user, "deleted") +jest.spyOn(events.user, "permissionAdminAssigned") +jest.spyOn(events.user, "permissionAdminRemoved") +jest.spyOn(events.user, "permissionBuilderAssigned") +jest.spyOn(events.user, "permissionBuilderRemoved") +jest.spyOn(events.user, "invited") +jest.spyOn(events.user, "inviteAccepted") +jest.spyOn(events.user, "passwordForceReset") +jest.spyOn(events.user, "passwordUpdated") +jest.spyOn(events.user, "passwordResetRequested") +jest.spyOn(events.user, "passwordReset") + +jest.spyOn(events.serve, "servedBuilder") +jest.spyOn(events.serve, "servedApp") +jest.spyOn(events.serve, "servedAppPreview") + +jest.spyOn(events.table, "created") +jest.spyOn(events.table, "updated") +jest.spyOn(events.table, "deleted") +jest.spyOn(events.table, "exported") +jest.spyOn(events.table, "imported") + +jest.spyOn(events.view, "created") +jest.spyOn(events.view, "updated") +jest.spyOn(events.view, "deleted") +jest.spyOn(events.view, "exported") +jest.spyOn(events.view, "filterCreated") +jest.spyOn(events.view, "filterUpdated") +jest.spyOn(events.view, "filterDeleted") +jest.spyOn(events.view, "calculationCreated") +jest.spyOn(events.view, "calculationUpdated") +jest.spyOn(events.view, "calculationDeleted") + module.exports = events diff --git a/packages/backend-core/tsconfig.build.json b/packages/backend-core/tsconfig.build.json index 602d4b3a7d..c18128c16e 100644 --- a/packages/backend-core/tsconfig.build.json +++ b/packages/backend-core/tsconfig.build.json @@ -1,6 +1,9 @@ { // Used for building with tsc "extends": "./tsconfig.json", + "references": [ + { "path": "../types" } + ], "exclude": [ "node_modules", "dist/**/*", diff --git a/packages/backend-core/yarn.lock b/packages/backend-core/yarn.lock index 606e978ab1..98c82da865 100644 --- a/packages/backend-core/yarn.lock +++ b/packages/backend-core/yarn.lock @@ -895,6 +895,13 @@ resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-1.8.2.tgz#7315b4c4c54f82d13fa61c228ec5c2ea5cc9e0e1" integrity sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w== +"@types/ioredis@^4.27.1": + version "4.28.10" + resolved "https://registry.yarnpkg.com/@types/ioredis/-/ioredis-4.28.10.tgz#40ceb157a4141088d1394bb87c98ed09a75a06ff" + integrity sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ== + dependencies: + "@types/node" "*" + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" @@ -1092,11 +1099,6 @@ acorn-walk@^7.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -acorn@^5.2.1: - version "5.7.4" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" - integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== - acorn@^7.1.1: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" @@ -1132,11 +1134,6 @@ ajv@^6.12.3: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -amdefine@>=0.0.4: - version "1.0.1" - resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" - integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= - ansi-align@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" @@ -1228,11 +1225,6 @@ assert-plus@^0.2.0: resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" integrity sha1-104bh+ev/A24qttwIfP+SBAasjQ= -ast-types@0.9.6: - version "0.9.6" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9" - integrity sha1-ECyenpAF0+fjgpvwxPok7oYu6bk= - async@~2.1.4: version "2.1.5" resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc" @@ -1361,11 +1353,6 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base62@^1.1.0: - version "1.2.8" - resolved "https://registry.yarnpkg.com/base62/-/base62-1.2.8.tgz#1264cb0fb848d875792877479dbe8bae6bae3428" - integrity sha512-V6YHUbjLxN1ymqNLb1DPHoU1CpfdL7d2YTIp5W3U4hhoG4hhxNmsFDs66M9EXxBiSEke5Bt5dwdfMwwZF70iLA== - base64-js@^1.0.2, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -1721,26 +1708,6 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@^2.5.0: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -commoner@^0.10.1: - version "0.10.8" - resolved "https://registry.yarnpkg.com/commoner/-/commoner-0.10.8.tgz#34fc3672cd24393e8bb47e70caa0293811f4f2c5" - integrity sha1-NPw2cs0kOT6LtH5wyqApOBH08sU= - dependencies: - commander "^2.5.0" - detective "^4.3.1" - glob "^5.0.15" - graceful-fs "^4.1.2" - iconv-lite "^0.4.5" - mkdirp "^0.5.0" - private "^0.1.6" - q "^1.1.2" - recast "^0.11.17" - component-type@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-type/-/component-type-1.2.1.tgz#8a47901700238e4fc32269771230226f24b415a9" @@ -1945,11 +1912,6 @@ deferred-leveldown@~5.3.0: abstract-leveldown "~6.2.1" inherits "^2.0.3" -defined@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" - integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= - delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -1990,14 +1952,6 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== -detective@^4.3.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/detective/-/detective-4.7.1.tgz#0eca7314338442febb6d65da54c10bb1c82b246e" - integrity sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig== - dependencies: - acorn "^5.2.1" - defined "^1.0.0" - diff-sequences@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" @@ -2134,15 +2088,6 @@ error-inject@^1.0.0: resolved "https://registry.yarnpkg.com/error-inject/-/error-inject-1.0.0.tgz#e2b3d91b54aed672f309d950d154850fa11d4f37" integrity sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc= -es3ify@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/es3ify/-/es3ify-0.2.2.tgz#5dae3e650e5be3684b88066513d528d092629862" - integrity sha1-Xa4+ZQ5b42hLiAZlE9Uo0JJimGI= - dependencies: - esprima "^2.7.1" - jstransform "~11.0.0" - through "~2.3.4" - escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -2180,26 +2125,11 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -esprima-fb@^15001.1.0-dev-harmony-fb: - version "15001.1.0-dev-harmony-fb" - resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1.0-dev-harmony-fb.tgz#30a947303c6b8d5e955bee2b99b1d233206a6901" - integrity sha512-59dDGQo2b3M/JfKIws0/z8dcXH2mnVHkfSPRhCYS91JNGfGNwr7GsSF6qzWZuOGvw5Ii0w9TtylrX07MGmlOoQ== - -esprima@^2.7.1: - version "2.7.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" - integrity sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A== - esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esprima@~3.1.0: - version "3.1.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" - integrity sha512-AWwVMNxwhN8+NIPQzAQZCm7RkLC4RbM3B1OobMuyp3i+w73X57KCKaVIxaRZb+DYCojq7rspo+fmuQfAboyhFg== - estraverse@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" @@ -2467,17 +2397,6 @@ glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" -glob@^5.0.15: - version "5.0.15" - resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" - integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" - glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" @@ -2716,7 +2635,7 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -iconv-lite@0.4.24, iconv-lite@^0.4.5: +iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -3550,17 +3469,6 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.3.6" -jstransform@~11.0.0: - version "11.0.3" - resolved "https://registry.yarnpkg.com/jstransform/-/jstransform-11.0.3.tgz#09a78993e0ae4d4ef4487f6155a91f6190cb4223" - integrity sha1-CaeJk+CuTU70SH9hVakfYZDLQiM= - dependencies: - base62 "^1.1.0" - commoner "^0.10.1" - esprima-fb "^15001.1.0-dev-harmony-fb" - object-assign "^2.0.0" - source-map "^0.4.2" - jwa@^1.1.4: version "1.1.5" resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.1.5.tgz#a0552ce0220742cd52e153774a32905c30e756e5" @@ -4069,13 +3977,6 @@ mimic-response@^1.0.0, mimic-response@^1.0.1: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== -"minimatch@2 || 3": - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -4083,7 +3984,7 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: +minimist@^1.2.0, minimist@^1.2.5: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== @@ -4108,13 +4009,6 @@ mkdirp-classic@^0.5.2: resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -mkdirp@^0.5.0: - version "0.5.6" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" - integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== - dependencies: - minimist "^1.2.6" - mkdirp@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" @@ -4295,11 +4189,6 @@ oauth@0.9.x, oauth@^0.9.15: resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1" integrity sha1-vR/vr2hslrdUda7VGWQS/2DPucE= -object-assign@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-2.1.1.tgz#43c36e5d569ff8e4816c4efa8be02d26967c18aa" - integrity sha1-Q8NuXVaf+OSBbE76i+AtJpZ8GKo= - object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -4624,17 +4513,6 @@ pouchdb-adapter-utils@7.2.2: pouchdb-merge "7.2.2" pouchdb-utils "7.2.2" -pouchdb-all-dbs@^1.0.2: - version "1.1.1" - resolved "https://registry.yarnpkg.com/pouchdb-all-dbs/-/pouchdb-all-dbs-1.1.1.tgz#85f04a39cafda52497ec49abf1c93bb5e72813f6" - integrity sha512-UUnsdmcnRSQ8MAOYSJjfTwKkQNb/6fvOfd/f7dNNivWZ2YDYVuMfgw1WQdL634yEtcXTxAENZ/EyLRdzPCB41A== - dependencies: - argsarray "0.0.1" - es3ify "^0.2.2" - inherits "~2.0.1" - pouchdb-promise "6.4.3" - tiny-queue "^0.2.0" - pouchdb-binary-utils@7.2.2: version "7.2.2" resolved "https://registry.yarnpkg.com/pouchdb-binary-utils/-/pouchdb-binary-utils-7.2.2.tgz#0690b348052c543b1e67f032f47092ca82bcb10e" @@ -4711,7 +4589,7 @@ pouchdb-merge@7.2.2: resolved "https://registry.yarnpkg.com/pouchdb-merge/-/pouchdb-merge-7.2.2.tgz#940d85a2b532d6a93a6cab4b250f5648511bcc16" integrity sha512-6yzKJfjIchBaS7Tusuk8280WJdESzFfQ0sb4jeMUNnrqs4Cx3b0DIEOYTRRD9EJDM+je7D3AZZ4AT0tFw8gb4A== -pouchdb-promise@6.4.3, pouchdb-promise@^6.0.4: +pouchdb-promise@^6.0.4: version "6.4.3" resolved "https://registry.yarnpkg.com/pouchdb-promise/-/pouchdb-promise-6.4.3.tgz#74516f4acf74957b54debd0fb2c0e5b5a68ca7b3" integrity sha512-ruJaSFXwzsxRHQfwNHjQfsj58LBOY1RzGzde4PM5CWINZwFjCQAhZwfMrch2o/0oZT6d+Xtt0HTWhq35p3b0qw== @@ -4798,11 +4676,6 @@ pretty-format@^27.0.0, pretty-format@^27.5.1: ansi-styles "^5.0.0" react-is "^17.0.1" -private@^0.1.6, private@~0.1.5: - version "0.1.8" - resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" - integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== - process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -4861,11 +4734,6 @@ pupa@^2.1.1: dependencies: escape-goat "^2.0.0" -q@^1.1.2: - version "1.5.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" - integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= - qs@~6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" @@ -4950,16 +4818,6 @@ readline-sync@^1.4.9: resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.10.tgz#41df7fbb4b6312d673011594145705bf56d8873b" integrity sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw== -recast@^0.11.17: - version "0.11.23" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.23.tgz#451fd3004ab1e4df9b4e4b66376b2a21912462d3" - integrity sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM= - dependencies: - ast-types "0.9.6" - esprima "~3.1.0" - private "~0.1.5" - source-map "~0.5.0" - redis-commands@1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.7.0.tgz#15a6fea2d58281e27b1cd1acfb4b293e278c3a89" @@ -5230,14 +5088,7 @@ source-map-support@^0.5.6: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.4.2: - version "0.4.4" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" - integrity sha1-66T12pwNyZneaAMti092FzZSA2s= - dependencies: - amdefine ">=0.0.4" - -source-map@^0.5.0, source-map@~0.5.0: +source-map@^0.5.0: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= @@ -5510,21 +5361,11 @@ through2@^2.0.0, through2@^2.0.2, through2@^2.0.3: readable-stream "~2.3.6" xtend "~4.0.1" -through@~2.3.4: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - timekeeper@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/timekeeper/-/timekeeper-2.2.0.tgz#9645731fce9e3280a18614a57a9d1b72af3ca368" integrity sha512-W3AmPTJWZkRwu+iSNxPIsLZ2ByADsOLbbLxe46UJyWj3mlYLlwucKiq+/dPm0l9wTzqoF3/2PH0AGFCebjq23A== -tiny-queue@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/tiny-queue/-/tiny-queue-0.2.1.tgz#25a67f2c6e253b2ca941977b5ef7442ef97a6046" - integrity sha1-JaZ/LG4lOyypQZd7XvdELvl6YEY= - tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" diff --git a/packages/server/scripts/jestSetup.js b/packages/server/scripts/jestSetup.js index ef5ab8e9b5..f0241a03ff 100644 --- a/packages/server/scripts/jestSetup.js +++ b/packages/server/scripts/jestSetup.js @@ -1,11 +1,5 @@ const { tmpdir } = require("os") const env = require("../src/environment") -const { mocks } = require("@budibase/backend-core/testUtils") - -// mock all dates to 2020-01-01T00:00:00.000Z -// use tk.reset() to use real dates in individual tests -const tk = require("timekeeper") -tk.freeze(mocks.date.MOCK_DATE) env._set("SELF_HOSTED", "1") env._set("NODE_ENV", "jest") @@ -15,4 +9,11 @@ env._set("BUDIBASE_DIR", tmpdir("budibase-unittests")) env._set("LOG_LEVEL", "silent") env._set("PORT", 0) +const { mocks } = require("@budibase/backend-core/testUtils") + +// mock all dates to 2020-01-01T00:00:00.000Z +// use tk.reset() to use real dates in individual tests +const tk = require("timekeeper") +tk.freeze(mocks.date.MOCK_DATE) + global.console.log = jest.fn() // console.log are ignored in tests diff --git a/packages/server/src/api/controllers/automation.js b/packages/server/src/api/controllers/automation.js index ddea76beb4..2469e132d3 100644 --- a/packages/server/src/api/controllers/automation.js +++ b/packages/server/src/api/controllers/automation.js @@ -71,9 +71,9 @@ exports.create = async function (ctx) { newAuto: automation, }) const response = await db.put(automation) - await events.automation.created() + await events.automation.created(automation) for (let step of automation.definition.steps) { - await events.automation.stepCreated(step) + await events.automation.stepCreated(automation, step) } automation._rev = response.rev diff --git a/packages/server/src/api/controllers/datasource.js b/packages/server/src/api/controllers/datasource.js index 63c51fb92c..e217dd2c47 100644 --- a/packages/server/src/api/controllers/datasource.js +++ b/packages/server/src/api/controllers/datasource.js @@ -144,7 +144,7 @@ exports.save = async function (ctx) { } const dbResp = await db.put(datasource) - await events.datasource.created() + await events.datasource.created(datasource) datasource._rev = dbResp.rev // Drain connection pools when configuration is changed diff --git a/packages/server/src/api/controllers/query/import/tests/index.spec.js b/packages/server/src/api/controllers/query/import/tests/index.spec.js index 0e9c5f7543..00d7daa43f 100644 --- a/packages/server/src/api/controllers/query/import/tests/index.spec.js +++ b/packages/server/src/api/controllers/query/import/tests/index.spec.js @@ -53,9 +53,11 @@ describe("Rest Importer", () => { } const runTest = async (test, assertions) => { - for (let [key, data] of Object.entries(datasets)) { - await test(key, data, assertions) - } + config.doInContext(config.appId, async () => { + for (let [key, data] of Object.entries(datasets)) { + await test(key, data, assertions) + } + }) } const testGetInfo = async (key, data, assertions) => { diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts index 63dc3f1a6f..149a2389bb 100644 --- a/packages/server/src/app.ts +++ b/packages/server/src/app.ts @@ -83,6 +83,22 @@ module.exports = server.listen(env.PORT || 0, async () => { eventEmitter.emitPort(env.PORT) fileSystem.init() await redis.init() + + // run migrations on startup if not done via http + // not recommended in a clustered environment + if (!env.HTTP_MIGRATIONS && !env.isTest()) { + try { + await migrations.migrate() + } catch (e) { + console.error("Error performing migrations. Exiting.\n", e) + shutdown() + } + } + + // check for version updates + await installation.checkInstallVersion() + + // done last - this will never complete await automations.init() }) @@ -99,15 +115,3 @@ process.on("uncaughtException", err => { process.on("SIGTERM", () => { shutdown() }) - -// run migrations on startup if not done via http -// not recommended in a clustered environment -if (!env.HTTP_MIGRATIONS && !env.isTest()) { - migrations.migrate().catch(err => { - console.error("Error performing migrations. Exiting.\n", err) - shutdown() - }) -} - -// check for version updates -installation.checkInstallVersion() diff --git a/packages/server/src/migrations/functions/backfill/app.ts b/packages/server/src/migrations/functions/backfill/app.ts index 60ff953aa0..83f99a1c16 100644 --- a/packages/server/src/migrations/functions/backfill/app.ts +++ b/packages/server/src/migrations/functions/backfill/app.ts @@ -6,8 +6,21 @@ import * as roles from "./app/roles" import * as tables from "./app/tables" import * as screens from "./app/screens" import * as global from "./global" -import { App } from "@budibase/types" +import { App, AppBackfillSucceededEvent } from "@budibase/types" import { db as dbUtils, events } from "@budibase/backend-core" +import env from "../../../environment" + +const failGraceful = env.SELF_HOSTED && !env.isDev() + +const handleError = (e: any, errors?: any) => { + if (failGraceful) { + if (errors) { + errors.push(e) + } + return + } + throw e +} /** * Date: @@ -18,28 +31,92 @@ import { db as dbUtils, events } from "@budibase/backend-core" */ export const run = async (appDb: any) => { - if (await global.isComplete()) { - // make sure new apps aren't backfilled - // return if the global migration for this tenant is complete - // which runs after the app migrations - return - } + try { + if (await global.isComplete()) { + // make sure new apps aren't backfilled + // return if the global migration for this tenant is complete + // which runs after the app migrations + return + } - const app: App = await appDb.get(dbUtils.DocumentTypes.APP_METADATA) - const timestamp = app.createdAt as string + const app: App = await appDb.get(dbUtils.DocumentTypes.APP_METADATA) + const timestamp = app.createdAt as string - if (dbUtils.isDevAppID(app.appId)) { - await events.app.created(app, timestamp) - await automations.backfill(appDb, timestamp) - await datasources.backfill(appDb, timestamp) - await layouts.backfill(appDb, timestamp) - await queries.backfill(appDb, timestamp) - await roles.backfill(appDb, timestamp) - await tables.backfill(appDb, timestamp) - await screens.backfill(appDb, timestamp) - } + if (dbUtils.isProdAppID(app.appId)) { + await events.app.published(app, timestamp) + } - if (dbUtils.isProdAppID(app.appId)) { - await events.app.published(app, timestamp) + const totals: any = {} + const errors: any = [] + + if (dbUtils.isDevAppID(app.appId)) { + await events.app.created(app, timestamp) + try { + totals.automations = await automations.backfill(appDb, timestamp) + } catch (e) { + handleError(e, errors) + } + + try { + totals.datasources = await datasources.backfill(appDb, timestamp) + } catch (e) { + handleError(e, errors) + } + + try { + totals.layouts = await layouts.backfill(appDb, timestamp) + } catch (e) { + handleError(e, errors) + } + + try { + totals.queries = await queries.backfill(appDb, timestamp) + } catch (e) { + handleError(e, errors) + } + + try { + totals.roles = await roles.backfill(appDb, timestamp) + } catch (e) { + handleError(e, errors) + } + + try { + totals.screens = await screens.backfill(appDb, timestamp) + } catch (e) { + handleError(e, errors) + } + + try { + totals.tables = await tables.backfill(appDb, timestamp) + } catch (e) { + handleError(e, errors) + } + } + + const properties: AppBackfillSucceededEvent = { + appId: app.appId, + automations: totals.automations, + datasources: totals.datasources, + layouts: totals.layouts, + queries: totals.queries, + roles: totals.roles, + tables: totals.tables, + screens: totals.screens, + } + + if (errors.length) { + properties.errors = errors.map((e: any) => + JSON.stringify(e, Object.getOwnPropertyNames(e)) + ) + properties.errorCount = errors.length + } else { + properties.errorCount = 0 + } + + await events.backfill.appSucceeded(properties) + } catch (e) { + handleError(e) + await events.backfill.appFailed(e) } } diff --git a/packages/server/src/migrations/functions/backfill/app/automations.ts b/packages/server/src/migrations/functions/backfill/app/automations.ts index f455e10fc8..e4c3ad19f3 100644 --- a/packages/server/src/migrations/functions/backfill/app/automations.ts +++ b/packages/server/src/migrations/functions/backfill/app/automations.ts @@ -21,4 +21,6 @@ export const backfill = async (appDb: any, timestamp: string) => { await events.automation.stepCreated(automation, step, timestamp) } } + + return automations.length } diff --git a/packages/server/src/migrations/functions/backfill/app/datasources.ts b/packages/server/src/migrations/functions/backfill/app/datasources.ts index 1919231a88..1245e5d745 100644 --- a/packages/server/src/migrations/functions/backfill/app/datasources.ts +++ b/packages/server/src/migrations/functions/backfill/app/datasources.ts @@ -17,4 +17,6 @@ export const backfill = async (appDb: any, timestamp: string) => { for (const datasource of datasources) { await events.datasource.created(datasource, timestamp) } + + return datasources.length } diff --git a/packages/server/src/migrations/functions/backfill/app/layouts.ts b/packages/server/src/migrations/functions/backfill/app/layouts.ts index a02eeb7123..ec36d59b30 100644 --- a/packages/server/src/migrations/functions/backfill/app/layouts.ts +++ b/packages/server/src/migrations/functions/backfill/app/layouts.ts @@ -17,4 +17,6 @@ export const backfill = async (appDb: any, timestamp: string) => { for (const layout of layouts) { await events.layout.created(layout, timestamp) } + + return layouts.length } diff --git a/packages/server/src/migrations/functions/backfill/app/queries.ts b/packages/server/src/migrations/functions/backfill/app/queries.ts index 9e83e6a371..fb0e561363 100644 --- a/packages/server/src/migrations/functions/backfill/app/queries.ts +++ b/packages/server/src/migrations/functions/backfill/app/queries.ts @@ -28,4 +28,6 @@ export const backfill = async (appDb: any, timestamp: string) => { ) await events.query.created(datasource, query, timestamp) } + + return queries.length } diff --git a/packages/server/src/migrations/functions/backfill/app/roles.ts b/packages/server/src/migrations/functions/backfill/app/roles.ts index 4fa57b4165..1c90b0f658 100644 --- a/packages/server/src/migrations/functions/backfill/app/roles.ts +++ b/packages/server/src/migrations/functions/backfill/app/roles.ts @@ -17,4 +17,6 @@ export const backfill = async (appDb: any, timestamp: string) => { for (const role of roles) { await events.role.created(role, timestamp) } + + return roles.length } diff --git a/packages/server/src/migrations/functions/backfill/app/screens.ts b/packages/server/src/migrations/functions/backfill/app/screens.ts index b1d9791c2d..a17be76ea3 100644 --- a/packages/server/src/migrations/functions/backfill/app/screens.ts +++ b/packages/server/src/migrations/functions/backfill/app/screens.ts @@ -17,4 +17,6 @@ export const backfill = async (appDb: any, timestamp: string) => { for (const screen of screens) { await events.screen.created(screen, timestamp) } + + return screens.length } diff --git a/packages/server/src/migrations/functions/backfill/app/tables.ts b/packages/server/src/migrations/functions/backfill/app/tables.ts index 23d0dff702..ea41cd896a 100644 --- a/packages/server/src/migrations/functions/backfill/app/tables.ts +++ b/packages/server/src/migrations/functions/backfill/app/tables.ts @@ -31,4 +31,6 @@ export const backfill = async (appDb: any, timestamp: string) => { } } } + + return tables.length } diff --git a/packages/server/src/migrations/functions/backfill/global.ts b/packages/server/src/migrations/functions/backfill/global.ts index a03ae67f41..7031b0f61f 100644 --- a/packages/server/src/migrations/functions/backfill/global.ts +++ b/packages/server/src/migrations/functions/backfill/global.ts @@ -1,10 +1,62 @@ +import { TenantBackfillSucceededEvent } from "./../../../../../types/src/events/backfill" import * as users from "./global/users" -import * as rows from "./global/rows" import * as configs from "./global/configs" -import { tenancy, events, migrations, accounts } from "@budibase/backend-core" -import { CloudAccount } from "@budibase/types" +import * as quotas from "./global/quotas" +import { + tenancy, + events, + migrations, + accounts, + db as dbUtils, +} from "@budibase/backend-core" +import { QuotaUsage } from "@budibase/pro" +import { CloudAccount, App } from "@budibase/types" import env from "../../../environment" +const failGraceful = env.SELF_HOSTED && !env.isDev() + +const handleError = (e: any, errors?: any) => { + if (failGraceful) { + if (errors) { + errors.push(e) + } + return + } + throw e +} + +const formatUsage = (usage: QuotaUsage) => { + let maxAutomations = 0 + let maxQueries = 0 + let rows = 0 + let developers = 0 + + if (usage) { + if (usage.usageQuota) { + rows = usage.usageQuota.rows + developers = usage.usageQuota.developers + } + + if (usage.monthly) { + for (const value of Object.values(usage.monthly)) { + if (value.automations > maxAutomations) { + maxAutomations = value.automations + } + if (value.queries > maxQueries) { + maxQueries = value.queries + } + } + } + } + + return { + maxAutomations, + maxQueries, + rows, + developers, + } +} + /** * Date: * May 2022 @@ -14,23 +66,75 @@ import env from "../../../environment" */ export const run = async (db: any) => { - const tenantId = tenancy.getTenantId() - const installTimestamp = (await getInstallTimestamp(db)) as number - let account: CloudAccount | undefined - if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { - account = await accounts.getAccountByTenantId(tenantId) + try { + const tenantId = tenancy.getTenantId() + let installTimestamp + + const totals: any = {} + const errors: any = [] + + try { + installTimestamp = await getInstallTimestamp(db) + } catch (e) { + handleError(e, errors) + } + + let account: CloudAccount | undefined + if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { + account = await accounts.getAccountByTenantId(tenantId) + } + + try { + await events.identification.identifyTenantGroup( + tenantId, + account, + installTimestamp + ) + } catch (e) { + handleError(e, errors) + } + + try { + await configs.backfill(db, installTimestamp) + } catch (e) { + handleError(e, errors) + } + + try { + totals.users = await users.backfill(db, account) + } catch (e) { + handleError(e, errors) + } + + try { + const allApps: App[] = await dbUtils.getAllApps({ dev: true }) + totals.apps = allApps.length + + totals.usage = await quotas.backfill(allApps) + } catch (e) { + handleError(e, errors) + } + + const properties: TenantBackfillSucceededEvent = { + apps: totals.apps, + users: totals.users, + ...formatUsage(totals.usage), + usage: totals.usage, + } + + if (errors.length) { + properties.errors = errors.map((e: any) => + JSON.stringify(e, Object.getOwnPropertyNames(e)) + ) + properties.errorCount = errors.length + } else { + properties.errorCount = 0 + } + await events.backfill.tenantSucceeded(properties) + } catch (e) { + handleError(e) + await events.backfill.tenantFailed(e) } - - await events.identification.identifyTenantGroup( - tenantId, - account, - installTimestamp - ) - await configs.backfill(db, installTimestamp) - - // users and rows provide their own timestamp - await users.backfill(db, account) - await rows.backfill() } export const isComplete = async (): Promise => { diff --git a/packages/server/src/migrations/functions/backfill/global/quotas.ts b/packages/server/src/migrations/functions/backfill/global/quotas.ts new file mode 100644 index 0000000000..274804adad --- /dev/null +++ b/packages/server/src/migrations/functions/backfill/global/quotas.ts @@ -0,0 +1,53 @@ +import { events } from "@budibase/backend-core" +import { quotas } from "@budibase/pro" +import { App } from "@budibase/types" + +const getOldestCreatedAt = (allApps: App[]): string | undefined => { + const timestamps = allApps + .filter(app => !!app.createdAt) + .map(app => app.createdAt as string) + .sort((a, b) => new Date(a).getTime() - new Date(b).getTime()) + + if (timestamps.length) { + return timestamps[0] + } +} + +const getMonthTimestamp = (monthString: string): number => { + const parts = monthString.split("-") + const month = parseInt(parts[0]) - 1 // we already do +1 in month string calculation + const year = parseInt(parts[1]) + + // using 0 as the day in next month gives us last day in previous month + const date = new Date(year, month + 1, 0).getTime() + const now = new Date().getTime() + + if (date > now) { + return now + } else { + return date + } +} + +export const backfill = async (allApps: App[]) => { + const usage = await quotas.getQuotaUsage() + + const rows = usage.usageQuota.rows + const rowsTimestamp = getOldestCreatedAt(allApps) + await events.rows.created(rows, rowsTimestamp) + + for (const [monthString, quotas] of Object.entries(usage.monthly)) { + if (monthString === "current") { + continue + } + const monthTimestamp = getMonthTimestamp(monthString) + + const queries = quotas.queries + await events.query.run(queries, monthTimestamp) + + const automations = quotas.automations + await events.automation.run(automations, monthTimestamp) + } + + return usage +} diff --git a/packages/server/src/migrations/functions/backfill/global/rows.ts b/packages/server/src/migrations/functions/backfill/global/rows.ts deleted file mode 100644 index b336668d64..0000000000 --- a/packages/server/src/migrations/functions/backfill/global/rows.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { events, db as dbUtils } from "@budibase/backend-core" -import { Row, App } from "@budibase/types" -import { getUniqueRows } from "../../../../utilities/usageQuota/rows" - -// Rows is a special circumstance where we get rows across all apps -// therefore migration is performed at the global level - -const getOldestCreatedAt = (allApps: App[]): string | undefined => { - const timestamps = allApps - .filter(app => !!app.createdAt) - .map(app => app.createdAt as string) - .sort((a, b) => new Date(a).getTime() - new Date(b).getTime()) - - if (timestamps.length) { - return timestamps[0] - } -} - -export const backfill = async () => { - const allApps: App[] = await dbUtils.getAllApps({ dev: true }) - const timestamp = getOldestCreatedAt(allApps) - const appIds = allApps ? allApps.map((app: { appId: any }) => app.appId) : [] - const rows: Row[] = await getUniqueRows(appIds) - const rowCount = rows ? rows.length : 0 - if (rowCount) { - await events.rows.created(rowCount, timestamp) - } -} diff --git a/packages/server/src/migrations/functions/backfill/global/users.ts b/packages/server/src/migrations/functions/backfill/global/users.ts index 5ffbfd056c..0a59bec630 100644 --- a/packages/server/src/migrations/functions/backfill/global/users.ts +++ b/packages/server/src/migrations/functions/backfill/global/users.ts @@ -40,4 +40,6 @@ export const backfill = async ( } } } + + return users.length } diff --git a/packages/server/src/migrations/functions/backfill/installation.ts b/packages/server/src/migrations/functions/backfill/installation.ts index b807d650e8..425ec80f0d 100644 --- a/packages/server/src/migrations/functions/backfill/installation.ts +++ b/packages/server/src/migrations/functions/backfill/installation.ts @@ -1,6 +1,19 @@ import { events, tenancy, installation } from "@budibase/backend-core" import { Installation } from "@budibase/types" import * as global from "./global" +import env from "../../../environment" + +const failGraceful = env.SELF_HOSTED + +const handleError = (e: any, errors?: any) => { + if (failGraceful) { + if (errors) { + errors.push(e) + } + return + } + throw e +} /** * Date: @@ -11,14 +24,21 @@ import * as global from "./global" */ export const run = async () => { - // need to use the default tenant to try to get the installation time - await tenancy.doInTenant(tenancy.DEFAULT_TENANT_ID, async () => { - const db = tenancy.getGlobalDB() - const installTimestamp = (await global.getInstallTimestamp(db)) as number - const install: Installation = await installation.getInstall() - await events.identification.identifyInstallationGroup( - install.installId, - installTimestamp - ) - }) + try { + // need to use the default tenant to try to get the installation time + await tenancy.doInTenant(tenancy.DEFAULT_TENANT_ID, async () => { + const db = tenancy.getGlobalDB() + const installTimestamp = (await global.getInstallTimestamp(db)) as number + const install: Installation = await installation.getInstall() + await events.identification.identifyInstallationGroup( + install.installId, + installTimestamp + ) + }) + await events.backfill.installationSucceeded() + throw new Error("fail") + } catch (e) { + handleError(e) + await events.backfill.installationFailed(e) + } } diff --git a/packages/types/src/documents/global/config.ts b/packages/types/src/documents/global/config.ts index 9ad80e4eef..f62f0a12a8 100644 --- a/packages/types/src/documents/global/config.ts +++ b/packages/types/src/documents/global/config.ts @@ -19,7 +19,7 @@ export interface SettingsConfig extends Config { company: string logoUrl: string platformUrl: string - globalId?: string + uniqueTenantId?: string } } diff --git a/packages/types/src/events/automation.ts b/packages/types/src/events/automation.ts index 4bd40bee92..108894cec6 100644 --- a/packages/types/src/events/automation.ts +++ b/packages/types/src/events/automation.ts @@ -43,3 +43,7 @@ export interface AutomationStepDeletedEvent { stepId: string stepType: string } + +export interface AutomationsRunEvent { + count: number +} diff --git a/packages/types/src/events/backfill.ts b/packages/types/src/events/backfill.ts new file mode 100644 index 0000000000..35e921dece --- /dev/null +++ b/packages/types/src/events/backfill.ts @@ -0,0 +1,35 @@ +export interface AppBackfillSucceededEvent { + appId: string + automations: number + datasources: number + layouts: number + queries: number + roles: number + tables: number + screens: number + errors?: string[] + errorCount?: number +} + +export interface AppBackfillFailedEvent { + error: string +} + +export interface TenantBackfillSucceededEvent { + apps: number + users: number + + usage: any + errors?: [string] + errorCount?: number +} + +export interface TenantBackfillFailedEvent { + error: string +} + +export interface InstallationBackfillSucceededEvent {} + +export interface InstallationBackfillFailedEvent { + error: string +} diff --git a/packages/types/src/events/event.ts b/packages/types/src/events/event.ts index d5528ea114..9c8e10b9ad 100644 --- a/packages/types/src/events/event.ts +++ b/packages/types/src/events/event.ts @@ -81,7 +81,7 @@ export enum Event { QUERY_UPDATED = "query:updated", QUERY_DELETED = "query:deleted", QUERY_IMPORT = "query:import", - // QUERY_RUN = "query:run", + QUERIES_RUN = "queries:run", QUERY_PREVIEWED = "query:previewed", // TABLE @@ -124,7 +124,7 @@ export enum Event { AUTOMATION_CREATED = "automation:created", AUTOMATION_DELETED = "automation:deleted", AUTOMATION_TESTED = "automation:tested", - // AUTOMATION_RUN = "automation:run", + AUTOMATIONS_RUN = "automations:run", AUTOMATION_STEP_CREATED = "automation:step:created", AUTOMATION_STEP_DELETED = "automation:step:deleted", AUTOMATION_TRIGGER_UPDATED = "automation:trigger:updated", @@ -140,6 +140,14 @@ export enum Event { ACCOUNT_CREATED = "account:created", ACCOUNT_DELETED = "account:deleted", ACCOUNT_VERIFIED = "account:verified", + + // BACKFILL + APP_BACKFILL_SUCCEEDED = "app:backfill:succeeded", + APP_BACKFILL_FAILED = "app:backfill:failed", + TENANT_BACKFILL_SUCCEEDED = "tenant:backfill:succeeded", + TENANT_BACKFILL_FAILED = "tenant:backfill:failed", + INSTALLATION_BACKFILL_SUCCEEDED = "installation:backfill:succeeded", + INSTALLATION_BACKFILL_FAILED = "installation:backfill:failed", } export type RowImportFormat = "csv" diff --git a/packages/types/src/events/index.ts b/packages/types/src/events/index.ts index 44f6f297a6..d87558332a 100644 --- a/packages/types/src/events/index.ts +++ b/packages/types/src/events/index.ts @@ -16,3 +16,4 @@ export * from "./table" export * from "./user" export * from "./view" export * from "./account" +export * from "./backfill" diff --git a/packages/types/src/events/query.ts b/packages/types/src/events/query.ts index 4a89c38f79..864298e7e2 100644 --- a/packages/types/src/events/query.ts +++ b/packages/types/src/events/query.ts @@ -7,3 +7,7 @@ export interface QueryDeletedEvent {} export interface QueryImportedEvent {} export interface QueryPreviewedEvent {} + +export interface QueriesRunEvent { + count: number +} diff --git a/packages/worker/src/api/controllers/global/configs.js b/packages/worker/src/api/controllers/global/configs.js index 5ce3fd0bac..227b86d181 100644 --- a/packages/worker/src/api/controllers/global/configs.js +++ b/packages/worker/src/api/controllers/global/configs.js @@ -167,6 +167,7 @@ exports.save = async function (ctx) { try { const response = await db.put(ctx.request.body) await bustCache(CacheKeys.CHECKLIST) + await bustCache(CacheKeys.ANALYTICS_ENABLED) for (const fn of eventFns) { await fn() From 7ebfaf8a3133c08165f32e88435d21f7ef2f69ac Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 31 May 2022 21:04:41 +0100 Subject: [PATCH 51/75] Failover protection with event cache --- packages/backend-core/src/cache/generic.js | 42 +++- .../backend-core/src/db/tests/index.spec.js | 2 +- packages/backend-core/src/events/backfill.ts | 183 ++++++++++++++++++ packages/backend-core/src/events/events.ts | 20 +- packages/backend-core/src/events/index.ts | 5 +- .../src/events/processors/PosthogProcessor.ts | 14 +- .../src/events/publishers/email.ts | 14 +- .../src/events/publishers/license.ts | 10 - .../src/events/publishers/query.ts | 37 +++- .../src/events/publishers/role.ts | 34 +++- .../src/events/publishers/rows.ts | 6 +- .../src/events/publishers/screen.ts | 12 +- .../src/events/publishers/serve.ts | 3 - .../src/events/publishers/table.ts | 24 ++- .../src/events/publishers/user.ts | 52 +++-- .../src/events/publishers/view.ts | 61 ++++-- packages/backend-core/src/redis/index.js | 4 + .../src/tests/utilities/mocks/events.js | 9 +- .../src/tests/utilities/mocks/index.js | 3 +- packages/backend-core/src/tests/utils.spec.js | 10 +- .../server/src/api/controllers/analytics.ts | 3 +- .../server/src/api/controllers/automation.js | 10 +- .../server/src/api/controllers/datasource.js | 12 +- packages/server/src/api/controllers/dev.js | 2 +- .../query/import/tests/index.spec.js | 2 +- .../server/src/api/controllers/query/index.ts | 6 +- .../server/src/api/controllers/view/index.js | 18 +- .../server/src/api/routes/tests/query.spec.js | 17 +- .../src/migrations/functions/backfill/app.ts | 24 ++- .../functions/backfill/app/layouts.ts | 7 + .../functions/backfill/app/tables.ts | 4 +- .../migrations/functions/backfill/global.ts | 31 ++- .../functions/backfill/global/configs.ts | 2 +- .../server/src/migrations/tests/helpers.ts | 12 +- .../server/src/migrations/tests/index.spec.ts | 19 +- .../src/tests/utilities/TestConfiguration.js | 1 - .../server/src/tests/utilities/mocks/core.js | 3 - .../server/src/tests/utilities/mocks/index.js | 1 - packages/types/src/documents/app/role.ts | 5 +- packages/types/src/documents/app/screen.ts | 8 +- packages/types/src/events/account.ts | 8 +- packages/types/src/events/app.ts | 24 +-- packages/types/src/events/auth.ts | 14 +- packages/types/src/events/automation.ts | 16 +- packages/types/src/events/backfill.ts | 23 ++- packages/types/src/events/config.ts | 3 - packages/types/src/events/datasource.ts | 8 +- packages/types/src/events/email.ts | 5 + packages/types/src/events/event.ts | 12 +- packages/types/src/events/index.ts | 2 +- packages/types/src/events/layout.ts | 6 +- packages/types/src/events/license.ts | 2 - packages/types/src/events/query.ts | 39 +++- packages/types/src/events/role.ts | 30 ++- packages/types/src/events/rows.ts | 10 +- packages/types/src/events/screen.ts | 14 +- packages/types/src/events/serve.ts | 10 +- packages/types/src/events/table.ts | 24 ++- packages/types/src/events/user.ts | 44 +++-- packages/types/src/events/version.ts | 6 +- packages/types/src/events/view.ts | 47 ++++- packages/worker/scripts/jestSetup.js | 13 +- .../src/api/controllers/global/users.ts | 2 +- .../worker/src/api/routes/tests/self.spec.js | 2 +- .../worker/src/api/routes/tests/users.spec.js | 1 - packages/worker/src/tests/mocks/core.js | 3 - packages/worker/src/tests/mocks/index.js | 1 - 67 files changed, 842 insertions(+), 259 deletions(-) create mode 100644 packages/backend-core/src/events/backfill.ts delete mode 100644 packages/server/src/tests/utilities/mocks/core.js delete mode 100644 packages/server/src/tests/utilities/mocks/index.js delete mode 100644 packages/types/src/events/config.ts create mode 100644 packages/types/src/events/email.ts delete mode 100644 packages/worker/src/tests/mocks/core.js diff --git a/packages/backend-core/src/cache/generic.js b/packages/backend-core/src/cache/generic.js index 9ddde7f99a..8f0ef78954 100644 --- a/packages/backend-core/src/cache/generic.js +++ b/packages/backend-core/src/cache/generic.js @@ -1,5 +1,4 @@ const redis = require("../redis/authRedis") -const env = require("../environment") const { getTenantId } = require("../context") exports.CacheKeys = { @@ -7,6 +6,8 @@ exports.CacheKeys = { INSTALLATION: "installation", ANALYTICS_ENABLED: "analyticsEnabled", UNIQUE_TENANT_ID: "uniqueTenantId", + EVENTS: "events", + BACKFILL_METADATA: "backfillMetadata", } exports.TTL = { @@ -20,10 +21,41 @@ function generateTenantKey(key) { return `${key}:${tenantId}` } -exports.withCache = async (key, ttl, fetchFn, opts = { useTenancy: true }) => { +exports.keys = async pattern => { + const client = await redis.getCacheClient() + return client.keys(pattern) +} + +/** + * Read only from the cache. + */ +exports.get = async (key, opts = { useTenancy: true }) => { key = opts.useTenancy ? generateTenantKey(key) : key const client = await redis.getCacheClient() - const cachedValue = await client.get(key) + const value = await client.get(key) + return value +} + +/** + * Write to the cache. + */ +exports.store = async (key, value, ttl, opts = { useTenancy: true }) => { + key = opts.useTenancy ? generateTenantKey(key) : key + const client = await redis.getCacheClient() + await client.store(key, value, ttl) +} + +exports.delete = async (key, opts = { useTenancy: true }) => { + key = opts.useTenancy ? generateTenantKey(key) : key + const client = await redis.getCacheClient() + return client.delete(key) +} + +/** + * Read from the cache. Write to the cache if not exists. + */ +exports.withCache = async (key, ttl, fetchFn, opts = { useTenancy: true }) => { + const cachedValue = await exports.get(key, opts) if (cachedValue) { return cachedValue } @@ -31,9 +63,7 @@ exports.withCache = async (key, ttl, fetchFn, opts = { useTenancy: true }) => { try { const fetchedValue = await fetchFn() - if (!env.isTest()) { - await client.store(key, fetchedValue, ttl) - } + await exports.store(key, fetchedValue, ttl, opts) return fetchedValue } catch (err) { console.error("Error fetching before cache - ", err) diff --git a/packages/backend-core/src/db/tests/index.spec.js b/packages/backend-core/src/db/tests/index.spec.js index 0ede666197..32b8f02c36 100644 --- a/packages/backend-core/src/db/tests/index.spec.js +++ b/packages/backend-core/src/db/tests/index.spec.js @@ -1,5 +1,5 @@ require("../../tests/utilities/TestConfiguration") -const { dangerousGetDB, allDbs } = require("../") +const { dangerousGetDB } = require("../") describe("db", () => { diff --git a/packages/backend-core/src/events/backfill.ts b/packages/backend-core/src/events/backfill.ts new file mode 100644 index 0000000000..b4b7d826bf --- /dev/null +++ b/packages/backend-core/src/events/backfill.ts @@ -0,0 +1,183 @@ +import { UserPermissionAssignedEvent } from "./../../../types/src/events/user" +import { + Event, + BackfillMetadata, + CachedEvent, + SSOCreatedEvent, + AutomationCreatedEvent, + AutomationStepCreatedEvent, + DatasourceCreatedEvent, + LayoutCreatedEvent, + QueryCreatedEvent, + RoleCreatedEvent, + ScreenCreatedEvent, + TableCreatedEvent, + ViewCreatedEvent, + ViewCalculationCreatedEvent, + ViewFilterCreatedEvent, + AppPublishedEvent, + UserCreatedEvent, + RoleAssignedEvent, + RowsCreatedEvent, +} from "@budibase/types" +import * as context from "../context" +import { CacheKeys } from "../cache/generic" +import * as cache from "../cache/generic" + +// LIFECYCLE + +export const start = async (events: Event[]) => { + const metadata: BackfillMetadata = { + eventWhitelist: events, + } + return saveBackfillMetadata(metadata) +} + +export const recordEvent = async (event: Event, properties: any) => { + const eventKey = getEventKey(event, properties) + // don't use a ttl - cleaned up by migration + // don't use tenancy - already in the key + await cache.store(eventKey, properties, undefined, { useTenancy: false }) +} + +export const end = async () => { + await deleteBackfillMetadata() + await clearEvents() +} + +// CRUD + +const getBackfillMetadata = async (): Promise => { + return cache.get(CacheKeys.BACKFILL_METADATA) +} + +const saveBackfillMetadata = async ( + backfill: BackfillMetadata +): Promise => { + // no TTL - deleted by backfill + return cache.store(CacheKeys.BACKFILL_METADATA, backfill) +} + +const deleteBackfillMetadata = async (): Promise => { + await cache.delete(CacheKeys.BACKFILL_METADATA) +} + +const clearEvents = async () => { + // wildcard + const pattern = getEventKey() + const keys = await cache.keys(pattern) + + for (const key of keys) { + // delete each key + // don't use tenancy, already in the key + await cache.delete(key, { useTenancy: false }) + } +} + +// HELPERS + +export const isBackfillingEvent = async (event: Event) => { + const backfill = await getBackfillMetadata() + const events = backfill?.eventWhitelist + if (events && events.includes(event)) { + return true + } else { + return false + } +} + +export const isAlreadySent = async (event: Event, properties: any) => { + const eventKey = getEventKey(event, properties) + const cachedEvent: CachedEvent = await cache.get(eventKey, { + useTenancy: false, + }) + return !!cachedEvent +} + +const CUSTOM_PROPERTY_SUFFIX: any = { + // APP EVENTS + [Event.AUTOMATION_CREATED]: (properties: AutomationCreatedEvent) => { + return properties.automationId + }, + [Event.AUTOMATION_STEP_CREATED]: (properties: AutomationStepCreatedEvent) => { + return properties.stepId + }, + [Event.DATASOURCE_CREATED]: (properties: DatasourceCreatedEvent) => { + return properties.datasourceId + }, + [Event.LAYOUT_CREATED]: (properties: LayoutCreatedEvent) => { + return properties.layoutId + }, + [Event.QUERY_CREATED]: (properties: QueryCreatedEvent) => { + return properties.queryId + }, + [Event.ROLE_CREATED]: (properties: RoleCreatedEvent) => { + return properties.roleId + }, + [Event.SCREEN_CREATED]: (properties: ScreenCreatedEvent) => { + return properties.screenId + }, + [Event.TABLE_CREATED]: (properties: TableCreatedEvent) => { + return properties.tableId + }, + [Event.VIEW_CREATED]: (properties: ViewCreatedEvent) => { + return properties.tableId // best uniqueness + }, + [Event.VIEW_CALCULATION_CREATED]: ( + properties: ViewCalculationCreatedEvent + ) => { + return properties.tableId // best uniqueness + }, + [Event.VIEW_FILTER_CREATED]: (properties: ViewFilterCreatedEvent) => { + return properties.tableId // best uniqueness + }, + [Event.APP_PUBLISHED]: (properties: AppPublishedEvent) => { + return properties.appId // best uniqueness + }, + [Event.APP_PUBLISHED]: (properties: AppPublishedEvent) => { + return properties.appId // best uniqueness + }, + // GLOBAL EVENTS + [Event.AUTH_SSO_CREATED]: (properties: SSOCreatedEvent) => { + return properties.type + }, + [Event.AUTH_SSO_ACTIVATED]: (properties: SSOCreatedEvent) => { + return properties.type + }, + [Event.USER_CREATED]: (properties: UserCreatedEvent) => { + return properties.userId + }, + [Event.USER_PERMISSION_ADMIN_ASSIGNED]: ( + properties: UserPermissionAssignedEvent + ) => { + return properties.userId + }, + [Event.USER_PERMISSION_BUILDER_ASSIGNED]: ( + properties: UserPermissionAssignedEvent + ) => { + return properties.userId + }, + [Event.ROLE_ASSIGNED]: (properties: RoleAssignedEvent) => { + return `${properties.roleId}-${properties.userId}` + }, +} + +const getEventKey = (event?: Event, properties?: any) => { + let eventKey: string + + const tenantId = context.getTenantId() + if (event) { + eventKey = `${CacheKeys.EVENTS}:${tenantId}:${event}` + + // use some properties to make the key more unique + const custom = CUSTOM_PROPERTY_SUFFIX[event] + const suffix = custom ? custom(properties) : undefined + if (suffix) { + eventKey = `${event}:${suffix}` + } + } else { + eventKey = `${CacheKeys.EVENTS}:${tenantId}:*` + } + + return eventKey +} diff --git a/packages/backend-core/src/events/events.ts b/packages/backend-core/src/events/events.ts index 8d81608365..cda90d12c9 100644 --- a/packages/backend-core/src/events/events.ts +++ b/packages/backend-core/src/events/events.ts @@ -1,6 +1,7 @@ import { Event } from "@budibase/types" import { processors } from "./processors" import * as identification from "./identification" +import * as backfill from "./backfill" export const publishEvent = async ( event: Event, @@ -9,5 +10,22 @@ export const publishEvent = async ( ) => { // in future this should use async events via a distributed queue. const identity = await identification.getCurrentIdentity() - await processors.processEvent(event, identity, properties, timestamp) + + const backfilling = await backfill.isBackfillingEvent(event) + // no backfill - send the event and exit + if (!backfilling) { + await processors.processEvent(event, identity, properties, timestamp) + return + } + + // backfill active - check if the event has been sent already + const alreadySent = await backfill.isAlreadySent(event, properties) + if (alreadySent) { + // do nothing + return + } else { + // send and record the event + await processors.processEvent(event, identity, properties, timestamp) + await backfill.recordEvent(event, properties) + } } diff --git a/packages/backend-core/src/events/index.ts b/packages/backend-core/src/events/index.ts index ae5b66023d..814399655d 100644 --- a/packages/backend-core/src/events/index.ts +++ b/packages/backend-core/src/events/index.ts @@ -1,7 +1,10 @@ -import { processors } from "./processors" export * from "./publishers" +export * as processors from "./processors" export * as analytics from "./analytics" export * as identification from "./identification" +export * as backfillCache from "./backfill" + +import { processors } from "./processors" export const shutdown = () => { processors.shutdown() diff --git a/packages/backend-core/src/events/processors/PosthogProcessor.ts b/packages/backend-core/src/events/processors/PosthogProcessor.ts index 4c1b4b1195..efe4ed26c2 100644 --- a/packages/backend-core/src/events/processors/PosthogProcessor.ts +++ b/packages/backend-core/src/events/processors/PosthogProcessor.ts @@ -1,7 +1,8 @@ import PostHog from "posthog-node" -import { Event, Identity, Group } from "@budibase/types" +import { Event, Identity, Group, BaseEvent } from "@budibase/types" import { EventProcessor } from "./types" import env from "../../environment" +import context from "../../context" const pkg = require("../../../package.json") export default class PosthogProcessor implements EventProcessor { @@ -17,12 +18,19 @@ export default class PosthogProcessor implements EventProcessor { async processEvent( event: Event, identity: Identity, - properties: any, + properties: BaseEvent, timestamp?: string | number ): Promise { properties.version = pkg.version properties.service = env.SERVICE + + const appId = context.getAppId() + if (appId) { + properties.appId = appId + } + const payload: any = { distinctId: identity.id, event, properties } + if (timestamp) { payload.timestamp = new Date(timestamp) } @@ -32,9 +40,11 @@ export default class PosthogProcessor implements EventProcessor { payload.groups = {} if (identity.installationId) { payload.groups.installation = identity.installationId + payload.properties.installationId = identity.installationId } if (identity.tenantId) { payload.groups.tenant = identity.tenantId + payload.properties.tenantId = identity.tenantId } } diff --git a/packages/backend-core/src/events/publishers/email.ts b/packages/backend-core/src/events/publishers/email.ts index 102cd2dedb..42c5fb4d4c 100644 --- a/packages/backend-core/src/events/publishers/email.ts +++ b/packages/backend-core/src/events/publishers/email.ts @@ -1,20 +1,12 @@ import { publishEvent } from "../events" -import { - Event, - SMTPConfig, - SMTPCreatedEvent, - SMTPUpdatedEvent, -} from "@budibase/types" +import { Event, SMTPCreatedEvent, SMTPUpdatedEvent } from "@budibase/types" -export async function SMTPCreated( - config: SMTPConfig, - timestamp?: string | number -) { +export async function SMTPCreated(timestamp?: string | number) { const properties: SMTPCreatedEvent = {} await publishEvent(Event.EMAIL_SMTP_CREATED, properties, timestamp) } -export async function SMTPUpdated(config: SMTPConfig) { +export async function SMTPUpdated() { const properties: SMTPUpdatedEvent = {} await publishEvent(Event.EMAIL_SMTP_UPDATED, properties) } diff --git a/packages/backend-core/src/events/publishers/license.ts b/packages/backend-core/src/events/publishers/license.ts index 1ec6649223..44dafd84ce 100644 --- a/packages/backend-core/src/events/publishers/license.ts +++ b/packages/backend-core/src/events/publishers/license.ts @@ -4,7 +4,6 @@ import { License, LicenseActivatedEvent, LicenseDowngradedEvent, - LicenseQuotaExceededEvent, LicenseUpdatedEvent, LicenseUpgradedEvent, } from "@budibase/types" @@ -32,12 +31,3 @@ export async function activated(license: License) { const properties: LicenseActivatedEvent = {} await publishEvent(Event.LICENSE_ACTIVATED, properties) } - -// TODO -export async function quotaExceeded(quotaName: string, value: number) { - const properties: LicenseQuotaExceededEvent = { - name: quotaName, - value, - } - await publishEvent(Event.LICENSE_QUOTA_EXCEEDED, properties) -} diff --git a/packages/backend-core/src/events/publishers/query.ts b/packages/backend-core/src/events/publishers/query.ts index 1a567bb051..c031885ac3 100644 --- a/packages/backend-core/src/events/publishers/query.ts +++ b/packages/backend-core/src/events/publishers/query.ts @@ -18,17 +18,32 @@ export const created = async ( query: Query, timestamp?: string ) => { - const properties: QueryCreatedEvent = {} + const properties: QueryCreatedEvent = { + queryId: query._id as string, + datasourceId: datasource._id as string, + source: datasource.source, + queryVerb: query.queryVerb, + } await publishEvent(Event.QUERY_CREATED, properties, timestamp) } export const updated = async (datasource: Datasource, query: Query) => { - const properties: QueryUpdatedEvent = {} + const properties: QueryUpdatedEvent = { + queryId: query._id as string, + datasourceId: datasource._id as string, + source: datasource.source, + queryVerb: query.queryVerb, + } await publishEvent(Event.QUERY_UPDATED, properties) } export const deleted = async (datasource: Datasource, query: Query) => { - const properties: QueryDeletedEvent = {} + const properties: QueryDeletedEvent = { + queryId: query._id as string, + datasourceId: datasource._id as string, + source: datasource.source, + queryVerb: query.queryVerb, + } await publishEvent(Event.QUERY_DELETED, properties) } @@ -37,7 +52,12 @@ export const imported = async ( importSource: any, count: any ) => { - const properties: QueryImportedEvent = {} + const properties: QueryImportedEvent = { + datasourceId: datasource._id as string, + source: datasource.source, + count, + importSource, + } await publishEvent(Event.QUERY_IMPORT, properties) } @@ -48,7 +68,12 @@ export const run = async (count: number, timestamp?: string | number) => { await publishEvent(Event.QUERIES_RUN, properties, timestamp) } -export const previewed = async (datasource: Datasource) => { - const properties: QueryPreviewedEvent = {} +export const previewed = async (datasource: Datasource, query: Query) => { + const properties: QueryPreviewedEvent = { + queryId: query._id, + datasourceId: datasource._id as string, + source: datasource.source, + queryVerb: query.queryVerb, + } await publishEvent(Event.QUERY_PREVIEWED, properties) } diff --git a/packages/backend-core/src/events/publishers/role.ts b/packages/backend-core/src/events/publishers/role.ts index 0002499814..4cd5f91703 100644 --- a/packages/backend-core/src/events/publishers/role.ts +++ b/packages/backend-core/src/events/publishers/role.ts @@ -10,29 +10,45 @@ import { User, } from "@budibase/types" -/* eslint-disable */ - export async function created(role: Role, timestamp?: string) { - const properties: RoleCreatedEvent = {} + const properties: RoleCreatedEvent = { + roleId: role._id as string, + permissionId: role.permissionId, + inherits: role.inherits, + } await publishEvent(Event.ROLE_CREATED, properties, timestamp) } export async function updated(role: Role) { - const properties: RoleUpdatedEvent = {} + const properties: RoleUpdatedEvent = { + roleId: role._id as string, + permissionId: role.permissionId, + inherits: role.inherits, + } await publishEvent(Event.ROLE_UPDATED, properties) } export async function deleted(role: Role) { - const properties: RoleDeletedEvent = {} + const properties: RoleDeletedEvent = { + roleId: role._id as string, + permissionId: role.permissionId, + inherits: role.inherits, + } await publishEvent(Event.ROLE_DELETED, properties) } -export async function assigned(user: User, role: string, timestamp?: number) { - const properties: RoleAssignedEvent = {} +export async function assigned(user: User, roleId: string, timestamp?: number) { + const properties: RoleAssignedEvent = { + userId: user._id as string, + roleId, + } await publishEvent(Event.ROLE_ASSIGNED, properties, timestamp) } -export async function unassigned(user: User, role: string) { - const properties: RoleUnassignedEvent = {} +export async function unassigned(user: User, roleId: string) { + const properties: RoleUnassignedEvent = { + userId: user._id as string, + roleId, + } await publishEvent(Event.ROLE_UNASSIGNED, properties) } diff --git a/packages/backend-core/src/events/publishers/rows.ts b/packages/backend-core/src/events/publishers/rows.ts index 4affd5d989..9ff5b4d3d7 100644 --- a/packages/backend-core/src/events/publishers/rows.ts +++ b/packages/backend-core/src/events/publishers/rows.ts @@ -21,6 +21,10 @@ export const imported = async ( format: RowImportFormat, count: number ) => { - const properties: RowsImportedEvent = {} + const properties: RowsImportedEvent = { + tableId: table._id as string, + format, + count, + } await publishEvent(Event.ROWS_IMPORTED, properties) } diff --git a/packages/backend-core/src/events/publishers/screen.ts b/packages/backend-core/src/events/publishers/screen.ts index 038f7bec3a..cbd19327e6 100644 --- a/packages/backend-core/src/events/publishers/screen.ts +++ b/packages/backend-core/src/events/publishers/screen.ts @@ -7,11 +7,19 @@ import { } from "@budibase/types" export async function created(screen: Screen, timestamp?: string) { - const properties: ScreenCreatedEvent = {} + const properties: ScreenCreatedEvent = { + layoutId: screen.layoutId, + screenId: screen._id as string, + roleId: screen.routing.roleId, + } await publishEvent(Event.SCREEN_CREATED, properties, timestamp) } export async function deleted(screen: Screen) { - const properties: ScreenDeletedEvent = {} + const properties: ScreenDeletedEvent = { + layoutId: screen.layoutId, + screenId: screen._id as string, + roleId: screen.routing.roleId, + } await publishEvent(Event.SCREEN_DELETED, properties) } diff --git a/packages/backend-core/src/events/publishers/serve.ts b/packages/backend-core/src/events/publishers/serve.ts index 8505df2987..13afede029 100644 --- a/packages/backend-core/src/events/publishers/serve.ts +++ b/packages/backend-core/src/events/publishers/serve.ts @@ -7,8 +7,6 @@ import { AppServedEvent, } from "@budibase/types" -/* eslint-disable */ - export async function servedBuilder() { const properties: BuilderServedEvent = {} await publishEvent(Event.SERVED_BUILDER, properties) @@ -16,7 +14,6 @@ export async function servedBuilder() { export async function servedApp(app: App) { const properties: AppServedEvent = { - appId: app.appId, appVersion: app.version, } await publishEvent(Event.SERVED_APP, properties) diff --git a/packages/backend-core/src/events/publishers/table.ts b/packages/backend-core/src/events/publishers/table.ts index c1f050b25e..9853be7eab 100644 --- a/packages/backend-core/src/events/publishers/table.ts +++ b/packages/backend-core/src/events/publishers/table.ts @@ -11,29 +11,39 @@ import { TableImportedEvent, } from "@budibase/types" -/* eslint-disable */ - export async function created(table: Table, timestamp?: string) { - const properties: TableCreatedEvent = {} + const properties: TableCreatedEvent = { + tableId: table._id as string, + } await publishEvent(Event.TABLE_CREATED, properties, timestamp) } export async function updated(table: Table) { - const properties: TableUpdatedEvent = {} + const properties: TableUpdatedEvent = { + tableId: table._id as string, + } await publishEvent(Event.TABLE_UPDATED, properties) } export async function deleted(table: Table) { - const properties: TableDeletedEvent = {} + const properties: TableDeletedEvent = { + tableId: table._id as string, + } await publishEvent(Event.TABLE_DELETED, properties) } export async function exported(table: Table, format: TableExportFormat) { - const properties: TableExportedEvent = {} + const properties: TableExportedEvent = { + tableId: table._id as string, + format, + } await publishEvent(Event.TABLE_EXPORTED, properties) } export async function imported(table: Table, format: TableImportFormat) { - const properties: TableImportedEvent = {} + const properties: TableImportedEvent = { + tableId: table._id as string, + format, + } await publishEvent(Event.TABLE_IMPORTED, properties) } diff --git a/packages/backend-core/src/events/publishers/user.ts b/packages/backend-core/src/events/publishers/user.ts index c5c90fb4e1..2c83b267d6 100644 --- a/packages/backend-core/src/events/publishers/user.ts +++ b/packages/backend-core/src/events/publishers/user.ts @@ -15,27 +15,33 @@ import { UserUpdatedEvent, } from "@budibase/types" -/* eslint-disable */ - export async function created(user: User, timestamp?: number) { - const properties: UserCreatedEvent = {} + const properties: UserCreatedEvent = { + userId: user._id as string, + } await publishEvent(Event.USER_CREATED, properties, timestamp) } export async function updated(user: User) { - const properties: UserUpdatedEvent = {} + const properties: UserUpdatedEvent = { + userId: user._id as string, + } await publishEvent(Event.USER_UPDATED, properties) } export async function deleted(user: User) { - const properties: UserDeletedEvent = {} + const properties: UserDeletedEvent = { + userId: user._id as string, + } await publishEvent(Event.USER_DELETED, properties) } // PERMISSIONS export async function permissionAdminAssigned(user: User, timestamp?: number) { - const properties: UserPermissionAssignedEvent = {} + const properties: UserPermissionAssignedEvent = { + userId: user._id as string, + } await publishEvent( Event.USER_PERMISSION_ADMIN_ASSIGNED, properties, @@ -44,7 +50,9 @@ export async function permissionAdminAssigned(user: User, timestamp?: number) { } export async function permissionAdminRemoved(user: User) { - const properties: UserPermissionRemovedEvent = {} + const properties: UserPermissionRemovedEvent = { + userId: user._id as string, + } await publishEvent(Event.USER_PERMISSION_ADMIN_REMOVED, properties) } @@ -52,7 +60,9 @@ export async function permissionBuilderAssigned( user: User, timestamp?: number ) { - const properties: UserPermissionAssignedEvent = {} + const properties: UserPermissionAssignedEvent = { + userId: user._id as string, + } await publishEvent( Event.USER_PERMISSION_BUILDER_ASSIGNED, properties, @@ -61,40 +71,52 @@ export async function permissionBuilderAssigned( } export async function permissionBuilderRemoved(user: User) { - const properties: UserPermissionRemovedEvent = {} + const properties: UserPermissionRemovedEvent = { + userId: user._id as string, + } await publishEvent(Event.USER_PERMISSION_BUILDER_REMOVED, properties) } // INVITE -export async function invited(userInfo: any) { +export async function invited() { const properties: UserInvitedEvent = {} await publishEvent(Event.USER_INVITED, properties) } export async function inviteAccepted(user: User) { - const properties: UserInviteAcceptedEvent = {} + const properties: UserInviteAcceptedEvent = { + userId: user._id as string, + } await publishEvent(Event.USER_INVITED_ACCEPTED, properties) } // PASSWORD export async function passwordForceReset(user: User) { - const properties: UserPasswordForceResetEvent = {} + const properties: UserPasswordForceResetEvent = { + userId: user._id as string, + } await publishEvent(Event.USER_PASSWORD_FORCE_RESET, properties) } export async function passwordUpdated(user: User) { - const properties: UserPasswordUpdatedEvent = {} + const properties: UserPasswordUpdatedEvent = { + userId: user._id as string, + } await publishEvent(Event.USER_PASSWORD_UPDATED, properties) } export async function passwordResetRequested(user: User) { - const properties: UserPasswordResetRequestedEvent = {} + const properties: UserPasswordResetRequestedEvent = { + userId: user._id as string, + } await publishEvent(Event.USER_PASSWORD_RESET_REQUESTED, properties) } export async function passwordReset(user: User) { - const properties: UserPasswordResetEvent = {} + const properties: UserPasswordResetEvent = { + userId: user._id as string, + } await publishEvent(Event.USER_PASSWORD_RESET, properties) } diff --git a/packages/backend-core/src/events/publishers/view.ts b/packages/backend-core/src/events/publishers/view.ts index e6f82579cd..1a382ec00e 100644 --- a/packages/backend-core/src/events/publishers/view.ts +++ b/packages/backend-core/src/events/publishers/view.ts @@ -20,54 +20,75 @@ import { /* eslint-disable */ export async function created(view: View, timestamp?: string) { - const properties: ViewCreatedEvent = {} + const properties: ViewCreatedEvent = { + tableId: view.tableId, + } await publishEvent(Event.VIEW_CREATED, properties, timestamp) } export async function updated(view: View) { - const properties: ViewUpdatedEvent = {} + const properties: ViewUpdatedEvent = { + tableId: view.tableId, + } await publishEvent(Event.VIEW_UPDATED, properties) } -export async function deleted() { - const properties: ViewDeletedEvent = {} +export async function deleted(view: View) { + const properties: ViewDeletedEvent = { + tableId: view.tableId, + } await publishEvent(Event.VIEW_DELETED, properties) } export async function exported(table: Table, format: TableExportFormat) { - const properties: ViewExportedEvent = {} + const properties: ViewExportedEvent = { + tableId: table._id as string, + format, + } await publishEvent(Event.VIEW_EXPORTED, properties) } -export async function filterCreated(timestamp?: string) { - const properties: ViewFilterCreatedEvent = {} +export async function filterCreated(view: View, timestamp?: string) { + const properties: ViewFilterCreatedEvent = { + tableId: view.tableId, + } await publishEvent(Event.VIEW_FILTER_CREATED, properties, timestamp) } -export async function filterUpdated() { - const properties: ViewFilterUpdatedEvent = {} +export async function filterUpdated(view: View) { + const properties: ViewFilterUpdatedEvent = { + tableId: view.tableId, + } await publishEvent(Event.VIEW_FILTER_UPDATED, properties) } -export async function filterDeleted() { - const properties: ViewFilterDeletedEvent = {} +export async function filterDeleted(view: View) { + const properties: ViewFilterDeletedEvent = { + tableId: view.tableId, + } await publishEvent(Event.VIEW_FILTER_DELETED, properties) } -export async function calculationCreated( - calculation: ViewCalculation, - timestamp?: string -) { - const properties: ViewCalculationCreatedEvent = {} +export async function calculationCreated(view: View, timestamp?: string) { + const properties: ViewCalculationCreatedEvent = { + tableId: view.tableId, + calculation: view.calculation as ViewCalculation, + } await publishEvent(Event.VIEW_CALCULATION_CREATED, properties, timestamp) } -export async function calculationUpdated() { - const properties: ViewCalculationUpdatedEvent = {} +export async function calculationUpdated(view: View) { + const properties: ViewCalculationUpdatedEvent = { + tableId: view.tableId, + calculation: view.calculation as ViewCalculation, + } await publishEvent(Event.VIEW_CALCULATION_UPDATED, properties) } -export async function calculationDeleted() { - const properties: ViewCalculationDeletedEvent = {} +export async function calculationDeleted(existingView: View) { + const properties: ViewCalculationDeletedEvent = { + tableId: existingView.tableId, + calculation: existingView.calculation as ViewCalculation, + } await publishEvent(Event.VIEW_CALCULATION_DELETED, properties) } diff --git a/packages/backend-core/src/redis/index.js b/packages/backend-core/src/redis/index.js index 0ee17265ce..65d1847eb7 100644 --- a/packages/backend-core/src/redis/index.js +++ b/packages/backend-core/src/redis/index.js @@ -164,6 +164,10 @@ class RedisWrapper { return promisifyStream(stream) } + async keys(pattern) { + return CLIENT.keys(pattern) + } + async get(key) { const db = this._db let response = await CLIENT.get(addDbPrefix(db, key)) diff --git a/packages/backend-core/src/tests/utilities/mocks/events.js b/packages/backend-core/src/tests/utilities/mocks/events.js index c1964d7ca7..f3a1ca9791 100644 --- a/packages/backend-core/src/tests/utilities/mocks/events.js +++ b/packages/backend-core/src/tests/utilities/mocks/events.js @@ -1,8 +1,15 @@ +const processors = require("../../../events/processors") + +jest.spyOn(processors.analyticsProcessor, "processEvent") + const events = require("../../../events") jest.spyOn(events.identification, "identifyTenantGroup") jest.spyOn(events.identification, "identifyUser") +jest.spyOn(events.backfill, "appSucceeded") +jest.spyOn(events.backfill, "tenantSucceeded") + jest.spyOn(events.account, "created") jest.spyOn(events.account, "deleted") jest.spyOn(events.account, "verified") @@ -102,5 +109,3 @@ jest.spyOn(events.view, "filterDeleted") jest.spyOn(events.view, "calculationCreated") jest.spyOn(events.view, "calculationUpdated") jest.spyOn(events.view, "calculationDeleted") - -module.exports = events diff --git a/packages/backend-core/src/tests/utilities/mocks/index.js b/packages/backend-core/src/tests/utilities/mocks/index.js index 5604ce75e6..743dca06f0 100644 --- a/packages/backend-core/src/tests/utilities/mocks/index.js +++ b/packages/backend-core/src/tests/utilities/mocks/index.js @@ -1,7 +1,6 @@ -const events = require("./events") +require("./events") const date = require("./date") module.exports = { - events, date, } diff --git a/packages/backend-core/src/tests/utils.spec.js b/packages/backend-core/src/tests/utils.spec.js index bb0f24fad6..43927df2fb 100644 --- a/packages/backend-core/src/tests/utils.spec.js +++ b/packages/backend-core/src/tests/utils.spec.js @@ -1,13 +1,17 @@ +require("./utilities/TestConfiguration") const { structures } = require("./utilities") const utils = require("../utils") const events = require("../events") +const { doInTenant, DEFAULT_TENANT_ID }= require("../context") describe("utils", () => { describe("platformLogout", () => { it("should call platform logout", async () => { - const ctx = structures.koa.newContext() - await utils.platformLogout({ ctx, userId: "test" }) - expect(events.auth.logout).toBeCalledTimes(1) + await doInTenant(DEFAULT_TENANT_ID, async () => { + const ctx = structures.koa.newContext() + await utils.platformLogout({ ctx, userId: "test" }) + expect(events.auth.logout).toBeCalledTimes(1) + }) }) }) }) \ No newline at end of file diff --git a/packages/server/src/api/controllers/analytics.ts b/packages/server/src/api/controllers/analytics.ts index e8fa8d82fb..efb9115e54 100644 --- a/packages/server/src/api/controllers/analytics.ts +++ b/packages/server/src/api/controllers/analytics.ts @@ -1,7 +1,8 @@ import { events } from "@budibase/backend-core" export const isEnabled = async (ctx: any) => { + const enabled = await events.analytics.enabled() ctx.body = { - enabled: events.analytics.enabled(), + enabled, } } diff --git a/packages/server/src/api/controllers/automation.js b/packages/server/src/api/controllers/automation.js index 2469e132d3..f014002cdb 100644 --- a/packages/server/src/api/controllers/automation.js +++ b/packages/server/src/api/controllers/automation.js @@ -101,13 +101,13 @@ const handleStepEvents = async (oldAutomation, automation) => { // new steps const newSteps = getNewSteps(oldAutomation, automation) for (let step of newSteps) { - await events.automation.stepCreated(step) + await events.automation.stepCreated(automation, step) } // old steps const deletedSteps = getDeletedSteps(oldAutomation, automation) for (let step of deletedSteps) { - await events.automation.stepDeleted(step) + await events.automation.stepDeleted(automation, step) } } @@ -134,7 +134,7 @@ exports.update = async function (ctx) { : {} // trigger has been updated, remove the test inputs if (oldAutoTrigger && oldAutoTrigger.id !== newAutoTrigger.id) { - await events.automation.triggerUpdated() + await events.automation.triggerUpdated(automation) await deleteEntityMetadata( ctx.appId, MetadataTypes.AUTOMATION_TEST_INPUT, @@ -180,7 +180,7 @@ exports.destroy = async function (ctx) { // delete metadata first await cleanupAutomationMetadata(automationId) ctx.body = await db.remove(automationId, ctx.params.rev) - await events.automation.deleted() + await events.automation.deleted(oldAutomation) } exports.getActionList = async function (ctx) { @@ -248,5 +248,5 @@ exports.test = async function (ctx) { }) await clearTestFlag(automation._id) ctx.body = response - await events.automation.tested() + await events.automation.tested(automation) } diff --git a/packages/server/src/api/controllers/datasource.js b/packages/server/src/api/controllers/datasource.js index e217dd2c47..379b719f4f 100644 --- a/packages/server/src/api/controllers/datasource.js +++ b/packages/server/src/api/controllers/datasource.js @@ -109,7 +109,7 @@ exports.update = async function (ctx) { } const response = await db.put(datasource) - await events.datasource.updated() + await events.datasource.updated(datasource) datasource._rev = response.rev // Drain connection pools when configuration is changed @@ -164,11 +164,11 @@ exports.save = async function (ctx) { exports.destroy = async function (ctx) { const db = getAppDB() + const datasourceId = ctx.params.datasourceId + const datasource = await db.get(datasourceId) // Delete all queries for the datasource - const queries = await db.allDocs( - getQueryParams(ctx.params.datasourceId, null) - ) + const queries = await db.allDocs(getQueryParams(datasourceId, null)) await db.bulkDocs( queries.rows.map(row => ({ _id: row.id, @@ -178,8 +178,8 @@ exports.destroy = async function (ctx) { ) // delete the datasource - await db.remove(ctx.params.datasourceId, ctx.params.revId) - await events.datasource.deleted() + await db.remove(datasourceId, ctx.params.revId) + await events.datasource.deleted(datasource) ctx.message = `Datasource deleted.` ctx.status = 200 diff --git a/packages/server/src/api/controllers/dev.js b/packages/server/src/api/controllers/dev.js index dd2bf45b21..8e4c597e38 100644 --- a/packages/server/src/api/controllers/dev.js +++ b/packages/server/src/api/controllers/dev.js @@ -118,7 +118,7 @@ exports.revert = async ctx => { ctx.body = { message: "Reverted changes successfully.", } - await events.app.reverted() + await events.app.reverted(appDoc) } catch (err) { ctx.throw(400, `Unable to revert. ${err}`) } finally { diff --git a/packages/server/src/api/controllers/query/import/tests/index.spec.js b/packages/server/src/api/controllers/query/import/tests/index.spec.js index 00d7daa43f..adcb31f8e7 100644 --- a/packages/server/src/api/controllers/query/import/tests/index.spec.js +++ b/packages/server/src/api/controllers/query/import/tests/index.spec.js @@ -53,7 +53,7 @@ describe("Rest Importer", () => { } const runTest = async (test, assertions) => { - config.doInContext(config.appId, async () => { + await config.doInContext(config.appId, async () => { for (let [key, data] of Object.entries(datasets)) { await test(key, data, assertions) } diff --git a/packages/server/src/api/controllers/query/index.ts b/packages/server/src/api/controllers/query/index.ts index ff101d48a7..2abd83140a 100644 --- a/packages/server/src/api/controllers/query/index.ts +++ b/packages/server/src/api/controllers/query/index.ts @@ -114,10 +114,10 @@ export async function preview(ctx: any) { const db = getAppDB() const datasource = await db.get(ctx.request.body.datasourceId) + const query = ctx.request.body // preview may not have a queryId as it hasn't been saved, but if it does // this stops dynamic variables from calling the same query - const { fields, parameters, queryVerb, transformer, queryId } = - ctx.request.body + const { fields, parameters, queryVerb, transformer, queryId } = query try { const runFn = () => @@ -132,7 +132,7 @@ export async function preview(ctx: any) { }) const { rows, keys, info, extra } = await quotas.addQuery(runFn) - await events.query.previewed(datasource) + await events.query.previewed(datasource, query) ctx.body = { rows, schemaFields: [...new Set(keys)], diff --git a/packages/server/src/api/controllers/view/index.js b/packages/server/src/api/controllers/view/index.js index 404652a8db..5fc479adfb 100644 --- a/packages/server/src/api/controllers/view/index.js +++ b/packages/server/src/api/controllers/view/index.js @@ -52,11 +52,11 @@ const calculationEvents = async (existingView, newView) => { const newCalculation = newView && newView.calculation if (existingCalculation && !newCalculation) { - await events.view.calculationDeleted() + await events.view.calculationDeleted(existingView) } if (!existingCalculation && newCalculation) { - await events.view.calculationCreated() + await events.view.calculationCreated(newView) } if ( @@ -64,7 +64,7 @@ const calculationEvents = async (existingView, newView) => { newCalculation && existingCalculation !== newCalculation ) { - await events.view.calculationUpdated() + await events.view.calculationUpdated(newView) } } @@ -77,11 +77,11 @@ const filterEvents = async (existingView, newView) => { const hasNewFilters = !!(newView && newView.filters && newView.filters.length) if (hasExistingFilters && !hasNewFilters) { - await events.view.filterDeleted() + await events.view.filterDeleted(newView) } if (!hasExistingFilters && hasNewFilters) { - await events.view.filterCreated() + await events.view.filterCreated(newView) } if ( @@ -89,15 +89,15 @@ const filterEvents = async (existingView, newView) => { hasNewFilters && !isEqual(existingView.filters, newView.filters) ) { - await events.view.filterUpdated() + await events.view.filterUpdated(newView) } } const handleViewEvents = async (existingView, newView) => { if (!existingView) { - await events.view.created() + await events.view.created(newView) } else { - await events.view.updated() + await events.view.updated(newView) } await calculationEvents(existingView, newView) await filterEvents(existingView, newView) @@ -110,7 +110,7 @@ exports.destroy = async ctx => { const table = await db.get(view.meta.tableId) delete table.views[viewName] await db.put(table) - await events.view.deleted() + await events.view.deleted(view) ctx.body = view } diff --git a/packages/server/src/api/routes/tests/query.spec.js b/packages/server/src/api/routes/tests/query.spec.js index dd2c62e028..5dacda3505 100644 --- a/packages/server/src/api/routes/tests/query.spec.js +++ b/packages/server/src/api/routes/tests/query.spec.js @@ -201,15 +201,16 @@ describe("/queries", () => { describe("preview", () => { it("should be able to preview the query", async () => { + const query = { + datasourceId: datasource._id, + parameters: {}, + fields: {}, + queryVerb: "read", + name: datasource.name, + } const res = await request .post(`/api/queries/preview`) - .send({ - datasourceId: datasource._id, - parameters: {}, - fields: {}, - queryVerb: "read", - name: datasource.name, - }) + .send(query) .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) @@ -218,7 +219,7 @@ describe("/queries", () => { expect(res.body.rows.length).toEqual(1) expect(events.query.previewed).toBeCalledTimes(1) datasource.config = { schema: "public" } - expect(events.query.previewed).toBeCalledWith(datasource) + expect(events.query.previewed).toBeCalledWith(datasource, query) }) it("should apply authorization to endpoint", async () => { diff --git a/packages/server/src/migrations/functions/backfill/app.ts b/packages/server/src/migrations/functions/backfill/app.ts index 83f99a1c16..3c562ecc1d 100644 --- a/packages/server/src/migrations/functions/backfill/app.ts +++ b/packages/server/src/migrations/functions/backfill/app.ts @@ -6,7 +6,7 @@ import * as roles from "./app/roles" import * as tables from "./app/tables" import * as screens from "./app/screens" import * as global from "./global" -import { App, AppBackfillSucceededEvent } from "@budibase/types" +import { App, AppBackfillSucceededEvent, Event } from "@budibase/types" import { db as dbUtils, events } from "@budibase/backend-core" import env from "../../../environment" @@ -22,6 +22,22 @@ const handleError = (e: any, errors?: any) => { throw e } +const EVENTS = [ + Event.AUTOMATION_CREATED, + Event.AUTOMATION_STEP_CREATED, + Event.DATASOURCE_CREATED, + Event.LAYOUT_CREATED, + Event.QUERY_CREATED, + Event.ROLE_CREATED, + Event.SCREEN_CREATED, + Event.TABLE_CREATED, + Event.VIEW_CREATED, + Event.VIEW_CALCULATION_CREATED, + Event.VIEW_FILTER_CREATED, + Event.APP_PUBLISHED, + Event.APP_CREATED, +] + /** * Date: * May 2022 @@ -39,6 +55,10 @@ export const run = async (appDb: any) => { return } + // tell the event pipeline to start caching + // events for this tenant + await events.backfillCache.start(EVENTS) + const app: App = await appDb.get(dbUtils.DocumentTypes.APP_METADATA) const timestamp = app.createdAt as string @@ -115,6 +135,8 @@ export const run = async (appDb: any) => { } await events.backfill.appSucceeded(properties) + // tell the event pipeline to stop caching events for this tenant + await events.backfillCache.end() } catch (e) { handleError(e) await events.backfill.appFailed(e) diff --git a/packages/server/src/migrations/functions/backfill/app/layouts.ts b/packages/server/src/migrations/functions/backfill/app/layouts.ts index ec36d59b30..d2a551096d 100644 --- a/packages/server/src/migrations/functions/backfill/app/layouts.ts +++ b/packages/server/src/migrations/functions/backfill/app/layouts.ts @@ -15,6 +15,13 @@ export const backfill = async (appDb: any, timestamp: string) => { const layouts: Layout[] = await getLayouts(appDb) for (const layout of layouts) { + // exclude default layouts + if ( + layout._id === "layout_private_master" || + layout._id === "layout_public_master" + ) { + continue + } await events.layout.created(layout, timestamp) } diff --git a/packages/server/src/migrations/functions/backfill/app/tables.ts b/packages/server/src/migrations/functions/backfill/app/tables.ts index ea41cd896a..484a179bbe 100644 --- a/packages/server/src/migrations/functions/backfill/app/tables.ts +++ b/packages/server/src/migrations/functions/backfill/app/tables.ts @@ -22,11 +22,11 @@ export const backfill = async (appDb: any, timestamp: string) => { await events.view.created(view, timestamp) if (view.calculation) { - await events.view.calculationCreated(view.calculation, timestamp) + await events.view.calculationCreated(view, timestamp) } if (view.filters?.length) { - await events.view.filterCreated(timestamp) + await events.view.filterCreated(view, timestamp) } } } diff --git a/packages/server/src/migrations/functions/backfill/global.ts b/packages/server/src/migrations/functions/backfill/global.ts index 7031b0f61f..8b58b15c7e 100644 --- a/packages/server/src/migrations/functions/backfill/global.ts +++ b/packages/server/src/migrations/functions/backfill/global.ts @@ -1,4 +1,3 @@ -import { TenantBackfillSucceededEvent } from "./../../../../../types/src/events/backfill" import * as users from "./global/users" import * as configs from "./global/configs" import * as quotas from "./global/quotas" @@ -10,7 +9,12 @@ import { db as dbUtils, } from "@budibase/backend-core" import { QuotaUsage } from "@budibase/pro" -import { CloudAccount, App } from "@budibase/types" +import { + CloudAccount, + App, + TenantBackfillSucceededEvent, + Event, +} from "@budibase/types" import env from "../../../environment" const failGraceful = env.SELF_HOSTED && !env.isDev() @@ -57,6 +61,22 @@ const formatUsage = (usage: QuotaUsage) => { } } +const EVENTS = [ + Event.EMAIL_SMTP_CREATED, + Event.AUTH_SSO_CREATED, + Event.AUTH_SSO_ACTIVATED, + Event.ORG_NAME_UPDATED, + Event.ORG_LOGO_UPDATED, + Event.ORG_PLATFORM_URL_UPDATED, + Event.USER_CREATED, + Event.USER_PERMISSION_ADMIN_ASSIGNED, + Event.USER_PERMISSION_BUILDER_ASSIGNED, + Event.ROLE_ASSIGNED, + Event.ROWS_CREATED, + Event.QUERIES_RUN, + Event.AUTOMATIONS_RUN, +] + /** * Date: * May 2022 @@ -94,6 +114,10 @@ export const run = async (db: any) => { handleError(e, errors) } + // tell the event pipeline to start caching + // events for this tenant + await events.backfillCache.start(EVENTS) + try { await configs.backfill(db, installTimestamp) } catch (e) { @@ -130,7 +154,10 @@ export const run = async (db: any) => { } else { properties.errorCount = 0 } + await events.backfill.tenantSucceeded(properties) + // tell the event pipeline to stop caching events for this tenant + await events.backfillCache.end() } catch (e) { handleError(e) await events.backfill.tenantFailed(e) diff --git a/packages/server/src/migrations/functions/backfill/global/configs.ts b/packages/server/src/migrations/functions/backfill/global/configs.ts index 94ac838e20..9186a1626a 100644 --- a/packages/server/src/migrations/functions/backfill/global/configs.ts +++ b/packages/server/src/migrations/functions/backfill/global/configs.ts @@ -28,7 +28,7 @@ export const backfill = async ( for (const config of configs) { if (isSMTPConfig(config)) { - await events.email.SMTPCreated(config, timestamp) + await events.email.SMTPCreated(timestamp) } if (isGoogleConfig(config)) { await events.auth.SSOCreated("google", timestamp) diff --git a/packages/server/src/migrations/tests/helpers.ts b/packages/server/src/migrations/tests/helpers.ts index a2e45fbb26..17658d5290 100644 --- a/packages/server/src/migrations/tests/helpers.ts +++ b/packages/server/src/migrations/tests/helpers.ts @@ -26,5 +26,15 @@ export const saveSmtpConfig = async (globalDb: any) => { const saveConfig = async (config: Config, globalDb: any) => { config._id = db.generateConfigID({ type: config.type }) - await globalDb.put(config) + + let response + try { + response = await globalDb.get(config._id) + config._rev = response._rev + await globalDb.put(config) + } catch (e: any) { + if (e.status === 404) { + await globalDb.put(config) + } + } } diff --git a/packages/server/src/migrations/tests/index.spec.ts b/packages/server/src/migrations/tests/index.spec.ts index 220db083f7..ca91b3a29c 100644 --- a/packages/server/src/migrations/tests/index.spec.ts +++ b/packages/server/src/migrations/tests/index.spec.ts @@ -7,8 +7,6 @@ import * as helpers from "./helpers" const { mocks } = require("@budibase/backend-core/testUtils") const timestamp = mocks.date.MOCK_DATE.toISOString() -jest.setTimeout(100000) - describe("migrations", () => { const config = new TestConfig() @@ -50,7 +48,7 @@ describe("migrations", () => { expect(events.automation.created).toBeCalledTimes(2) expect(events.automation.stepCreated).toBeCalledTimes(1) expect(events.datasource.created).toBeCalledTimes(2) - expect(events.layout.created).toBeCalledTimes(3) + expect(events.layout.created).toBeCalledTimes(1) expect(events.query.created).toBeCalledTimes(2) expect(events.role.created).toBeCalledTimes(2) expect(events.table.created).toBeCalledTimes(3) @@ -58,6 +56,15 @@ describe("migrations", () => { expect(events.view.calculationCreated).toBeCalledTimes(1) expect(events.view.filterCreated).toBeCalledTimes(1) expect(events.screen.created).toBeCalledTimes(2) + expect(events.backfill.appSucceeded).toBeCalledTimes(2) + + const processor = events.processors.analyticsProcessor.processEvent + console.log(processor) + + // to make sure caching is working as expected + expect( + events.processors.analyticsProcessor.processEvent + ).toBeCalledTimes(23) }) }) }) @@ -96,6 +103,12 @@ describe("migrations", () => { expect(events.org.logoUpdated).toBeCalledTimes(1) expect(events.org.nameUpdated).toBeCalledTimes(1) expect(events.org.platformURLUpdated).toBeCalledTimes(1) + expect(events.backfill.tenantSucceeded).toBeCalledTimes(1) + + // to make sure caching is working as expected + expect(events.processors.analyticsProcessor.processEvent).toBeCalledTimes( + 19 + ) }) }) }) diff --git a/packages/server/src/tests/utilities/TestConfiguration.js b/packages/server/src/tests/utilities/TestConfiguration.js index 2be9ae1234..fc4d302c63 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.js +++ b/packages/server/src/tests/utilities/TestConfiguration.js @@ -1,4 +1,3 @@ -require("./mocks") require("../../db").init() const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles") const env = require("../../environment") diff --git a/packages/server/src/tests/utilities/mocks/core.js b/packages/server/src/tests/utilities/mocks/core.js deleted file mode 100644 index 2e15ade664..0000000000 --- a/packages/server/src/tests/utilities/mocks/core.js +++ /dev/null @@ -1,3 +0,0 @@ -const utils = require("@budibase/backend-core/testUtils") -const core = require("@budibase/backend-core") -core.events = utils.mocks.events diff --git a/packages/server/src/tests/utilities/mocks/index.js b/packages/server/src/tests/utilities/mocks/index.js deleted file mode 100644 index 1e7730932f..0000000000 --- a/packages/server/src/tests/utilities/mocks/index.js +++ /dev/null @@ -1 +0,0 @@ -require("./core") diff --git a/packages/types/src/documents/app/role.ts b/packages/types/src/documents/app/role.ts index 0248fe733d..1c4b15ba2c 100644 --- a/packages/types/src/documents/app/role.ts +++ b/packages/types/src/documents/app/role.ts @@ -1,3 +1,6 @@ import { Document } from "../document" -export interface Role extends Document {} +export interface Role extends Document { + permissionId: string + inherits: string +} diff --git a/packages/types/src/documents/app/screen.ts b/packages/types/src/documents/app/screen.ts index 01d3f02746..311c023011 100644 --- a/packages/types/src/documents/app/screen.ts +++ b/packages/types/src/documents/app/screen.ts @@ -1,3 +1,9 @@ import { Document } from "../document" -export interface Screen extends Document {} +export interface Screen extends Document { + layoutId: string + routing: { + route: string + roleId: string + } +} diff --git a/packages/types/src/events/account.ts b/packages/types/src/events/account.ts index c41ab27cc0..76fbd60f9b 100644 --- a/packages/types/src/events/account.ts +++ b/packages/types/src/events/account.ts @@ -1,13 +1,15 @@ -export interface AccountCreatedEvent { +import { BaseEvent } from "./event" + +export interface AccountCreatedEvent extends BaseEvent { tenantId: string registrationStep?: string } -export interface AccountDeletedEvent { +export interface AccountDeletedEvent extends BaseEvent { tenantId: string registrationStep?: string } -export interface AccountVerifiedEvent { +export interface AccountVerifiedEvent extends BaseEvent { tenantId: string } diff --git a/packages/types/src/events/app.ts b/packages/types/src/events/app.ts index d46f0be513..73d491070f 100644 --- a/packages/types/src/events/app.ts +++ b/packages/types/src/events/app.ts @@ -1,50 +1,52 @@ -export interface AppCreatedEvent { +import { BaseEvent } from "./event" + +export interface AppCreatedEvent extends BaseEvent { appId: string version: string } -export interface AppUpdatedEvent { +export interface AppUpdatedEvent extends BaseEvent { appId: string version: string } -export interface AppDeletedEvent { +export interface AppDeletedEvent extends BaseEvent { appId: string } -export interface AppPublishedEvent { +export interface AppPublishedEvent extends BaseEvent { appId: string } -export interface AppUnpublishedEvent { +export interface AppUnpublishedEvent extends BaseEvent { appId: string } -export interface AppFileImportedEvent { +export interface AppFileImportedEvent extends BaseEvent { appId: string } -export interface AppTemplateImportedEvent { +export interface AppTemplateImportedEvent extends BaseEvent { appId: string templateKey: string } -export interface AppVersionUpdatedEvent { +export interface AppVersionUpdatedEvent extends BaseEvent { appId: string currentVersion: string updatedToVersion: string } -export interface AppVersionRevertedEvent { +export interface AppVersionRevertedEvent extends BaseEvent { appId: string currentVersion: string revertedToVersion: string } -export interface AppRevertedEvent { +export interface AppRevertedEvent extends BaseEvent { appId: string } -export interface AppExportedEvent { +export interface AppExportedEvent extends BaseEvent { appId: string } diff --git a/packages/types/src/events/auth.ts b/packages/types/src/events/auth.ts index c7e6cbb4ef..6d79fb344b 100644 --- a/packages/types/src/events/auth.ts +++ b/packages/types/src/events/auth.ts @@ -1,27 +1,29 @@ +import { BaseEvent } from "./event" + export type LoginSource = "local" | "google" | "oidc" | "google-internal" export type SSOType = "oidc" | "google" -export interface LoginEvent { +export interface LoginEvent extends BaseEvent { userId: string source: LoginSource } -export interface LogoutEvent { +export interface LogoutEvent extends BaseEvent { userId: string } -export interface SSOCreatedEvent { +export interface SSOCreatedEvent extends BaseEvent { type: SSOType } -export interface SSOUpdatedEvent { +export interface SSOUpdatedEvent extends BaseEvent { type: SSOType } -export interface SSOActivatedEvent { +export interface SSOActivatedEvent extends BaseEvent { type: SSOType } -export interface SSODeactivatedEvent { +export interface SSODeactivatedEvent extends BaseEvent { type: SSOType } diff --git a/packages/types/src/events/automation.ts b/packages/types/src/events/automation.ts index 108894cec6..beabdbb9eb 100644 --- a/packages/types/src/events/automation.ts +++ b/packages/types/src/events/automation.ts @@ -1,32 +1,34 @@ -export interface AutomationCreatedEvent { +import { BaseEvent } from "./event" + +export interface AutomationCreatedEvent extends BaseEvent { appId: string automationId: string triggerId: string triggerType: string } -export interface AutomationTriggerUpdatedEvent { +export interface AutomationTriggerUpdatedEvent extends BaseEvent { appId: string automationId: string triggerId: string triggerType: string } -export interface AutomationDeletedEvent { +export interface AutomationDeletedEvent extends BaseEvent { appId: string automationId: string triggerId: string triggerType: string } -export interface AutomationTestedEvent { +export interface AutomationTestedEvent extends BaseEvent { appId: string automationId: string triggerId: string triggerType: string } -export interface AutomationStepCreatedEvent { +export interface AutomationStepCreatedEvent extends BaseEvent { appId: string automationId: string triggerId: string @@ -35,7 +37,7 @@ export interface AutomationStepCreatedEvent { stepType: string } -export interface AutomationStepDeletedEvent { +export interface AutomationStepDeletedEvent extends BaseEvent { appId: string automationId: string triggerId: string @@ -44,6 +46,6 @@ export interface AutomationStepDeletedEvent { stepType: string } -export interface AutomationsRunEvent { +export interface AutomationsRunEvent extends BaseEvent { count: number } diff --git a/packages/types/src/events/backfill.ts b/packages/types/src/events/backfill.ts index 35e921dece..163b0fe6a2 100644 --- a/packages/types/src/events/backfill.ts +++ b/packages/types/src/events/backfill.ts @@ -1,4 +1,6 @@ -export interface AppBackfillSucceededEvent { +import { BaseEvent, Event } from "./event" + +export interface AppBackfillSucceededEvent extends BaseEvent { appId: string automations: number datasources: number @@ -11,11 +13,11 @@ export interface AppBackfillSucceededEvent { errorCount?: number } -export interface AppBackfillFailedEvent { +export interface AppBackfillFailedEvent extends BaseEvent { error: string } -export interface TenantBackfillSucceededEvent { +export interface TenantBackfillSucceededEvent extends BaseEvent { apps: number users: number @@ -24,12 +26,21 @@ export interface TenantBackfillSucceededEvent { errorCount?: number } -export interface TenantBackfillFailedEvent { +export interface TenantBackfillFailedEvent extends BaseEvent { error: string } -export interface InstallationBackfillSucceededEvent {} +export interface InstallationBackfillSucceededEvent extends BaseEvent {} -export interface InstallationBackfillFailedEvent { +export interface InstallationBackfillFailedEvent extends BaseEvent { error: string } + +export interface BackfillMetadata extends BaseEvent { + eventWhitelist: Event[] +} + +export interface CachedEvent extends BaseEvent { + event: Event + properties: any +} diff --git a/packages/types/src/events/config.ts b/packages/types/src/events/config.ts deleted file mode 100644 index de3c5dc2dd..0000000000 --- a/packages/types/src/events/config.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface SMTPCreatedEvent {} - -export interface SMTPUpdatedEvent {} diff --git a/packages/types/src/events/datasource.ts b/packages/types/src/events/datasource.ts index cead646477..20d1b78fbb 100644 --- a/packages/types/src/events/datasource.ts +++ b/packages/types/src/events/datasource.ts @@ -1,14 +1,16 @@ -export interface DatasourceCreatedEvent { +import { BaseEvent } from "./event" + +export interface DatasourceCreatedEvent extends BaseEvent { datasourceId: string source: string } -export interface DatasourceUpdatedEvent { +export interface DatasourceUpdatedEvent extends BaseEvent { datasourceId: string source: string } -export interface DatasourceDeletedEvent { +export interface DatasourceDeletedEvent extends BaseEvent { datasourceId: string source: string } diff --git a/packages/types/src/events/email.ts b/packages/types/src/events/email.ts new file mode 100644 index 0000000000..b1e6fb268c --- /dev/null +++ b/packages/types/src/events/email.ts @@ -0,0 +1,5 @@ +import { BaseEvent } from "./event" + +export interface SMTPCreatedEvent extends BaseEvent {} + +export interface SMTPUpdatedEvent extends BaseEvent {} diff --git a/packages/types/src/events/event.ts b/packages/types/src/events/event.ts index 9c8e10b9ad..5f61174b88 100644 --- a/packages/types/src/events/event.ts +++ b/packages/types/src/events/event.ts @@ -37,7 +37,7 @@ export enum Event { ORG_LOGO_UPDATED = "org:info:logo:updated", ORG_PLATFORM_URL_UPDATED = "org:platformurl:updated", - // ORG / UPDATE + // VERSIONS VERSION_CHECKED = "version:checked", VERSION_UPGRADED = "version:upgraded", VERSION_DOWNGRADED = "version:downgraded", @@ -134,7 +134,6 @@ export enum Event { LICENSE_DOWNGRADED = "license:downgraded", LICENSE_UPDATED = "license:updated", LICENSE_ACTIVATED = "license:activated", - LICENSE_QUOTA_EXCEEDED = "license:quota:exceeded", // ACCOUNT ACCOUNT_CREATED = "account:created", @@ -150,6 +149,15 @@ export enum Event { INSTALLATION_BACKFILL_FAILED = "installation:backfill:failed", } +// properties added at the final stage of the event pipeline +export interface BaseEvent { + version?: string + service?: string + appId?: string + installationId?: string + tenantId?: string +} + export type RowImportFormat = "csv" export type TableExportFormat = "json" | "csv" export type TableImportFormat = "csv" diff --git a/packages/types/src/events/index.ts b/packages/types/src/events/index.ts index d87558332a..b88a078087 100644 --- a/packages/types/src/events/index.ts +++ b/packages/types/src/events/index.ts @@ -1,7 +1,7 @@ export * from "./app" export * from "./auth" export * from "./automation" -export * from "./config" +export * from "./email" export * from "./datasource" export * from "./event" export * from "./layout" diff --git a/packages/types/src/events/layout.ts b/packages/types/src/events/layout.ts index b0b5be4f6d..12680a28b5 100644 --- a/packages/types/src/events/layout.ts +++ b/packages/types/src/events/layout.ts @@ -1,7 +1,9 @@ -export interface LayoutCreatedEvent { +import { BaseEvent } from "./event" + +export interface LayoutCreatedEvent extends BaseEvent { layoutId: string } -export interface LayoutDeletedEvent { +export interface LayoutDeletedEvent extends BaseEvent { layoutId: string } diff --git a/packages/types/src/events/license.ts b/packages/types/src/events/license.ts index 2fa1f0b6cf..771327c960 100644 --- a/packages/types/src/events/license.ts +++ b/packages/types/src/events/license.ts @@ -5,5 +5,3 @@ export interface LicenseDowngradedEvent {} export interface LicenseUpdatedEvent {} export interface LicenseActivatedEvent {} - -export interface LicenseQuotaExceededEvent {} diff --git a/packages/types/src/events/query.ts b/packages/types/src/events/query.ts index 864298e7e2..2b07f18253 100644 --- a/packages/types/src/events/query.ts +++ b/packages/types/src/events/query.ts @@ -1,13 +1,40 @@ -export interface QueryCreatedEvent {} +import { BaseEvent } from "./event" -export interface QueryUpdatedEvent {} +export interface QueryCreatedEvent extends BaseEvent { + queryId: string + datasourceId: string + source: string + queryVerb: string +} -export interface QueryDeletedEvent {} +export interface QueryUpdatedEvent extends BaseEvent { + queryId: string + datasourceId: string + source: string + queryVerb: string +} -export interface QueryImportedEvent {} +export interface QueryDeletedEvent extends BaseEvent { + queryId: string + datasourceId: string + source: string + queryVerb: string +} -export interface QueryPreviewedEvent {} +export interface QueryImportedEvent extends BaseEvent { + datasourceId: string + source: string + count: number + importSource: string +} -export interface QueriesRunEvent { +export interface QueryPreviewedEvent extends BaseEvent { + queryId?: string + datasourceId: string + source: string + queryVerb: string +} + +export interface QueriesRunEvent extends BaseEvent { count: number } diff --git a/packages/types/src/events/role.ts b/packages/types/src/events/role.ts index 45e6c84938..adeb169a53 100644 --- a/packages/types/src/events/role.ts +++ b/packages/types/src/events/role.ts @@ -1,9 +1,29 @@ -export interface RoleCreatedEvent {} +import { BaseEvent } from "./event" -export interface RoleUpdatedEvent {} +export interface RoleCreatedEvent extends BaseEvent { + roleId: string + permissionId: string + inherits: string +} -export interface RoleDeletedEvent {} +export interface RoleUpdatedEvent extends BaseEvent { + roleId: string + permissionId: string + inherits: string +} -export interface RoleAssignedEvent {} +export interface RoleDeletedEvent extends BaseEvent { + roleId: string + permissionId: string + inherits: string +} -export interface RoleUnassignedEvent {} +export interface RoleAssignedEvent extends BaseEvent { + userId: string + roleId: string +} + +export interface RoleUnassignedEvent extends BaseEvent { + userId: string + roleId: string +} diff --git a/packages/types/src/events/rows.ts b/packages/types/src/events/rows.ts index bf7c021eb0..871658e6c9 100644 --- a/packages/types/src/events/rows.ts +++ b/packages/types/src/events/rows.ts @@ -1,5 +1,11 @@ -export interface RowsImportedEvent {} +import { BaseEvent, RowImportFormat } from "./event" -export interface RowsCreatedEvent { +export interface RowsImportedEvent extends BaseEvent { + tableId: string + format: RowImportFormat + count: number +} + +export interface RowsCreatedEvent extends BaseEvent { count: number } diff --git a/packages/types/src/events/screen.ts b/packages/types/src/events/screen.ts index 5c7e2952e7..0d9f8cb32c 100644 --- a/packages/types/src/events/screen.ts +++ b/packages/types/src/events/screen.ts @@ -1,3 +1,13 @@ -export interface ScreenCreatedEvent {} +import { BaseEvent } from "./event" -export interface ScreenDeletedEvent {} +export interface ScreenCreatedEvent extends BaseEvent { + screenId: string + layoutId: string + roleId: string +} + +export interface ScreenDeletedEvent extends BaseEvent { + screenId: string + layoutId: string + roleId: string +} diff --git a/packages/types/src/events/serve.ts b/packages/types/src/events/serve.ts index 8ac9ae2c6b..fe5b723942 100644 --- a/packages/types/src/events/serve.ts +++ b/packages/types/src/events/serve.ts @@ -1,11 +1,11 @@ -export interface BuilderServedEvent {} +import { BaseEvent } from "./event" -export interface AppServedEvent { - appId: string +export interface BuilderServedEvent extends BaseEvent {} + +export interface AppServedEvent extends BaseEvent { appVersion: string } -export interface AppPreviewServedEvent { - appId: string +export interface AppPreviewServedEvent extends BaseEvent { appVersion: string } diff --git a/packages/types/src/events/table.ts b/packages/types/src/events/table.ts index 2a3c5ef2aa..2f9ca1c93e 100644 --- a/packages/types/src/events/table.ts +++ b/packages/types/src/events/table.ts @@ -1,9 +1,23 @@ -export interface TableCreatedEvent {} +import { BaseEvent, TableExportFormat, TableImportFormat } from "./event" -export interface TableUpdatedEvent {} +export interface TableCreatedEvent extends BaseEvent { + tableId: string +} -export interface TableDeletedEvent {} +export interface TableUpdatedEvent extends BaseEvent { + tableId: string +} -export interface TableExportedEvent {} +export interface TableDeletedEvent extends BaseEvent { + tableId: string +} -export interface TableImportedEvent {} +export interface TableExportedEvent extends BaseEvent { + tableId: string + format: TableExportFormat +} + +export interface TableImportedEvent extends BaseEvent { + tableId: string + format: TableImportFormat +} diff --git a/packages/types/src/events/user.ts b/packages/types/src/events/user.ts index df75dd1158..c0721ab390 100644 --- a/packages/types/src/events/user.ts +++ b/packages/types/src/events/user.ts @@ -1,21 +1,43 @@ -export interface UserCreatedEvent {} +import { BaseEvent } from "./event" -export interface UserUpdatedEvent {} +export interface UserCreatedEvent extends BaseEvent { + userId: string +} -export interface UserDeletedEvent {} +export interface UserUpdatedEvent extends BaseEvent { + userId: string +} -export interface UserPermissionAssignedEvent {} +export interface UserDeletedEvent extends BaseEvent { + userId: string +} -export interface UserPermissionRemovedEvent {} +export interface UserPermissionAssignedEvent extends BaseEvent { + userId: string +} -export interface UserInvitedEvent {} +export interface UserPermissionRemovedEvent extends BaseEvent { + userId: string +} -export interface UserInviteAcceptedEvent {} +export interface UserInvitedEvent extends BaseEvent {} -export interface UserPasswordForceResetEvent {} +export interface UserInviteAcceptedEvent extends BaseEvent { + userId: string +} -export interface UserPasswordUpdatedEvent {} +export interface UserPasswordForceResetEvent extends BaseEvent { + userId: string +} -export interface UserPasswordResetRequestedEvent {} +export interface UserPasswordUpdatedEvent extends BaseEvent { + userId: string +} -export interface UserPasswordResetEvent {} +export interface UserPasswordResetRequestedEvent extends BaseEvent { + userId: string +} + +export interface UserPasswordResetEvent extends BaseEvent { + userId: string +} diff --git a/packages/types/src/events/version.ts b/packages/types/src/events/version.ts index 07873212c1..6535c5abca 100644 --- a/packages/types/src/events/version.ts +++ b/packages/types/src/events/version.ts @@ -1,8 +1,10 @@ -export interface VersionCheckedEvent { +import { BaseEvent } from "./event" + +export interface VersionCheckedEvent extends BaseEvent { currentVersion: string } -export interface VersionChangeEvent { +export interface VersionChangeEvent extends BaseEvent { from: string to: string } diff --git a/packages/types/src/events/view.ts b/packages/types/src/events/view.ts index eec8992002..f75bc4263f 100644 --- a/packages/types/src/events/view.ts +++ b/packages/types/src/events/view.ts @@ -1,19 +1,46 @@ -export interface ViewCreatedEvent {} +import { ViewCalculation } from "../documents" +import { BaseEvent, TableExportFormat } from "./event" -export interface ViewUpdatedEvent {} +export interface ViewCreatedEvent extends BaseEvent { + tableId: string +} -export interface ViewDeletedEvent {} +export interface ViewUpdatedEvent extends BaseEvent { + tableId: string +} -export interface ViewExportedEvent {} +export interface ViewDeletedEvent extends BaseEvent { + tableId: string +} -export interface ViewFilterCreatedEvent {} +export interface ViewExportedEvent extends BaseEvent { + tableId: string + format: TableExportFormat +} -export interface ViewFilterUpdatedEvent {} +export interface ViewFilterCreatedEvent extends BaseEvent { + tableId: string +} -export interface ViewFilterDeletedEvent {} +export interface ViewFilterUpdatedEvent extends BaseEvent { + tableId: string +} -export interface ViewCalculationCreatedEvent {} +export interface ViewFilterDeletedEvent extends BaseEvent { + tableId: string +} -export interface ViewCalculationUpdatedEvent {} +export interface ViewCalculationCreatedEvent extends BaseEvent { + tableId: string + calculation: ViewCalculation +} -export interface ViewCalculationDeletedEvent {} +export interface ViewCalculationUpdatedEvent extends BaseEvent { + tableId: string + calculation: ViewCalculation +} + +export interface ViewCalculationDeletedEvent extends BaseEvent { + tableId: string + calculation: ViewCalculation +} diff --git a/packages/worker/scripts/jestSetup.js b/packages/worker/scripts/jestSetup.js index 9cf398ca63..7af0f912a8 100644 --- a/packages/worker/scripts/jestSetup.js +++ b/packages/worker/scripts/jestSetup.js @@ -1,4 +1,11 @@ const env = require("../src/environment") + +env._set("SELF_HOSTED", "1") +env._set("NODE_ENV", "jest") +env._set("JWT_SECRET", "test-jwtsecret") +env._set("LOG_LEVEL", "silent") +env._set("MULTI_TENANCY", true) + const { mocks } = require("@budibase/backend-core/testUtils") // mock all dates to 2020-01-01T00:00:00.000Z @@ -6,8 +13,4 @@ const { mocks } = require("@budibase/backend-core/testUtils") const tk = require("timekeeper") tk.freeze(mocks.date.MOCK_DATE) -env._set("SELF_HOSTED", "1") -env._set("NODE_ENV", "jest") -env._set("JWT_SECRET", "test-jwtsecret") -env._set("LOG_LEVEL", "silent") -env._set("MULTI_TENANCY", true) +global.console.log = jest.fn() // console.log are ignored in tests diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index 0768fa33e4..b78c1c5762 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -141,7 +141,7 @@ export const invite = async (ctx: any) => { ctx.body = { message: "Invitation has been sent.", } - await events.user.invited(userInfo) + await events.user.invited() } export const inviteAccept = async (ctx: any) => { diff --git a/packages/worker/src/api/routes/tests/self.spec.js b/packages/worker/src/api/routes/tests/self.spec.js index 589d4e2b3f..64fce5ae1d 100644 --- a/packages/worker/src/api/routes/tests/self.spec.js +++ b/packages/worker/src/api/routes/tests/self.spec.js @@ -1,6 +1,6 @@ jest.mock("nodemailer") const { config, request } = require("../../../tests") -const { events, utils } = require("@budibase/backend-core") +const { events } = require("@budibase/backend-core") describe("/api/global/self", () => { diff --git a/packages/worker/src/api/routes/tests/users.spec.js b/packages/worker/src/api/routes/tests/users.spec.js index b70e93e5db..62cc02fde8 100644 --- a/packages/worker/src/api/routes/tests/users.spec.js +++ b/packages/worker/src/api/routes/tests/users.spec.js @@ -39,7 +39,6 @@ describe("/api/global/users", () => { expect(sendMailMock).toHaveBeenCalled() expect(code).toBeDefined() expect(events.user.invited).toBeCalledTimes(1) - expect(events.user.invited).toBeCalledWith({ tenantId: structures.TENANT_ID }) }) it("should be able to create new user from invite", async () => { diff --git a/packages/worker/src/tests/mocks/core.js b/packages/worker/src/tests/mocks/core.js deleted file mode 100644 index 2e15ade664..0000000000 --- a/packages/worker/src/tests/mocks/core.js +++ /dev/null @@ -1,3 +0,0 @@ -const utils = require("@budibase/backend-core/testUtils") -const core = require("@budibase/backend-core") -core.events = utils.mocks.events diff --git a/packages/worker/src/tests/mocks/index.js b/packages/worker/src/tests/mocks/index.js index 575938870b..5a85348350 100644 --- a/packages/worker/src/tests/mocks/index.js +++ b/packages/worker/src/tests/mocks/index.js @@ -1,4 +1,3 @@ -require("./core") const email = require("./email") module.exports = { From a4eef3daab61fae3c311d931d64bc7479347dc4c Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 1 Jun 2022 14:10:00 +0100 Subject: [PATCH 52/75] Add awareness of deployment environment --- charts/budibase/templates/app-service-deployment.yaml | 2 ++ charts/budibase/templates/worker-service-deployment.yaml | 2 ++ hosting/single/Dockerfile | 1 + packages/backend-core/src/environment.ts | 4 +++- packages/backend-core/src/events/backfill.ts | 5 ++--- .../backend-core/src/events/processors/PosthogProcessor.ts | 1 + packages/server/Dockerfile | 1 + packages/server/scripts/dev/manage.js | 2 ++ packages/types/src/events/event.ts | 1 + packages/worker/Dockerfile | 2 ++ packages/worker/scripts/dev/manage.js | 2 ++ 11 files changed, 19 insertions(+), 4 deletions(-) diff --git a/charts/budibase/templates/app-service-deployment.yaml b/charts/budibase/templates/app-service-deployment.yaml index 98a949418c..8797edf42b 100644 --- a/charts/budibase/templates/app-service-deployment.yaml +++ b/charts/budibase/templates/app-service-deployment.yaml @@ -28,6 +28,8 @@ spec: - env: - name: BUDIBASE_ENVIRONMENT value: {{ .Values.globals.budibaseEnv }} + - name: DEPLOYMENT_ENVIRONMENT + value: "kubernetes" - name: COUCH_DB_URL {{ if .Values.services.couchdb.url }} value: {{ .Values.services.couchdb.url }} diff --git a/charts/budibase/templates/worker-service-deployment.yaml b/charts/budibase/templates/worker-service-deployment.yaml index 15ff05e214..8c3c878bec 100644 --- a/charts/budibase/templates/worker-service-deployment.yaml +++ b/charts/budibase/templates/worker-service-deployment.yaml @@ -27,6 +27,8 @@ spec: spec: containers: - env: + - name: DEPLOYMENT_ENVIRONMENT + value: "kubernetes" - name: CLUSTER_PORT value: {{ .Values.services.worker.port | quote }} {{ if .Values.services.couchdb.enabled }} diff --git a/hosting/single/Dockerfile b/hosting/single/Dockerfile index 2123d237b5..88e7b1fb68 100644 --- a/hosting/single/Dockerfile +++ b/hosting/single/Dockerfile @@ -1,5 +1,6 @@ FROM couchdb +ENV DEPLOYMENT_ENVIRONMENT=docker ENV COUCHDB_PASSWORD=budibase ENV COUCHDB_USER=budibase ENV COUCH_DB_URL=http://budibase:budibase@localhost:5984 diff --git a/packages/backend-core/src/environment.ts b/packages/backend-core/src/environment.ts index 1576c4487d..1d9febc6f9 100644 --- a/packages/backend-core/src/environment.ts +++ b/packages/backend-core/src/environment.ts @@ -52,8 +52,10 @@ const env: any = { process.env.GLOBAL_CLOUD_BUCKET_NAME || "prod-budi-tenant-uploads", USE_COUCH: process.env.USE_COUCH || true, DISABLE_DEVELOPER_LICENSE: process.env.DISABLE_DEVELOPER_LICENSE, - SERVICE: process.env.SERVICE || "budibase", DEFAULT_LICENSE: process.env.DEFAULT_LICENSE, + SERVICE: process.env.SERVICE || "budibase", + DEPLOYMENT_ENVIRONMENT: + process.env.DEPLOYMENT_ENVIRONMENT || "docker-compose", _set(key: any, value: any) { process.env[key] = value module.exports[key] = value diff --git a/packages/backend-core/src/events/backfill.ts b/packages/backend-core/src/events/backfill.ts index b4b7d826bf..7b73cc4f82 100644 --- a/packages/backend-core/src/events/backfill.ts +++ b/packages/backend-core/src/events/backfill.ts @@ -1,4 +1,3 @@ -import { UserPermissionAssignedEvent } from "./../../../types/src/events/user" import { Event, BackfillMetadata, @@ -18,7 +17,7 @@ import { AppPublishedEvent, UserCreatedEvent, RoleAssignedEvent, - RowsCreatedEvent, + UserPermissionAssignedEvent, } from "@budibase/types" import * as context from "../context" import { CacheKeys } from "../cache/generic" @@ -173,7 +172,7 @@ const getEventKey = (event?: Event, properties?: any) => { const custom = CUSTOM_PROPERTY_SUFFIX[event] const suffix = custom ? custom(properties) : undefined if (suffix) { - eventKey = `${event}:${suffix}` + eventKey = `${eventKey}:${suffix}` } } else { eventKey = `${CacheKeys.EVENTS}:${tenantId}:*` diff --git a/packages/backend-core/src/events/processors/PosthogProcessor.ts b/packages/backend-core/src/events/processors/PosthogProcessor.ts index efe4ed26c2..5b6a267569 100644 --- a/packages/backend-core/src/events/processors/PosthogProcessor.ts +++ b/packages/backend-core/src/events/processors/PosthogProcessor.ts @@ -23,6 +23,7 @@ export default class PosthogProcessor implements EventProcessor { ): Promise { properties.version = pkg.version properties.service = env.SERVICE + properties.environment = env.DEPLOYMENT_ENVIRONMENT const appId = context.getAppId() if (appId) { diff --git a/packages/server/Dockerfile b/packages/server/Dockerfile index f70fdbca95..54468abdee 100644 --- a/packages/server/Dockerfile +++ b/packages/server/Dockerfile @@ -10,6 +10,7 @@ WORKDIR /app ENV PORT=4001 ENV COUCH_DB_URL=https://couchdb.budi.live:5984 ENV BUDIBASE_ENVIRONMENT=PRODUCTION +ENV SERVICE=app-service # copy files and install dependencies COPY . ./ diff --git a/packages/server/scripts/dev/manage.js b/packages/server/scripts/dev/manage.js index 9c1f0eb775..b5cce1c58b 100644 --- a/packages/server/scripts/dev/manage.js +++ b/packages/server/scripts/dev/manage.js @@ -54,6 +54,8 @@ async function init() { DISABLE_ACCOUNT_PORTAL: "", MULTI_TENANCY: "", DISABLE_THREADING: 1, + SERVICE: "app-service", + DEPLOYMENT_ENVIRONMENT: "development", } let envFile = "" Object.keys(envFileJson).forEach(key => { diff --git a/packages/types/src/events/event.ts b/packages/types/src/events/event.ts index 5f61174b88..6c1a37e264 100644 --- a/packages/types/src/events/event.ts +++ b/packages/types/src/events/event.ts @@ -153,6 +153,7 @@ export enum Event { export interface BaseEvent { version?: string service?: string + environment?: string appId?: string installationId?: string tenantId?: string diff --git a/packages/worker/Dockerfile b/packages/worker/Dockerfile index 8d6abc6ad1..086c69fddc 100644 --- a/packages/worker/Dockerfile +++ b/packages/worker/Dockerfile @@ -21,4 +21,6 @@ EXPOSE 4001 # which are actually needed to get this environment up and running ENV NODE_ENV=production ENV CLUSTER_MODE=${CLUSTER_MODE} +ENV SERVICE=worker-service + CMD ["./docker_run.sh"] diff --git a/packages/worker/scripts/dev/manage.js b/packages/worker/scripts/dev/manage.js index bb7e41ce87..96f5c29af4 100644 --- a/packages/worker/scripts/dev/manage.js +++ b/packages/worker/scripts/dev/manage.js @@ -26,6 +26,8 @@ async function init() { ACCOUNT_PORTAL_API_KEY: "budibase", PLATFORM_URL: "http://localhost:10000", APPS_URL: "http://localhost:4001", + SERVICE: "worker-service", + DEPLOYMENT_ENVIRONMENT: "development", } let envFile = "" Object.keys(envFileJson).forEach(key => { From 7f26a60cc97b6d61eb3133003d1f481723185312 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 1 Jun 2022 14:44:12 +0100 Subject: [PATCH 53/75] Default to historical timestamp --- .../backend-core/src/events/publishers/automation.ts | 7 +++++-- .../backend-core/src/events/publishers/datasource.ts | 5 ++++- .../backend-core/src/events/publishers/layout.ts | 2 +- packages/backend-core/src/events/publishers/query.ts | 2 +- packages/backend-core/src/events/publishers/role.ts | 2 +- packages/backend-core/src/events/publishers/rows.ts | 2 +- .../backend-core/src/events/publishers/screen.ts | 2 +- packages/backend-core/src/events/publishers/table.ts | 2 +- packages/backend-core/src/events/publishers/view.ts | 9 ++++++--- .../server/src/migrations/functions/backfill/app.ts | 6 +++++- .../migrations/functions/backfill/app/automations.ts | 2 +- .../migrations/functions/backfill/app/datasources.ts | 2 +- .../src/migrations/functions/backfill/app/layouts.ts | 2 +- .../src/migrations/functions/backfill/app/queries.ts | 2 +- .../src/migrations/functions/backfill/app/roles.ts | 2 +- .../src/migrations/functions/backfill/app/screens.ts | 2 +- .../src/migrations/functions/backfill/app/tables.ts | 2 +- .../src/migrations/functions/backfill/global.ts | 12 ++++++++---- .../migrations/functions/backfill/global/quotas.ts | 11 +++++++++-- .../migrations/functions/backfill/global/users.ts | 6 +++++- .../src/migrations/functions/backfill/index.ts | 4 ++++ .../migrations/functions/backfill/installation.ts | 11 +++++++++-- 22 files changed, 68 insertions(+), 29 deletions(-) diff --git a/packages/backend-core/src/events/publishers/automation.ts b/packages/backend-core/src/events/publishers/automation.ts index e1df28ead8..95f9cb8db6 100644 --- a/packages/backend-core/src/events/publishers/automation.ts +++ b/packages/backend-core/src/events/publishers/automation.ts @@ -12,7 +12,10 @@ import { AutomationsRunEvent, } from "@budibase/types" -export async function created(automation: Automation, timestamp?: string) { +export async function created( + automation: Automation, + timestamp?: string | number +) { const properties: AutomationCreatedEvent = { appId: automation.appId, automationId: automation._id as string, @@ -62,7 +65,7 @@ export const run = async (count: number, timestamp?: string | number) => { export async function stepCreated( automation: Automation, step: AutomationStep, - timestamp?: string + timestamp?: string | number ) { const properties: AutomationStepCreatedEvent = { appId: automation.appId, diff --git a/packages/backend-core/src/events/publishers/datasource.ts b/packages/backend-core/src/events/publishers/datasource.ts index 4cff0c9a18..3cd68033fc 100644 --- a/packages/backend-core/src/events/publishers/datasource.ts +++ b/packages/backend-core/src/events/publishers/datasource.ts @@ -7,7 +7,10 @@ import { DatasourceDeletedEvent, } from "@budibase/types" -export async function created(datasource: Datasource, timestamp?: string) { +export async function created( + datasource: Datasource, + timestamp?: string | number +) { const properties: DatasourceCreatedEvent = { datasourceId: datasource._id as string, source: datasource.source, diff --git a/packages/backend-core/src/events/publishers/layout.ts b/packages/backend-core/src/events/publishers/layout.ts index 82e9f613ca..1eede40143 100644 --- a/packages/backend-core/src/events/publishers/layout.ts +++ b/packages/backend-core/src/events/publishers/layout.ts @@ -6,7 +6,7 @@ import { LayoutDeletedEvent, } from "@budibase/types" -export async function created(layout: Layout, timestamp?: string) { +export async function created(layout: Layout, timestamp?: string | number) { const properties: LayoutCreatedEvent = { layoutId: layout._id as string, } diff --git a/packages/backend-core/src/events/publishers/query.ts b/packages/backend-core/src/events/publishers/query.ts index c031885ac3..1bcf561fd0 100644 --- a/packages/backend-core/src/events/publishers/query.ts +++ b/packages/backend-core/src/events/publishers/query.ts @@ -16,7 +16,7 @@ import { export const created = async ( datasource: Datasource, query: Query, - timestamp?: string + timestamp?: string | number ) => { const properties: QueryCreatedEvent = { queryId: query._id as string, diff --git a/packages/backend-core/src/events/publishers/role.ts b/packages/backend-core/src/events/publishers/role.ts index 4cd5f91703..99074d76a5 100644 --- a/packages/backend-core/src/events/publishers/role.ts +++ b/packages/backend-core/src/events/publishers/role.ts @@ -10,7 +10,7 @@ import { User, } from "@budibase/types" -export async function created(role: Role, timestamp?: string) { +export async function created(role: Role, timestamp?: string | number) { const properties: RoleCreatedEvent = { roleId: role._id as string, permissionId: role.permissionId, diff --git a/packages/backend-core/src/events/publishers/rows.ts b/packages/backend-core/src/events/publishers/rows.ts index 9ff5b4d3d7..487e7ea074 100644 --- a/packages/backend-core/src/events/publishers/rows.ts +++ b/packages/backend-core/src/events/publishers/rows.ts @@ -9,7 +9,7 @@ import { /* eslint-disable */ -export const created = async (count: number, timestamp?: string) => { +export const created = async (count: number, timestamp?: string | number) => { const properties: RowsCreatedEvent = { count, } diff --git a/packages/backend-core/src/events/publishers/screen.ts b/packages/backend-core/src/events/publishers/screen.ts index cbd19327e6..966bf72e52 100644 --- a/packages/backend-core/src/events/publishers/screen.ts +++ b/packages/backend-core/src/events/publishers/screen.ts @@ -6,7 +6,7 @@ import { ScreenDeletedEvent, } from "@budibase/types" -export async function created(screen: Screen, timestamp?: string) { +export async function created(screen: Screen, timestamp?: string | number) { const properties: ScreenCreatedEvent = { layoutId: screen.layoutId, screenId: screen._id as string, diff --git a/packages/backend-core/src/events/publishers/table.ts b/packages/backend-core/src/events/publishers/table.ts index 9853be7eab..ba53163ca3 100644 --- a/packages/backend-core/src/events/publishers/table.ts +++ b/packages/backend-core/src/events/publishers/table.ts @@ -11,7 +11,7 @@ import { TableImportedEvent, } from "@budibase/types" -export async function created(table: Table, timestamp?: string) { +export async function created(table: Table, timestamp?: string | number) { const properties: TableCreatedEvent = { tableId: table._id as string, } diff --git a/packages/backend-core/src/events/publishers/view.ts b/packages/backend-core/src/events/publishers/view.ts index 1a382ec00e..17947758d5 100644 --- a/packages/backend-core/src/events/publishers/view.ts +++ b/packages/backend-core/src/events/publishers/view.ts @@ -19,7 +19,7 @@ import { /* eslint-disable */ -export async function created(view: View, timestamp?: string) { +export async function created(view: View, timestamp?: string | number) { const properties: ViewCreatedEvent = { tableId: view.tableId, } @@ -48,7 +48,7 @@ export async function exported(table: Table, format: TableExportFormat) { await publishEvent(Event.VIEW_EXPORTED, properties) } -export async function filterCreated(view: View, timestamp?: string) { +export async function filterCreated(view: View, timestamp?: string | number) { const properties: ViewFilterCreatedEvent = { tableId: view.tableId, } @@ -69,7 +69,10 @@ export async function filterDeleted(view: View) { await publishEvent(Event.VIEW_FILTER_DELETED, properties) } -export async function calculationCreated(view: View, timestamp?: string) { +export async function calculationCreated( + view: View, + timestamp?: string | number +) { const properties: ViewCalculationCreatedEvent = { tableId: view.tableId, calculation: view.calculation as ViewCalculation, diff --git a/packages/server/src/migrations/functions/backfill/app.ts b/packages/server/src/migrations/functions/backfill/app.ts index 3c562ecc1d..0f53e4d0ad 100644 --- a/packages/server/src/migrations/functions/backfill/app.ts +++ b/packages/server/src/migrations/functions/backfill/app.ts @@ -9,6 +9,7 @@ import * as global from "./global" import { App, AppBackfillSucceededEvent, Event } from "@budibase/types" import { db as dbUtils, events } from "@budibase/backend-core" import env from "../../../environment" +import { DEFAULT_TIMESTAMP } from "." const failGraceful = env.SELF_HOSTED && !env.isDev() @@ -59,8 +60,11 @@ export const run = async (appDb: any) => { // events for this tenant await events.backfillCache.start(EVENTS) + let timestamp: string | number = DEFAULT_TIMESTAMP const app: App = await appDb.get(dbUtils.DocumentTypes.APP_METADATA) - const timestamp = app.createdAt as string + if (app.createdAt) { + timestamp = app.createdAt as string + } if (dbUtils.isProdAppID(app.appId)) { await events.app.published(app, timestamp) diff --git a/packages/server/src/migrations/functions/backfill/app/automations.ts b/packages/server/src/migrations/functions/backfill/app/automations.ts index e4c3ad19f3..20da8fd3c0 100644 --- a/packages/server/src/migrations/functions/backfill/app/automations.ts +++ b/packages/server/src/migrations/functions/backfill/app/automations.ts @@ -11,7 +11,7 @@ const getAutomations = async (appDb: any): Promise => { return response.rows.map((row: any) => row.doc) } -export const backfill = async (appDb: any, timestamp: string) => { +export const backfill = async (appDb: any, timestamp: string | number) => { const automations = await getAutomations(appDb) for (const automation of automations) { diff --git a/packages/server/src/migrations/functions/backfill/app/datasources.ts b/packages/server/src/migrations/functions/backfill/app/datasources.ts index 1245e5d745..5d7e1ad866 100644 --- a/packages/server/src/migrations/functions/backfill/app/datasources.ts +++ b/packages/server/src/migrations/functions/backfill/app/datasources.ts @@ -11,7 +11,7 @@ const getDatasources = async (appDb: any): Promise => { return response.rows.map((row: any) => row.doc) } -export const backfill = async (appDb: any, timestamp: string) => { +export const backfill = async (appDb: any, timestamp: string | number) => { const datasources: Datasource[] = await getDatasources(appDb) for (const datasource of datasources) { diff --git a/packages/server/src/migrations/functions/backfill/app/layouts.ts b/packages/server/src/migrations/functions/backfill/app/layouts.ts index d2a551096d..ee5806459b 100644 --- a/packages/server/src/migrations/functions/backfill/app/layouts.ts +++ b/packages/server/src/migrations/functions/backfill/app/layouts.ts @@ -11,7 +11,7 @@ const getLayouts = async (appDb: any): Promise => { return response.rows.map((row: any) => row.doc) } -export const backfill = async (appDb: any, timestamp: string) => { +export const backfill = async (appDb: any, timestamp: string | number) => { const layouts: Layout[] = await getLayouts(appDb) for (const layout of layouts) { diff --git a/packages/server/src/migrations/functions/backfill/app/queries.ts b/packages/server/src/migrations/functions/backfill/app/queries.ts index fb0e561363..e07dec8c06 100644 --- a/packages/server/src/migrations/functions/backfill/app/queries.ts +++ b/packages/server/src/migrations/functions/backfill/app/queries.ts @@ -18,7 +18,7 @@ const getDatasource = async ( return appDb.get(datasourceId) } -export const backfill = async (appDb: any, timestamp: string) => { +export const backfill = async (appDb: any, timestamp: string | number) => { const queries: Query[] = await getQueries(appDb) for (const query of queries) { diff --git a/packages/server/src/migrations/functions/backfill/app/roles.ts b/packages/server/src/migrations/functions/backfill/app/roles.ts index 1c90b0f658..494b6f6923 100644 --- a/packages/server/src/migrations/functions/backfill/app/roles.ts +++ b/packages/server/src/migrations/functions/backfill/app/roles.ts @@ -11,7 +11,7 @@ const getRoles = async (appDb: any): Promise => { return response.rows.map((row: any) => row.doc) } -export const backfill = async (appDb: any, timestamp: string) => { +export const backfill = async (appDb: any, timestamp: string | number) => { const roles = await getRoles(appDb) for (const role of roles) { diff --git a/packages/server/src/migrations/functions/backfill/app/screens.ts b/packages/server/src/migrations/functions/backfill/app/screens.ts index a17be76ea3..ab3b4b9d3c 100644 --- a/packages/server/src/migrations/functions/backfill/app/screens.ts +++ b/packages/server/src/migrations/functions/backfill/app/screens.ts @@ -11,7 +11,7 @@ const getScreens = async (appDb: any): Promise => { return response.rows.map((row: any) => row.doc) } -export const backfill = async (appDb: any, timestamp: string) => { +export const backfill = async (appDb: any, timestamp: string | number) => { const screens = await getScreens(appDb) for (const screen of screens) { diff --git a/packages/server/src/migrations/functions/backfill/app/tables.ts b/packages/server/src/migrations/functions/backfill/app/tables.ts index 484a179bbe..150a3b4d4a 100644 --- a/packages/server/src/migrations/functions/backfill/app/tables.ts +++ b/packages/server/src/migrations/functions/backfill/app/tables.ts @@ -11,7 +11,7 @@ const getTables = async (appDb: any): Promise => { return response.rows.map((row: any) => row.doc) } -export const backfill = async (appDb: any, timestamp: string) => { +export const backfill = async (appDb: any, timestamp: string | number) => { const tables = await getTables(appDb) for (const table of tables) { diff --git a/packages/server/src/migrations/functions/backfill/global.ts b/packages/server/src/migrations/functions/backfill/global.ts index 8b58b15c7e..cac38b8066 100644 --- a/packages/server/src/migrations/functions/backfill/global.ts +++ b/packages/server/src/migrations/functions/backfill/global.ts @@ -16,6 +16,7 @@ import { Event, } from "@budibase/types" import env from "../../../environment" +import { DEFAULT_TIMESTAMP } from "." const failGraceful = env.SELF_HOSTED && !env.isDev() @@ -88,13 +89,16 @@ const EVENTS = [ export const run = async (db: any) => { try { const tenantId = tenancy.getTenantId() - let installTimestamp + let timestamp: string | number = DEFAULT_TIMESTAMP const totals: any = {} const errors: any = [] try { - installTimestamp = await getInstallTimestamp(db) + const installTimestamp = await getInstallTimestamp(db) + if (installTimestamp) { + timestamp = installTimestamp + } } catch (e) { handleError(e, errors) } @@ -108,7 +112,7 @@ export const run = async (db: any) => { await events.identification.identifyTenantGroup( tenantId, account, - installTimestamp + timestamp ) } catch (e) { handleError(e, errors) @@ -119,7 +123,7 @@ export const run = async (db: any) => { await events.backfillCache.start(EVENTS) try { - await configs.backfill(db, installTimestamp) + await configs.backfill(db, timestamp) } catch (e) { handleError(e, errors) } diff --git a/packages/server/src/migrations/functions/backfill/global/quotas.ts b/packages/server/src/migrations/functions/backfill/global/quotas.ts index 274804adad..505274a318 100644 --- a/packages/server/src/migrations/functions/backfill/global/quotas.ts +++ b/packages/server/src/migrations/functions/backfill/global/quotas.ts @@ -1,3 +1,4 @@ +import { DEFAULT_TIMESTAMP } from "./../index" import { events } from "@budibase/backend-core" import { quotas } from "@budibase/pro" import { App } from "@budibase/types" @@ -33,8 +34,14 @@ export const backfill = async (allApps: App[]) => { const usage = await quotas.getQuotaUsage() const rows = usage.usageQuota.rows - const rowsTimestamp = getOldestCreatedAt(allApps) - await events.rows.created(rows, rowsTimestamp) + let timestamp: string | number = DEFAULT_TIMESTAMP + + const oldestAppTimestamp = getOldestCreatedAt(allApps) + if (oldestAppTimestamp) { + timestamp = oldestAppTimestamp + } + + await events.rows.created(rows, timestamp) for (const [monthString, quotas] of Object.entries(usage.monthly)) { if (monthString === "current") { diff --git a/packages/server/src/migrations/functions/backfill/global/users.ts b/packages/server/src/migrations/functions/backfill/global/users.ts index 0a59bec630..5f811614b4 100644 --- a/packages/server/src/migrations/functions/backfill/global/users.ts +++ b/packages/server/src/migrations/functions/backfill/global/users.ts @@ -1,5 +1,6 @@ import { events, db as dbUtils } from "@budibase/backend-core" import { User, CloudAccount } from "@budibase/types" +import { DEFAULT_TIMESTAMP } from ".." // manually define user doc params - normally server doesn't read users from the db const getUserParams = (props: any) => { @@ -22,7 +23,10 @@ export const backfill = async ( const users = await getUsers(globalDb) for (const user of users) { - const timestamp = user.createdAt as number + let timestamp: string | number = DEFAULT_TIMESTAMP + if (user.createdAt) { + timestamp = user.createdAt + } await events.identification.identifyUser(user, account, timestamp) await events.user.created(user, timestamp) diff --git a/packages/server/src/migrations/functions/backfill/index.ts b/packages/server/src/migrations/functions/backfill/index.ts index dee9b82e1a..00c04722b4 100644 --- a/packages/server/src/migrations/functions/backfill/index.ts +++ b/packages/server/src/migrations/functions/backfill/index.ts @@ -1,3 +1,7 @@ export * as app from "./app" export * as global from "./global" export * as installation from "./installation" + +// historical events are free in posthog - make sure we default to a +// historical time if no other can be found +export const DEFAULT_TIMESTAMP = new Date(2022, 0, 1).getTime() diff --git a/packages/server/src/migrations/functions/backfill/installation.ts b/packages/server/src/migrations/functions/backfill/installation.ts index 425ec80f0d..430fe2edd1 100644 --- a/packages/server/src/migrations/functions/backfill/installation.ts +++ b/packages/server/src/migrations/functions/backfill/installation.ts @@ -1,3 +1,4 @@ +import { DEFAULT_TIMESTAMP } from "./index" import { events, tenancy, installation } from "@budibase/backend-core" import { Installation } from "@budibase/types" import * as global from "./global" @@ -28,11 +29,17 @@ export const run = async () => { // need to use the default tenant to try to get the installation time await tenancy.doInTenant(tenancy.DEFAULT_TENANT_ID, async () => { const db = tenancy.getGlobalDB() - const installTimestamp = (await global.getInstallTimestamp(db)) as number + let timestamp: string | number = DEFAULT_TIMESTAMP + + const installTimestamp = await global.getInstallTimestamp(db) + if (installTimestamp) { + timestamp = installTimestamp + } + const install: Installation = await installation.getInstall() await events.identification.identifyInstallationGroup( install.installId, - installTimestamp + timestamp ) }) await events.backfill.installationSucceeded() From 4f34e1b2c73bb51bffaf843ff8ba5a6ce1f9235d Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 1 Jun 2022 15:17:49 +0100 Subject: [PATCH 54/75] app id fixes --- packages/backend-core/src/events/backfill.ts | 3 ++- packages/backend-core/src/migrations/index.js | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/backend-core/src/events/backfill.ts b/packages/backend-core/src/events/backfill.ts index 7b73cc4f82..e4577c5ab4 100644 --- a/packages/backend-core/src/events/backfill.ts +++ b/packages/backend-core/src/events/backfill.ts @@ -18,6 +18,7 @@ import { UserCreatedEvent, RoleAssignedEvent, UserPermissionAssignedEvent, + AppCreatedEvent, } from "@budibase/types" import * as context from "../context" import { CacheKeys } from "../cache/generic" @@ -130,7 +131,7 @@ const CUSTOM_PROPERTY_SUFFIX: any = { [Event.VIEW_FILTER_CREATED]: (properties: ViewFilterCreatedEvent) => { return properties.tableId // best uniqueness }, - [Event.APP_PUBLISHED]: (properties: AppPublishedEvent) => { + [Event.APP_CREATED]: (properties: AppCreatedEvent) => { return properties.appId // best uniqueness }, [Event.APP_PUBLISHED]: (properties: AppPublishedEvent) => { diff --git a/packages/backend-core/src/migrations/index.js b/packages/backend-core/src/migrations/index.js index 6bfedf03c5..11067d4cb0 100644 --- a/packages/backend-core/src/migrations/index.js +++ b/packages/backend-core/src/migrations/index.js @@ -9,6 +9,7 @@ const { getGlobalDBName, getTenantId, } = require("../tenancy") +const context = require("../context") exports.MIGRATION_TYPES = { GLOBAL: "global", // run once per tenant, recorded in global db, global db is provided as an argument @@ -90,7 +91,14 @@ exports.runMigration = async (migration, options = {}) => { `[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Running ${lengthStatement}` ) // run the migration with tenant context - await migration.fn(db) + if (migrationType === exports.MIGRATION_TYPES.APP) { + await context.doInAppContext(db.name, async () => { + await migration.fn(db) + }) + } else { + await migration.fn(db) + } + log( `[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Complete` ) From 081db8423e46a48e9f411c14c5c32506bc815e10 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 1 Jun 2022 15:31:27 +0100 Subject: [PATCH 55/75] Fix key eviction --- packages/backend-core/src/redis/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/backend-core/src/redis/index.js b/packages/backend-core/src/redis/index.js index 65d1847eb7..38f7130a4c 100644 --- a/packages/backend-core/src/redis/index.js +++ b/packages/backend-core/src/redis/index.js @@ -165,7 +165,8 @@ class RedisWrapper { } async keys(pattern) { - return CLIENT.keys(pattern) + const db = this._db + return CLIENT.keys(addDbPrefix(db, pattern)) } async get(key) { From 86d094dda4e2dd999d988d058bfba969b9e387c9 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Wed, 1 Jun 2022 17:52:41 +0100 Subject: [PATCH 56/75] Migration locks and add optional preventRetry option --- packages/backend-core/package.json | 2 + packages/backend-core/redis.js | 1 + packages/backend-core/src/migrations/index.js | 9 ++++ packages/backend-core/src/redis/authRedis.js | 16 +++++++ packages/backend-core/src/redis/index.js | 4 ++ packages/backend-core/src/redis/redlock.ts | 21 +++++++++ packages/backend-core/yarn.lock | 39 ++++++++++++--- packages/server/src/migrations/index.ts | 47 ++++++++++++++++++- 8 files changed, 130 insertions(+), 9 deletions(-) create mode 100644 packages/backend-core/src/redis/redlock.ts diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 6c5063eb01..2cd53537cd 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -19,6 +19,7 @@ "dotenv": "^16.0.1", "emitter-listener": "^1.1.2", "ioredis": "^4.27.1", + "redlock": "^4.0.0", "jsonwebtoken": "^8.5.1", "koa-passport": "^4.1.4", "lodash": "^4.17.21", @@ -55,6 +56,7 @@ "@types/tar-fs": "^2.0.1", "@types/uuid": "^8.3.4", "@types/semver": "^7.0.0", + "@types/redlock": "^4.0.0", "ioredis-mock": "^5.5.5", "jest": "^27.0.3", "koa": "2.7.0", diff --git a/packages/backend-core/redis.js b/packages/backend-core/redis.js index 0a9dc91881..badce3be20 100644 --- a/packages/backend-core/redis.js +++ b/packages/backend-core/redis.js @@ -1,4 +1,5 @@ module.exports = { Client: require("./src/redis"), utils: require("./src/redis/utils"), + clients: require("./src/redis/authRedis"), } diff --git a/packages/backend-core/src/migrations/index.js b/packages/backend-core/src/migrations/index.js index 11067d4cb0..5c3ab4f4a5 100644 --- a/packages/backend-core/src/migrations/index.js +++ b/packages/backend-core/src/migrations/index.js @@ -90,6 +90,15 @@ exports.runMigration = async (migration, options = {}) => { log( `[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Running ${lengthStatement}` ) + + if (migration.preventRetry) { + // eagerly set the completion date + // so that we never run this migration twice even upon failure + doc[migrationName] = Date.now() + const response = await db.put(doc) + doc._rev = response.rev + } + // run the migration with tenant context if (migrationType === exports.MIGRATION_TYPES.APP) { await context.doInAppContext(db.name, async () => { diff --git a/packages/backend-core/src/redis/authRedis.js b/packages/backend-core/src/redis/authRedis.js index b9f6d8d0b0..b2f686b801 100644 --- a/packages/backend-core/src/redis/authRedis.js +++ b/packages/backend-core/src/redis/authRedis.js @@ -1,13 +1,23 @@ const Client = require("./index") const utils = require("./utils") +const { getRedlock } = require("./redlock") let userClient, sessionClient, appClient, cacheClient +let migrationsRedlock + +// turn retry off so that only one instance can ever hold the lock +const migrationsRedlockConfig = { retryCount: 0 } async function init() { userClient = await new Client(utils.Databases.USER_CACHE).init() sessionClient = await new Client(utils.Databases.SESSIONS).init() appClient = await new Client(utils.Databases.APP_METADATA).init() cacheClient = await new Client(utils.Databases.GENERIC_CACHE).init() + // pass the underlying ioredis client to redlock + migrationsRedlock = getRedlock( + cacheClient.getClient(), + migrationsRedlockConfig + ) } process.on("exit", async () => { @@ -42,4 +52,10 @@ module.exports = { } return cacheClient }, + getMigrationsRedlock: async () => { + if (!migrationsRedlock) { + await init() + } + return migrationsRedlock + }, } diff --git a/packages/backend-core/src/redis/index.js b/packages/backend-core/src/redis/index.js index 38f7130a4c..32fce94ba5 100644 --- a/packages/backend-core/src/redis/index.js +++ b/packages/backend-core/src/redis/index.js @@ -139,6 +139,10 @@ class RedisWrapper { this._db = db } + getClient() { + return CLIENT + } + async init() { CLOSED = false init() diff --git a/packages/backend-core/src/redis/redlock.ts b/packages/backend-core/src/redis/redlock.ts new file mode 100644 index 0000000000..beef375b55 --- /dev/null +++ b/packages/backend-core/src/redis/redlock.ts @@ -0,0 +1,21 @@ +import Redlock from "redlock" + +export const getRedlock = (redisClient: any, opts = { retryCount: 10 }) => { + return new Redlock([redisClient], { + // the expected clock drift; for more details + // see http://redis.io/topics/distlock + driftFactor: 0.01, // multiplied by lock ttl to determine drift time + + // the max number of times Redlock will attempt + // to lock a resource before erroring + retryCount: opts.retryCount, + + // the time in ms between attempts + retryDelay: 200, // time in ms + + // the max time in ms randomly added to retries + // to improve performance under high contention + // see https://www.awsarchitectureblog.com/2015/03/backoff.html + retryJitter: 200, // time in ms + }) +} diff --git a/packages/backend-core/yarn.lock b/packages/backend-core/yarn.lock index 98c82da865..013bd6062f 100644 --- a/packages/backend-core/yarn.lock +++ b/packages/backend-core/yarn.lock @@ -829,6 +829,11 @@ dependencies: "@babel/types" "^7.3.0" +"@types/bluebird@*": + version "3.5.36" + resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.36.tgz#00d9301d4dc35c2f6465a8aec634bb533674c652" + integrity sha512-HBNx4lhkxN7bx6P0++W8E289foSu8kO8GCk2unhuVggO+cE7rh9DhZUyPhUxNRG9m+5B5BTKxZQ5ZP92x/mx9Q== + "@types/body-parser@*": version "1.19.2" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" @@ -895,13 +900,6 @@ resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-1.8.2.tgz#7315b4c4c54f82d13fa61c228ec5c2ea5cc9e0e1" integrity sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w== -"@types/ioredis@^4.27.1": - version "4.28.10" - resolved "https://registry.yarnpkg.com/@types/ioredis/-/ioredis-4.28.10.tgz#40ceb157a4141088d1394bb87c98ed09a75a06ff" - integrity sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ== - dependencies: - "@types/node" "*" - "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" @@ -993,6 +991,21 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== +"@types/redis@^2.8.0": + version "2.8.32" + resolved "https://registry.yarnpkg.com/@types/redis/-/redis-2.8.32.tgz#1d3430219afbee10f8cfa389dad2571a05ecfb11" + integrity sha512-7jkMKxcGq9p242exlbsVzuJb57KqHRhNl4dHoQu2Y5v9bCAbtIXXH0R3HleSQW4CTOqpHIYUW3t6tpUj4BVQ+w== + dependencies: + "@types/node" "*" + +"@types/redlock@^4.0.0": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/redlock/-/redlock-4.0.3.tgz#aeab5fe5f0d433a125f6dcf9a884372ac0cddd4b" + integrity sha512-mcvvrquwREbAqyZALNBIlf49AL9Aa324BG+J/Dv4TAP8g+nxQMBI4/APNqqS99QEY7VTNT9XvsaczCVGK8uNnQ== + dependencies: + "@types/bluebird" "*" + "@types/redis" "^2.8.0" + "@types/semver@^7.0.0": version "7.3.9" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.9.tgz#152c6c20a7688c30b967ec1841d31ace569863fc" @@ -1397,6 +1410,11 @@ bl@^4.0.3: inherits "^2.0.4" readable-stream "^3.4.0" +bluebird@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + boom@2.x.x: version "2.10.1" resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" @@ -4835,6 +4853,13 @@ redis-parser@^3.0.0: dependencies: redis-errors "^1.0.0" +redlock@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/redlock/-/redlock-4.2.0.tgz#c26590768559afd5fff76aa1133c94b411ff4f5f" + integrity sha512-j+oQlG+dOwcetUt2WJWttu4CZVeRzUrcVcISFmEmfyuwCVSJ93rDT7YSgg7H7rnxwoRyk/jU46kycVka5tW7jA== + dependencies: + bluebird "^3.7.2" + regenerator-runtime@^0.13.4: version "0.13.9" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" diff --git a/packages/server/src/migrations/index.ts b/packages/server/src/migrations/index.ts index 1418a05f18..c93f628c71 100644 --- a/packages/server/src/migrations/index.ts +++ b/packages/server/src/migrations/index.ts @@ -1,4 +1,4 @@ -import { migrations } from "@budibase/backend-core" +import { migrations, redis } from "@budibase/backend-core" // migration functions import * as userEmailViewCasing from "./functions/userEmailViewCasing" @@ -15,6 +15,7 @@ export interface Migration { opts?: object fn: Function silent?: boolean + preventRetry?: boolean } /** @@ -66,21 +67,63 @@ export const MIGRATIONS: Migration[] = [ opts: { all: true }, fn: backfill.app.run, silent: !!env.SELF_HOSTED, // reduce noisy logging + preventRetry: !!env.SELF_HOSTED, // only ever run once }, { type: migrations.MIGRATION_TYPES.GLOBAL, name: "event_global_backfill", fn: backfill.global.run, silent: !!env.SELF_HOSTED, // reduce noisy logging + preventRetry: !!env.SELF_HOSTED, // only ever run once }, { type: migrations.MIGRATION_TYPES.INSTALLATION, name: "event_installation_backfill", fn: backfill.installation.run, silent: !!env.SELF_HOSTED, // reduce noisy logging + preventRetry: !!env.SELF_HOSTED, // only ever run once }, ] export const migrate = async (options?: MigrationOptions) => { - await migrations.runMigrations(MIGRATIONS, options) + if (env.SELF_HOSTED) { + // self host runs migrations on startup + // make sure only a single instance runs them + await migrateWithLock(options) + } else { + await migrations.runMigrations(MIGRATIONS, options) + } +} + +const migrateWithLock = async (options?: MigrationOptions) => { + // get a new lock client + + const redlock = await redis.clients.getMigrationsRedlock() + // lock for 15 minutes + const ttl = 1000 * 60 * 15 + + let migrationLock + + // acquire lock + try { + migrationLock = await redlock.lock("migrations", ttl) + } catch (e: any) { + if (e.name === "LockError") { + return + } else { + throw e + } + } + + // run migrations + try { + await migrations.runMigrations(MIGRATIONS, options) + } finally { + // release lock + try { + await migrationLock.unlock() + } catch (e) { + console.error("unable to release migration lock") + } + } } From 33f44f6ee7a3e0cb397fdc7cd63800fe13918e8e Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Mon, 6 Jun 2022 11:11:13 +0100 Subject: [PATCH 57/75] Remove todos --- packages/builder/src/analytics/constants.js | 2 +- packages/builder/src/analytics/index.js | 2 +- packages/builder/src/api.js | 3 --- packages/builder/src/builderStore/datasource.js | 1 - packages/builder/src/builderStore/store/automation/index.js | 1 - packages/builder/src/builderStore/store/frontend.js | 1 - .../automation/AutomationPanel/CreateAutomationModal.svelte | 1 - .../automation/AutomationPanel/UpdateAutomationModal.svelte | 1 - .../components/backend/DataTable/modals/CalculateModal.svelte | 1 - .../components/backend/DataTable/modals/CreateViewModal.svelte | 1 - .../src/components/backend/DataTable/modals/FilterModal.svelte | 1 - .../DatasourceNavigator/modals/ImportRestQueriesModal.svelte | 1 - .../DatasourceNavigator/modals/UpdateDatasourceModal.svelte | 1 - .../backend/TableNavigator/modals/CreateTableModal.svelte | 1 - packages/builder/src/components/deploy/DeployModal.svelte | 1 - .../ComponentNavigationTree/ScreenDropdownMenu.svelte | 1 - .../src/components/design/NavigationPanel/ScreenWizard.svelte | 1 - packages/builder/src/components/start/CreateAppModal.svelte | 1 - packages/builder/src/pages/builder/portal/apps/index.svelte | 1 - .../builder/src/pages/builder/portal/manage/email/index.svelte | 1 - .../portal/manage/users/_components/AddUserModal.svelte | 1 - packages/builder/src/stores/portal/auth.js | 1 - packages/cli/src/analytics/Client.js | 1 - packages/cli/src/analytics/index.js | 2 -- packages/cli/src/constants.js | 1 - packages/cli/src/hosting/index.js | 1 - packages/frontend-core/src/api/analytics.js | 1 - 27 files changed, 2 insertions(+), 30 deletions(-) diff --git a/packages/builder/src/analytics/constants.js b/packages/builder/src/analytics/constants.js index 4e7ecc6535..d800ae2c3f 100644 --- a/packages/builder/src/analytics/constants.js +++ b/packages/builder/src/analytics/constants.js @@ -1,5 +1,5 @@ export const Events = { - // + // TODO: Remove most UI events BUILDER: { STARTED: "Builder Started", }, diff --git a/packages/builder/src/analytics/index.js b/packages/builder/src/analytics/index.js index 1283bce649..1b03b0855e 100644 --- a/packages/builder/src/analytics/index.js +++ b/packages/builder/src/analytics/index.js @@ -11,7 +11,7 @@ const posthog = new PosthogClient( const sentry = new SentryClient(process.env.SENTRY_DSN) const intercom = new IntercomClient(process.env.INTERCOM_TOKEN) -// +// TODO: Remove most UI events class AnalyticsHub { constructor() { this.clients = [posthog, sentry, intercom] diff --git a/packages/builder/src/api.js b/packages/builder/src/api.js index 808f434f1e..0f61dba4e1 100644 --- a/packages/builder/src/api.js +++ b/packages/builder/src/api.js @@ -22,9 +22,6 @@ export const API = createAPIClient({ onError: error => { const { url, message, status, method, handled } = error || {} - // Log all API errors to Sentry - // todo: move to api - // analytics.captureException(error) // Log any errors that we haven't manually handled if (!handled) { console.error("Unhandled error from API client", error) diff --git a/packages/builder/src/builderStore/datasource.js b/packages/builder/src/builderStore/datasource.js index cae0116d54..cfdeeac23e 100644 --- a/packages/builder/src/builderStore/datasource.js +++ b/packages/builder/src/builderStore/datasource.js @@ -31,7 +31,6 @@ export async function saveDatasource(config, skipFetch = false) { // update the tables incase data source plus await tables.fetch() await datasources.select(resp._id) - // todo: move to api analytics.captureEvent(Events.DATASOURCE.CREATED, { name: resp.name, source: resp.source, diff --git a/packages/builder/src/builderStore/store/automation/index.js b/packages/builder/src/builderStore/store/automation/index.js index 2b57f212d4..5252d4b06a 100644 --- a/packages/builder/src/builderStore/store/automation/index.js +++ b/packages/builder/src/builderStore/store/automation/index.js @@ -125,7 +125,6 @@ const automationActions = store => ({ state.selectedBlock = newBlock return state }) - // todo: move to api analytics.captureEvent(Events.AUTOMATION.BLOCK_ADDED, { name: block.name, }) diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index 836e4c3d24..3ffc890c7d 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -426,7 +426,6 @@ export const getFrontendStore = () => { }) // Log event - // todo: move to api analytics.captureEvent(Events.COMPONENT.CREATED, { name: componentInstance._component, }) diff --git a/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte b/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte index abf43a4691..4fb912939a 100644 --- a/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte +++ b/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte @@ -47,7 +47,6 @@ notifications.success(`Automation ${name} created`) $goto(`./${$automationStore.selectedAutomation.automation._id}`) - // todo: move to api analytics.captureEvent(Events.AUTOMATION.CREATED, { name }) } catch (error) { notifications.error("Error creating automation") diff --git a/packages/builder/src/components/automation/AutomationPanel/UpdateAutomationModal.svelte b/packages/builder/src/components/automation/AutomationPanel/UpdateAutomationModal.svelte index 16e981657c..bceff0ef2e 100644 --- a/packages/builder/src/components/automation/AutomationPanel/UpdateAutomationModal.svelte +++ b/packages/builder/src/components/automation/AutomationPanel/UpdateAutomationModal.svelte @@ -27,7 +27,6 @@ } await automationStore.actions.save(updatedAutomation) notifications.success(`Automation ${name} updated successfully`) - // todo: move to api analytics.captureEvent(Events.AUTOMATION.SAVED, { name }) hide() } catch (error) { diff --git a/packages/builder/src/components/backend/DataTable/modals/CalculateModal.svelte b/packages/builder/src/components/backend/DataTable/modals/CalculateModal.svelte index 852b91a7a0..81f54032f6 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CalculateModal.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CalculateModal.svelte @@ -41,7 +41,6 @@ try { views.save(view) notifications.success(`View ${view.name} saved`) - // todo: move to api analytics.captureEvent(Events.VIEW.ADDED_CALCULATE, { field: view.field }) } catch (error) { notifications.error("Error saving view") diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateViewModal.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateViewModal.svelte index 9ce7a283bf..2ea0c1b63b 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateViewModal.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateViewModal.svelte @@ -22,7 +22,6 @@ field, }) notifications.success(`View ${name} created`) - // todo: move to api analytics.captureEvent(Events.VIEW.CREATED, { name }) $goto(`../../view/${name}`) } catch (error) { diff --git a/packages/builder/src/components/backend/DataTable/modals/FilterModal.svelte b/packages/builder/src/components/backend/DataTable/modals/FilterModal.svelte index 2bfd6f0726..d7976cd387 100644 --- a/packages/builder/src/components/backend/DataTable/modals/FilterModal.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/FilterModal.svelte @@ -75,7 +75,6 @@ try { views.save(view) notifications.success(`View ${view.name} saved`) - // todo: move to api analytics.captureEvent(Events.VIEW.ADDED_FILTER, { filters: JSON.stringify(view.filters), }) diff --git a/packages/builder/src/components/backend/DatasourceNavigator/modals/ImportRestQueriesModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/modals/ImportRestQueriesModal.svelte index 292325edfa..cbf4274690 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/modals/ImportRestQueriesModal.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/modals/ImportRestQueriesModal.svelte @@ -72,7 +72,6 @@ } notifications.success(`Imported successfully.`) - // todo: move to api analytics.captureEvent(Events.QUERIES.REST.IMPORTED, { importType: lastTouched, newDatasource: createDatasource, diff --git a/packages/builder/src/components/backend/DatasourceNavigator/modals/UpdateDatasourceModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/modals/UpdateDatasourceModal.svelte index 8dd39200d6..28625aa86e 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/modals/UpdateDatasourceModal.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/modals/UpdateDatasourceModal.svelte @@ -35,7 +35,6 @@ } await datasources.save(updatedDatasource) notifications.success(`Datasource ${name} updated successfully.`) - // todo: move to api analytics.captureEvent(Events.DATASOURCE.UPDATED, updatedDatasource) hide() } diff --git a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte index 5c96445c4c..7830fd0246 100644 --- a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte +++ b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte @@ -59,7 +59,6 @@ try { table = await tables.save(newTable) notifications.success(`Table ${name} created successfully.`) - // todo: move to api analytics.captureEvent(Events.TABLE.CREATED, { name }) // Navigate to new table diff --git a/packages/builder/src/components/deploy/DeployModal.svelte b/packages/builder/src/components/deploy/DeployModal.svelte index 0018d644ac..0d9bfb7539 100644 --- a/packages/builder/src/components/deploy/DeployModal.svelte +++ b/packages/builder/src/components/deploy/DeployModal.svelte @@ -42,7 +42,6 @@ asyncModal.hide() publishCompleteModal.show() } catch (error) { - // todo: move to api analytics.captureException(error) notifications.error("Error publishing app") } diff --git a/packages/builder/src/components/design/NavigationPanel/ComponentNavigationTree/ScreenDropdownMenu.svelte b/packages/builder/src/components/design/NavigationPanel/ComponentNavigationTree/ScreenDropdownMenu.svelte index 65f8309cda..76bb48a26c 100644 --- a/packages/builder/src/components/design/NavigationPanel/ComponentNavigationTree/ScreenDropdownMenu.svelte +++ b/packages/builder/src/components/design/NavigationPanel/ComponentNavigationTree/ScreenDropdownMenu.svelte @@ -43,7 +43,6 @@ // Analytics if (screen.template) { - // todo: move to api analytics.captureEvent(Events.SCREEN.CREATED, { template: "createFromScratch", }) diff --git a/packages/builder/src/components/design/NavigationPanel/ScreenWizard.svelte b/packages/builder/src/components/design/NavigationPanel/ScreenWizard.svelte index 5606c41ade..1859ba93b6 100644 --- a/packages/builder/src/components/design/NavigationPanel/ScreenWizard.svelte +++ b/packages/builder/src/components/design/NavigationPanel/ScreenWizard.svelte @@ -67,7 +67,6 @@ // Analytics if (screen.template) { - // todo: move to api analytics.captureEvent(Events.SCREEN.CREATED, { template: screen.template, datasource: screen.datasource, diff --git a/packages/builder/src/components/start/CreateAppModal.svelte b/packages/builder/src/components/start/CreateAppModal.svelte index 05cb3b42bb..c88c2fe035 100644 --- a/packages/builder/src/components/start/CreateAppModal.svelte +++ b/packages/builder/src/components/start/CreateAppModal.svelte @@ -103,7 +103,6 @@ // Create App const createdApp = await API.createApp(data) - // todo: move to api analytics.captureEvent(Events.APP.CREATED, { name: $values.name, appId: createdApp.instance._id, diff --git a/packages/builder/src/pages/builder/portal/apps/index.svelte b/packages/builder/src/pages/builder/portal/apps/index.svelte index 946ae03446..2685881973 100644 --- a/packages/builder/src/pages/builder/portal/apps/index.svelte +++ b/packages/builder/src/pages/builder/portal/apps/index.svelte @@ -138,7 +138,6 @@ // Create App const createdApp = await API.createApp(data) - // todo: move to api analytics.captureEvent(Events.APP.CREATED, { name: appName, appId: createdApp.instance._id, diff --git a/packages/builder/src/pages/builder/portal/manage/email/index.svelte b/packages/builder/src/pages/builder/portal/manage/email/index.svelte index 1a7a71f171..3e5b35db41 100644 --- a/packages/builder/src/pages/builder/portal/manage/email/index.svelte +++ b/packages/builder/src/pages/builder/portal/manage/email/index.svelte @@ -60,7 +60,6 @@ smtpConfig._id = savedConfig._id await admin.getChecklist() notifications.success(`Settings saved`) - // todo: move to api analytics.captureEvent(Events.SMTP.SAVED) } catch (error) { notifications.error( diff --git a/packages/builder/src/pages/builder/portal/manage/users/_components/AddUserModal.svelte b/packages/builder/src/pages/builder/portal/manage/users/_components/AddUserModal.svelte index 6086c3b21b..0255784a7b 100644 --- a/packages/builder/src/pages/builder/portal/manage/users/_components/AddUserModal.svelte +++ b/packages/builder/src/pages/builder/portal/manage/users/_components/AddUserModal.svelte @@ -24,7 +24,6 @@ try { const res = await users.invite({ email: $email, builder, admin }) notifications.success(res.message) - // todo: move to api analytics.captureEvent(Events.USER.INVITE, { type: selected }) } catch (error) { notifications.error("Error inviting user") diff --git a/packages/builder/src/stores/portal/auth.js b/packages/builder/src/stores/portal/auth.js index b346b8d330..4a90323144 100644 --- a/packages/builder/src/stores/portal/auth.js +++ b/packages/builder/src/stores/portal/auth.js @@ -57,7 +57,6 @@ export function createAuthStore() { analytics .activate() .then(() => { - // todo: move to api analytics.identify(user._id, user) analytics.showChat({ email: user.email, diff --git a/packages/cli/src/analytics/Client.js b/packages/cli/src/analytics/Client.js index cdf345198e..a3b43b5ea1 100644 --- a/packages/cli/src/analytics/Client.js +++ b/packages/cli/src/analytics/Client.js @@ -6,7 +6,6 @@ const { } = require("../constants") const ConfigManager = require("../structures/ConfigManager") -// use backend core class AnalyticsClient { constructor() { this.client = new PostHog(BUDIBASE_POSTHOG_TOKEN, { diff --git a/packages/cli/src/analytics/index.js b/packages/cli/src/analytics/index.js index 514cbf60fd..d85323d1d0 100644 --- a/packages/cli/src/analytics/index.js +++ b/packages/cli/src/analytics/index.js @@ -3,8 +3,6 @@ const { CommandWords } = require("../constants") const { success, error } = require("../utils") const AnalyticsClient = require("./Client") -// use backend core - const client = new AnalyticsClient() async function optOut() { diff --git a/packages/cli/src/constants.js b/packages/cli/src/constants.js index 0fdd7560c7..7a3b1463ca 100644 --- a/packages/cli/src/constants.js +++ b/packages/cli/src/constants.js @@ -9,7 +9,6 @@ exports.InitTypes = { DIGITAL_OCEAN: "do", } -// todo: use backend core exports.AnalyticsEvents = { OptOut: "analytics_opt_out", OptIn: "analytics_opt_in", diff --git a/packages/cli/src/hosting/index.js b/packages/cli/src/hosting/index.js index 839dc7e6f2..2ebcee43a5 100644 --- a/packages/cli/src/hosting/index.js +++ b/packages/cli/src/hosting/index.js @@ -15,7 +15,6 @@ const makeEnv = require("./makeEnv") const axios = require("axios") const AnalyticsClient = require("../analytics/Client") -// todo: use backend core const BUDIBASE_SERVICES = ["app-service", "worker-service", "proxy-service"] const ERROR_FILE = "docker-error.log" const FILE_URLS = [ diff --git a/packages/frontend-core/src/api/analytics.js b/packages/frontend-core/src/api/analytics.js index 1a174b2edd..5aa35469ae 100644 --- a/packages/frontend-core/src/api/analytics.js +++ b/packages/frontend-core/src/api/analytics.js @@ -1,4 +1,3 @@ -// todo: audit export const buildAnalyticsEndpoints = API => ({ /** * Gets the current status of analytics for this environment From b0b83ae5a0e35ce249a10d108ff2237028355d70 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Mon, 6 Jun 2022 11:18:41 +0100 Subject: [PATCH 58/75] Update workflows to use release instead of develop (temporarily) --- .github/workflows/budibase_ci.yml | 1 + .github/workflows/release-develop.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index 8e235532cf..e940e6fa10 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -11,6 +11,7 @@ on: branches: - master - develop + - release workflow_dispatch: env: diff --git a/.github/workflows/release-develop.yml b/.github/workflows/release-develop.yml index a20e292923..aebc865002 100644 --- a/.github/workflows/release-develop.yml +++ b/.github/workflows/release-develop.yml @@ -4,7 +4,7 @@ concurrency: release-develop on: push: branches: - - develop + - release paths: - '.aws/**' - '.github/**' From 0a726be4bbe0040b8f5c278929ec897e6075bc60 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Mon, 6 Jun 2022 11:31:57 +0100 Subject: [PATCH 59/75] Update types dependency --- packages/backend-core/package.json | 2 +- packages/server/package.json | 2 +- packages/server/yarn.lock | 18 +++++++++--------- packages/types/package.json | 2 +- packages/worker/package.json | 2 +- packages/worker/yarn.lock | 18 +++++++++--------- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index a9530f423b..e1946d108f 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -48,7 +48,7 @@ }, "devDependencies": { "@shopify/jest-koa-mocks": "^3.1.5", - "@budibase/types": "^1.0.167-alpha.8", + "@budibase/types": "^1.0.195", "@types/jest": "^27.4.1", "@types/koa": "^2.13.3", "@types/node": "^15.12.4", diff --git a/packages/server/package.json b/packages/server/package.json index 5f5fe3b2b1..dcf1afb157 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -146,7 +146,7 @@ "@babel/core": "^7.14.3", "@babel/preset-env": "^7.14.4", "@budibase/standard-components": "^0.9.139", - "@budibase/types": "^1.0.167-alpha.8", + "@budibase/types": "^1.0.195", "@jest/test-sequencer": "^24.8.0", "@types/apidoc": "^0.50.0", "@types/bson": "^4.2.0", diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 46661fdd4a..2554be83a7 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -1021,10 +1021,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@1.0.194": - version "1.0.194" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.194.tgz#08b2b1aec3c88efbc7cfb14145ce6f199475c538" - integrity sha512-BbnJFtAioJeD9tQfSwc2uftFK8SagREgSfUYv06dfnf/NsmkrONzZiTon1oA57S7ifcSiu+WZf87lNX0k8pwfQ== +"@budibase/backend-core@1.0.195": + version "1.0.195" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.195.tgz#ee40c690ae4a54febab8b140c9bbb7d04221f3b9" + integrity sha512-6diWgRV9t4DU3kXteJJAhCxyta9m1wvzN7vNbflhY4kYJeg7BC+7jcvc2r8zl6s1vVeSW4ic5/ueSLRaTDySuw== dependencies: "@techpass/passport-openidconnect" "^0.3.0" aws-sdk "^2.901.0" @@ -1099,12 +1099,12 @@ svelte-flatpickr "^3.2.3" svelte-portal "^1.0.0" -"@budibase/pro@1.0.194": - version "1.0.194" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.194.tgz#fbf977b292b9a6dbf7b072b2e0379dcf4379943a" - integrity sha512-LSqVwlhKWwFNnC3acvLnCzbeBoze1Ma6GELE/b5ZxS65owsigu0KC6T/UuujEbU9xqbFJ3R6uV+4Fz4NUibLIw== +"@budibase/pro@1.0.195": + version "1.0.195" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.195.tgz#368652398d1da95f160fc0192b77144b11147ff5" + integrity sha512-zf1f1exHop4m6vda5hObUnTZa2PIBRnc4e0r9iqFbzvGBMfBLGUhGzu23JEwNYaS2xhWHj2FNv4/IVzIyLG4eA== dependencies: - "@budibase/backend-core" "1.0.194" + "@budibase/backend-core" "1.0.195" node-fetch "^2.6.1" "@budibase/standard-components@^0.9.139": diff --git a/packages/types/package.json b/packages/types/package.json index 6841478d4b..c4a67d1f0a 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/types", - "version": "1.0.167-alpha.8", + "version": "1.0.195", "description": "Budibase types", "main": "src/index.ts", "types": "src/index.ts", diff --git a/packages/worker/package.json b/packages/worker/package.json index 97ce58ce02..2cf9620192 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -65,7 +65,7 @@ "server-destroy": "^1.0.1" }, "devDependencies": { - "@budibase/types": "^1.0.167-alpha.8", + "@budibase/types": "^1.0.195", "@types/jest": "^26.0.23", "@types/koa": "^2.13.3", "@types/koa-router": "^7.4.2", diff --git a/packages/worker/yarn.lock b/packages/worker/yarn.lock index d17062ef5e..df7e681b50 100644 --- a/packages/worker/yarn.lock +++ b/packages/worker/yarn.lock @@ -293,10 +293,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@1.0.194": - version "1.0.194" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.194.tgz#08b2b1aec3c88efbc7cfb14145ce6f199475c538" - integrity sha512-BbnJFtAioJeD9tQfSwc2uftFK8SagREgSfUYv06dfnf/NsmkrONzZiTon1oA57S7ifcSiu+WZf87lNX0k8pwfQ== +"@budibase/backend-core@1.0.195": + version "1.0.195" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.195.tgz#ee40c690ae4a54febab8b140c9bbb7d04221f3b9" + integrity sha512-6diWgRV9t4DU3kXteJJAhCxyta9m1wvzN7vNbflhY4kYJeg7BC+7jcvc2r8zl6s1vVeSW4ic5/ueSLRaTDySuw== dependencies: "@techpass/passport-openidconnect" "^0.3.0" aws-sdk "^2.901.0" @@ -322,12 +322,12 @@ uuid "^8.3.2" zlib "^1.0.5" -"@budibase/pro@1.0.194": - version "1.0.194" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.194.tgz#fbf977b292b9a6dbf7b072b2e0379dcf4379943a" - integrity sha512-LSqVwlhKWwFNnC3acvLnCzbeBoze1Ma6GELE/b5ZxS65owsigu0KC6T/UuujEbU9xqbFJ3R6uV+4Fz4NUibLIw== +"@budibase/pro@1.0.195": + version "1.0.195" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.195.tgz#368652398d1da95f160fc0192b77144b11147ff5" + integrity sha512-zf1f1exHop4m6vda5hObUnTZa2PIBRnc4e0r9iqFbzvGBMfBLGUhGzu23JEwNYaS2xhWHj2FNv4/IVzIyLG4eA== dependencies: - "@budibase/backend-core" "1.0.194" + "@budibase/backend-core" "1.0.195" node-fetch "^2.6.1" "@cspotcode/source-map-consumer@0.8.0": From 6fbb5afeecee1e81becdad44c02f58b8c7935de0 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Mon, 6 Jun 2022 12:51:22 +0100 Subject: [PATCH 60/75] Remove ui events except for component created and view published app --- packages/builder/src/analytics/constants.js | 55 +------------------ packages/builder/src/analytics/index.js | 10 +--- .../builder/src/builderStore/datasource.js | 5 -- .../builderStore/store/automation/index.js | 4 -- .../src/builderStore/store/frontend.js | 2 +- .../CreateAutomationModal.svelte | 2 - .../UpdateAutomationModal.svelte | 2 - .../DataTable/modals/CalculateModal.svelte | 2 - .../DataTable/modals/CreateViewModal.svelte | 2 - .../DataTable/modals/FilterModal.svelte | 4 -- .../modals/ImportRestQueriesModal.svelte | 6 -- .../modals/UpdateDatasourceModal.svelte | 2 - .../modals/CreateTableModal.svelte | 2 - .../src/components/deploy/DeployModal.svelte | 5 +- .../components/deploy/DeployNavigation.svelte | 5 +- .../ScreenDropdownMenu.svelte | 8 --- .../NavigationPanel/ScreenWizard.svelte | 15 ----- .../components/start/CreateAppModal.svelte | 6 -- .../pages/builder/portal/apps/index.svelte | 11 +--- .../builder/portal/manage/auth/index.svelte | 3 - .../builder/portal/manage/email/index.svelte | 3 - .../users/_components/AddUserModal.svelte | 2 - .../overview/[application]/index.svelte | 5 +- packages/builder/src/stores/portal/auth.js | 2 +- packages/cli/src/analytics/Client.js | 10 +--- packages/cli/src/constants.js | 10 ++-- packages/server/src/environment.js | 1 - 27 files changed, 15 insertions(+), 169 deletions(-) diff --git a/packages/builder/src/analytics/constants.js b/packages/builder/src/analytics/constants.js index d800ae2c3f..424b32c8a4 100644 --- a/packages/builder/src/analytics/constants.js +++ b/packages/builder/src/analytics/constants.js @@ -1,57 +1,6 @@ export const Events = { - // TODO: Remove most UI events - BUILDER: { - STARTED: "Builder Started", - }, - COMPONENT: { - CREATED: "Added Component", - }, - DATASOURCE: { - CREATED: "Datasource Created", - UPDATED: "Datasource Updated", - }, - QUERIES: { - REST: "REST Queries Imported", - }, - TABLE: { - CREATED: "Table Created", - }, - VIEW: { - CREATED: "View Created", - ADDED_FILTER: "Added View Filter", - ADDED_CALCULATE: "Added View Calculate", - }, - SCREEN: { - CREATED: "Screen Created", - CREATE_ROLE_UPDATED: "Changed Role On Screen Creation", - }, - AUTOMATION: { - CREATED: "Automation Created", - SAVED: "Automation Saved", - BLOCK_ADDED: "Added Automation Block", - }, - NPS: { - SUBMITTED: "budibase:feedback_submitted", - }, - APP: { - CREATED: "budibase:app_created", - PUBLISHED: "budibase:app_published", - UNPUBLISHED: "budibase:app_unpublished", - VIEW_PUBLISHED: "budibase:view_published_app", - }, - ANALYTICS: { - OPT_IN: "budibase:analytics_opt_in", - OPT_OUT: "budibase:analytics_opt_out", - }, - USER: { - INVITE: "budibase:portal_user_invite", - }, - SMTP: { - SAVED: "budibase:smtp_saved", - }, - SSO: { - SAVED: "budibase:sso_saved", - }, + COMPONENT_CREATED: "component:created", + APP_VIEW_PUBLISHED: "app:view_published", } export const EventSource = { diff --git a/packages/builder/src/analytics/index.js b/packages/builder/src/analytics/index.js index 1b03b0855e..24d3da43c1 100644 --- a/packages/builder/src/analytics/index.js +++ b/packages/builder/src/analytics/index.js @@ -11,7 +11,6 @@ const posthog = new PosthogClient( const sentry = new SentryClient(process.env.SENTRY_DSN) const intercom = new IntercomClient(process.env.INTERCOM_TOKEN) -// TODO: Remove most UI events class AnalyticsHub { constructor() { this.clients = [posthog, sentry, intercom] @@ -25,11 +24,8 @@ class AnalyticsHub { } } - identify(id, metadata) { + identify(id) { posthog.identify(id) - if (metadata) { - posthog.updateUser(metadata) - } sentry.identify(id) } @@ -46,10 +42,6 @@ class AnalyticsHub { intercom.show(user) } - submitFeedback(values) { - posthog.npsFeedback(values) - } - async logout() { posthog.logout() intercom.logout() diff --git a/packages/builder/src/builderStore/datasource.js b/packages/builder/src/builderStore/datasource.js index cfdeeac23e..804d88bad6 100644 --- a/packages/builder/src/builderStore/datasource.js +++ b/packages/builder/src/builderStore/datasource.js @@ -1,6 +1,5 @@ import { datasources, tables } from "../stores/backend" import { IntegrationNames } from "../constants/backend" -import analytics, { Events } from "../analytics" import { get } from "svelte/store" import cloneDeep from "lodash/cloneDeepWith" @@ -31,10 +30,6 @@ export async function saveDatasource(config, skipFetch = false) { // update the tables incase data source plus await tables.fetch() await datasources.select(resp._id) - analytics.captureEvent(Events.DATASOURCE.CREATED, { - name: resp.name, - source: resp.source, - }) return resp } diff --git a/packages/builder/src/builderStore/store/automation/index.js b/packages/builder/src/builderStore/store/automation/index.js index 5252d4b06a..cf42492c05 100644 --- a/packages/builder/src/builderStore/store/automation/index.js +++ b/packages/builder/src/builderStore/store/automation/index.js @@ -2,7 +2,6 @@ import { writable } from "svelte/store" import { API } from "api" import Automation from "./Automation" import { cloneDeep } from "lodash/fp" -import analytics, { Events } from "analytics" const initialAutomationState = { automations: [], @@ -125,9 +124,6 @@ const automationActions = store => ({ state.selectedBlock = newBlock return state }) - analytics.captureEvent(Events.AUTOMATION.BLOCK_ADDED, { - name: block.name, - }) }, toggleFieldControl: value => { store.update(state => { diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index 3ffc890c7d..bc02e04db3 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -426,7 +426,7 @@ export const getFrontendStore = () => { }) // Log event - analytics.captureEvent(Events.COMPONENT.CREATED, { + analytics.captureEvent(Events.COMPONENT_CREATED, { name: componentInstance._component, }) diff --git a/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte b/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte index 4fb912939a..9543a9c552 100644 --- a/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte +++ b/packages/builder/src/components/automation/AutomationPanel/CreateAutomationModal.svelte @@ -11,7 +11,6 @@ Body, Icon, } from "@budibase/bbui" - import analytics, { Events } from "analytics" let name let selectedTrigger @@ -47,7 +46,6 @@ notifications.success(`Automation ${name} created`) $goto(`./${$automationStore.selectedAutomation.automation._id}`) - analytics.captureEvent(Events.AUTOMATION.CREATED, { name }) } catch (error) { notifications.error("Error creating automation") } diff --git a/packages/builder/src/components/automation/AutomationPanel/UpdateAutomationModal.svelte b/packages/builder/src/components/automation/AutomationPanel/UpdateAutomationModal.svelte index bceff0ef2e..9075a5433c 100644 --- a/packages/builder/src/components/automation/AutomationPanel/UpdateAutomationModal.svelte +++ b/packages/builder/src/components/automation/AutomationPanel/UpdateAutomationModal.svelte @@ -2,7 +2,6 @@ import { automationStore } from "builderStore" import { notifications } from "@budibase/bbui" import { Icon, Input, ModalContent, Modal } from "@budibase/bbui" - import analytics, { Events } from "analytics" let name let error = "" @@ -27,7 +26,6 @@ } await automationStore.actions.save(updatedAutomation) notifications.success(`Automation ${name} updated successfully`) - analytics.captureEvent(Events.AUTOMATION.SAVED, { name }) hide() } catch (error) { notifications.error("Error saving automation") diff --git a/packages/builder/src/components/backend/DataTable/modals/CalculateModal.svelte b/packages/builder/src/components/backend/DataTable/modals/CalculateModal.svelte index 81f54032f6..5487551137 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CalculateModal.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CalculateModal.svelte @@ -1,7 +1,6 @@ diff --git a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte index 7830fd0246..49138a3b0e 100644 --- a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte +++ b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte @@ -11,7 +11,6 @@ Layout, } from "@budibase/bbui" import TableDataImport from "../TableDataImport.svelte" - import analytics, { Events } from "analytics" import { buildAutoColumn, getAutoColumnInformation } from "builderStore/utils" $: tableNames = $tables.list.map(table => table.name) @@ -59,7 +58,6 @@ try { table = await tables.save(newTable) notifications.success(`Table ${name} created successfully.`) - analytics.captureEvent(Events.TABLE.CREATED, { name }) // Navigate to new table const currentUrl = $url() diff --git a/packages/builder/src/components/deploy/DeployModal.svelte b/packages/builder/src/components/deploy/DeployModal.svelte index 0d9bfb7539..5b0dea33fc 100644 --- a/packages/builder/src/components/deploy/DeployModal.svelte +++ b/packages/builder/src/components/deploy/DeployModal.svelte @@ -31,9 +31,6 @@ published = await API.deployAppChanges() - analytics.captureEvent(Events.APP.PUBLISHED, { - appId: $store.appId, - }) if (typeof onOk === "function") { await onOk() } @@ -49,7 +46,7 @@ const viewApp = () => { if (published) { - analytics.captureEvent(Events.APP.VIEW_PUBLISHED, { + analytics.captureEvent(Events.APP_VIEW_PUBLISHED, { appId: $store.appId, eventSource: EventSource.PORTAL, }) diff --git a/packages/builder/src/components/deploy/DeployNavigation.svelte b/packages/builder/src/components/deploy/DeployNavigation.svelte index ded75652cc..9cb7efd362 100644 --- a/packages/builder/src/components/deploy/DeployNavigation.svelte +++ b/packages/builder/src/components/deploy/DeployNavigation.svelte @@ -57,7 +57,7 @@ } const viewApp = () => { - analytics.captureEvent(Events.APP.VIEW_PUBLISHED, { + analytics.captureEvent(Events.APP_VIEW_PUBLISHED, { appId: selectedApp.appId, eventSource: EventSource.PORTAL, }) @@ -79,9 +79,6 @@ return } try { - analytics.captureEvent(Events.APP.UNPUBLISHED, { - appId: selectedApp.appId, - }) await API.unpublishApp(selectedApp.prodId) await apps.load() notifications.success("App unpublished successfully") diff --git a/packages/builder/src/components/design/NavigationPanel/ComponentNavigationTree/ScreenDropdownMenu.svelte b/packages/builder/src/components/design/NavigationPanel/ComponentNavigationTree/ScreenDropdownMenu.svelte index 76bb48a26c..a9cffa2253 100644 --- a/packages/builder/src/components/design/NavigationPanel/ComponentNavigationTree/ScreenDropdownMenu.svelte +++ b/packages/builder/src/components/design/NavigationPanel/ComponentNavigationTree/ScreenDropdownMenu.svelte @@ -12,7 +12,6 @@ } from "@budibase/bbui" import ScreenDetailsModal from "../ScreenDetailsModal.svelte" import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl" - import analytics, { Events } from "analytics" import { makeComponentUnique } from "builderStore/componentUtils" export let screenId @@ -40,13 +39,6 @@ try { // Create the screen await store.actions.screens.save(duplicateScreen) - - // Analytics - if (screen.template) { - analytics.captureEvent(Events.SCREEN.CREATED, { - template: "createFromScratch", - }) - } } catch (error) { notifications.error("Error duplicating screen") console.log(error) diff --git a/packages/builder/src/components/design/NavigationPanel/ScreenWizard.svelte b/packages/builder/src/components/design/NavigationPanel/ScreenWizard.svelte index 1859ba93b6..0a3c9611bc 100644 --- a/packages/builder/src/components/design/NavigationPanel/ScreenWizard.svelte +++ b/packages/builder/src/components/design/NavigationPanel/ScreenWizard.svelte @@ -5,7 +5,6 @@ import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl" import { Modal, ModalContent, Select, notifications } from "@budibase/bbui" import { store, selectedAccessRole } from "builderStore" - import analytics, { Events } from "analytics" import { get } from "svelte/store" import getTemplates from "builderStore/store/screenTemplates" import { tables, roles } from "stores/backend" @@ -65,15 +64,6 @@ // Create the screen await store.actions.screens.save(screen) - // Analytics - if (screen.template) { - analytics.captureEvent(Events.SCREEN.CREATED, { - template: screen.template, - datasource: screen.datasource, - screenAccessRole, - }) - } - // Add link in layout for list screens if (screen.props._instanceName.endsWith("List")) { await store.actions.components.links.save( @@ -208,11 +198,6 @@ Select which level of access you want your screens to have