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) }