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)