query / update events + tests
This commit is contained in:
parent
8a08e9322f
commit
ac8573b67e
|
@ -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
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 = () => {
|
||||
|
|
|
@ -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)
|
||||
// }
|
||||
|
|
|
@ -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(),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
|
|
|
@ -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<ImportResult> => {
|
||||
// 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ export abstract class ImportSource {
|
|||
abstract isSupported(data: string): Promise<boolean>
|
||||
abstract getInfo(): Promise<ImportInfo>
|
||||
abstract getQueries(datasourceId: string): Promise<Query[]>
|
||||
abstract getImportSource(): string
|
||||
|
||||
constructQuery = (
|
||||
datasourceId: string,
|
||||
|
|
|
@ -71,6 +71,10 @@ export class Curl extends ImportSource {
|
|||
}
|
||||
}
|
||||
|
||||
getImportSource(): string {
|
||||
return "curl"
|
||||
}
|
||||
|
||||
getQueries = async (datasourceId: string): Promise<Query[]> => {
|
||||
const url = this.getUrl()
|
||||
const name = url.pathname
|
||||
|
|
|
@ -70,6 +70,10 @@ export class OpenAPI2 extends OpenAPISource {
|
|||
}
|
||||
}
|
||||
|
||||
getImportSource(): string {
|
||||
return "openapi2.0"
|
||||
}
|
||||
|
||||
getQueries = async (datasourceId: string): Promise<Query[]> => {
|
||||
const url = this.getUrl()
|
||||
const queries = []
|
||||
|
|
|
@ -106,6 +106,10 @@ export class OpenAPI3 extends OpenAPISource {
|
|||
}
|
||||
}
|
||||
|
||||
getImportSource(): string {
|
||||
return "openapi3.0"
|
||||
}
|
||||
|
||||
getQueries = async (datasourceId: string): Promise<Query[]> => {
|
||||
let url: string | URL | undefined
|
||||
if (this.document.servers?.length) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
})
|
|
@ -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 () => {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue