App migrations finished
This commit is contained in:
parent
498c130e71
commit
756f5b51aa
|
@ -12,6 +12,7 @@ import {
|
||||||
ViewFilterUpdatedEvent,
|
ViewFilterUpdatedEvent,
|
||||||
ViewUpdatedEvent,
|
ViewUpdatedEvent,
|
||||||
View,
|
View,
|
||||||
|
ViewCalculation,
|
||||||
Table,
|
Table,
|
||||||
TableExportFormat,
|
TableExportFormat,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
@ -53,7 +54,7 @@ export function filterDeleted() {
|
||||||
publishEvent(Event.VIEW_FILTER_DELETED, properties)
|
publishEvent(Event.VIEW_FILTER_DELETED, properties)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function calculationCreated() {
|
export function calculationCreated(calculation: ViewCalculation) {
|
||||||
const properties: ViewCalculationCreatedEvent = {}
|
const properties: ViewCalculationCreatedEvent = {}
|
||||||
publishEvent(Event.VIEW_CALCULATION_CREATED, properties)
|
publishEvent(Event.VIEW_CALCULATION_CREATED, properties)
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ jest.mock("../../../events", () => {
|
||||||
assigned: jest.fn(),
|
assigned: jest.fn(),
|
||||||
unassigned: jest.fn(),
|
unassigned: jest.fn(),
|
||||||
},
|
},
|
||||||
row: {
|
rows: {
|
||||||
imported: jest.fn(),
|
imported: jest.fn(),
|
||||||
},
|
},
|
||||||
screen: {
|
screen: {
|
||||||
|
|
|
@ -3,11 +3,11 @@ import { generateQueryID } from "../../../../db/utils"
|
||||||
import { ImportInfo, ImportSource } from "./sources/base"
|
import { ImportInfo, ImportSource } from "./sources/base"
|
||||||
import { OpenAPI2 } from "./sources/openapi2"
|
import { OpenAPI2 } from "./sources/openapi2"
|
||||||
import { OpenAPI3 } from "./sources/openapi3"
|
import { OpenAPI3 } from "./sources/openapi3"
|
||||||
import { Query } from "./../../../../definitions/common"
|
|
||||||
import { Curl } from "./sources/curl"
|
import { Curl } from "./sources/curl"
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { getAppDB } from "@budibase/backend-core/context"
|
import { getAppDB } from "@budibase/backend-core/context"
|
||||||
import { events } from "@budibase/backend-core"
|
import { events } from "@budibase/backend-core"
|
||||||
|
import { Datasource, Query } from "@budibase/types"
|
||||||
|
|
||||||
interface ImportResult {
|
interface ImportResult {
|
||||||
errorQueries: Query[]
|
errorQueries: Query[]
|
||||||
|
@ -83,7 +83,7 @@ export class RestImporter {
|
||||||
// events
|
// events
|
||||||
const count = successQueries.length
|
const count = successQueries.length
|
||||||
const importSource = this.source.getImportSource()
|
const importSource = this.source.getImportSource()
|
||||||
const datasource = await db.get(datasourceId)
|
const datasource: Datasource = await db.get(datasourceId)
|
||||||
events.query.imported(datasource, importSource, count)
|
events.query.imported(datasource, importSource, count)
|
||||||
for (let query of successQueries) {
|
for (let query of successQueries) {
|
||||||
events.query.created(datasource, query)
|
events.query.created(datasource, query)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Query, QueryParameter } from "../../../../../../definitions/datasource"
|
import { Query, QueryParameter } from "@budibase/types"
|
||||||
import { URL } from "url"
|
import { URL } from "url"
|
||||||
|
|
||||||
export interface ImportInfo {
|
export interface ImportInfo {
|
||||||
|
|
|
@ -55,8 +55,8 @@ describe("/tables", () => {
|
||||||
expect(events.table.created).toBeCalledWith(res.body)
|
expect(events.table.created).toBeCalledWith(res.body)
|
||||||
expect(events.table.imported).toBeCalledTimes(1)
|
expect(events.table.imported).toBeCalledTimes(1)
|
||||||
expect(events.table.imported).toBeCalledWith(res.body, "csv")
|
expect(events.table.imported).toBeCalledWith(res.body, "csv")
|
||||||
expect(events.row.imported).toBeCalledTimes(1)
|
expect(events.rowsimported).toBeCalledTimes(1)
|
||||||
expect(events.row.imported).toBeCalledWith(res.body, "csv", 1)
|
expect(events.rows.imported).toBeCalledWith(res.body, "csv", 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should apply authorization to endpoint", async () => {
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import * as layouts from "./app/layouts"
|
||||||
import * as queries from "./app/queries"
|
import * as queries from "./app/queries"
|
||||||
import * as roles from "./app/roles"
|
import * as roles from "./app/roles"
|
||||||
import * as tables from "./app/tables"
|
import * as tables from "./app/tables"
|
||||||
|
import * as screens from "./app/screens"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date:
|
* Date:
|
||||||
|
@ -22,4 +23,5 @@ export const run = async (appDb: any) => {
|
||||||
await queries.backfill(appDb)
|
await queries.backfill(appDb)
|
||||||
await roles.backfill(appDb)
|
await roles.backfill(appDb)
|
||||||
await tables.backfill(appDb)
|
await tables.backfill(appDb)
|
||||||
|
await screens.backfill(appDb)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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<Screen[]> => {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,20 @@ export const backfill = async (appDb: any) => {
|
||||||
|
|
||||||
for (const table of tables) {
|
for (const table of tables) {
|
||||||
events.table.created(table)
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
// VIEW_CREATED = "view:created",
|
|
||||||
// VIEW_FILTER_CREATED = "view:filter:created",
|
|
||||||
// VIEW_CALCULATION_CREATED = "view:calculation:created",
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -29,7 +29,11 @@ describe("migrations", () => {
|
||||||
await config.createRole()
|
await config.createRole()
|
||||||
await config.createRole()
|
await config.createRole()
|
||||||
await config.createTable()
|
await config.createTable()
|
||||||
|
await config.createView()
|
||||||
await config.createTable()
|
await config.createTable()
|
||||||
|
await config.createView(structures.view(config.table._id))
|
||||||
|
await config.createScreen()
|
||||||
|
await config.createScreen()
|
||||||
|
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
const migration = MIGRATIONS.filter(
|
const migration = MIGRATIONS.filter(
|
||||||
|
@ -46,6 +50,10 @@ describe("migrations", () => {
|
||||||
expect(events.query.created).toBeCalledTimes(2)
|
expect(events.query.created).toBeCalledTimes(2)
|
||||||
expect(events.role.created).toBeCalledTimes(2)
|
expect(events.role.created).toBeCalledTimes(2)
|
||||||
expect(events.table.created).toBeCalledTimes(3)
|
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)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -409,7 +409,6 @@ class TestConfiguration {
|
||||||
throw "Test requires table to be configured."
|
throw "Test requires table to be configured."
|
||||||
}
|
}
|
||||||
const view = config || {
|
const view = config || {
|
||||||
map: "function(doc) { emit(doc[doc.key], doc._id); } ",
|
|
||||||
tableId: this.table._id,
|
tableId: this.table._id,
|
||||||
name: "ViewTest",
|
name: "ViewTest",
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) => {
|
exports.automationStep = (actionDefinition = ACTION_DEFINITIONS.CREATE_ROW) => {
|
||||||
return {
|
return {
|
||||||
id: uuidv4(),
|
id: uuidv4(),
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
export interface Automation {
|
import { Document } from "./document"
|
||||||
|
|
||||||
|
export interface Automation extends Document {
|
||||||
definition: {
|
definition: {
|
||||||
steps: AutomationStep[]
|
steps: AutomationStep[]
|
||||||
trigger: AutomationTrigger
|
trigger: AutomationTrigger
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
export interface Datasource {}
|
import { Document } from "./document"
|
||||||
|
|
||||||
|
export interface Datasource extends Document {}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
export interface Document {
|
export interface Document {
|
||||||
_id: string
|
_id?: string
|
||||||
_rev?: string
|
_rev?: string
|
||||||
createdAt: string
|
createdAt?: string
|
||||||
updatedAt: string
|
updatedAt?: string
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
export interface Layout {}
|
import { Document } from "./document"
|
||||||
|
|
||||||
|
export interface Layout extends Document {}
|
||||||
|
|
|
@ -1,3 +1,44 @@
|
||||||
export interface Query {
|
import { Document } from "./document"
|
||||||
|
|
||||||
|
export interface Query extends Document {
|
||||||
datasourceId: string
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
export interface Row {}
|
import { Document } from "./document"
|
||||||
|
|
||||||
|
export interface Row extends Document {}
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
export interface Screen {}
|
import { Document } from "./document"
|
||||||
|
|
||||||
|
export interface Screen extends Document {}
|
||||||
|
|
|
@ -1 +1,6 @@
|
||||||
export interface Table {}
|
import { Document } from "./document"
|
||||||
|
import { View } from "./view"
|
||||||
|
|
||||||
|
export interface Table extends Document {
|
||||||
|
views: { [key: string]: View }
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
export interface UserMetadata {
|
import { Document } from "./document"
|
||||||
|
|
||||||
|
export interface UserMetadata extends Document {
|
||||||
roleId: string
|
roleId: string
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
|
}
|
||||||
|
|
|
@ -139,12 +139,13 @@ describe("/api/global/users", () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
await createUser(user)
|
await createUser(user)
|
||||||
|
const savedUser = await config.getUser(user.email)
|
||||||
|
|
||||||
expect(events.user.created).toBeCalledTimes(1)
|
expect(events.user.created).toBeCalledTimes(1)
|
||||||
expect(events.user.updated).not.toBeCalled()
|
expect(events.user.updated).not.toBeCalled()
|
||||||
expect(events.role.assigned).toBeCalledTimes(2)
|
expect(events.role.assigned).toBeCalledTimes(2)
|
||||||
expect(events.role.assigned).toBeCalledWith("role1")
|
expect(events.role.assigned).toBeCalledWith(savedUser, "role1")
|
||||||
expect(events.role.assigned).toBeCalledWith("role2")
|
expect(events.role.assigned).toBeCalledWith(savedUser, "role2")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -243,12 +244,13 @@ describe("/api/global/users", () => {
|
||||||
"app_456": "role2",
|
"app_456": "role2",
|
||||||
}
|
}
|
||||||
await updateUser(user)
|
await updateUser(user)
|
||||||
|
const savedUser = await config.getUser(user.email)
|
||||||
|
|
||||||
expect(events.user.created).not.toBeCalled()
|
expect(events.user.created).not.toBeCalled()
|
||||||
expect(events.user.updated).toBeCalledTimes(1)
|
expect(events.user.updated).toBeCalledTimes(1)
|
||||||
expect(events.role.assigned).toBeCalledTimes(2)
|
expect(events.role.assigned).toBeCalledTimes(2)
|
||||||
expect(events.role.assigned).toBeCalledWith("role1")
|
expect(events.role.assigned).toBeCalledWith(savedUser, "role1")
|
||||||
expect(events.role.assigned).toBeCalledWith("role2")
|
expect(events.role.assigned).toBeCalledWith(savedUser, "role2")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should be able to unassign app roles", async () => {
|
it("should be able to unassign app roles", async () => {
|
||||||
|
@ -262,12 +264,13 @@ describe("/api/global/users", () => {
|
||||||
|
|
||||||
user.roles = {}
|
user.roles = {}
|
||||||
await updateUser(user)
|
await updateUser(user)
|
||||||
|
const savedUser = await config.getUser(user.email)
|
||||||
|
|
||||||
expect(events.user.created).not.toBeCalled()
|
expect(events.user.created).not.toBeCalled()
|
||||||
expect(events.user.updated).toBeCalledTimes(1)
|
expect(events.user.updated).toBeCalledTimes(1)
|
||||||
expect(events.role.unassigned).toBeCalledTimes(2)
|
expect(events.role.unassigned).toBeCalledTimes(2)
|
||||||
expect(events.role.unassigned).toBeCalledWith("role1")
|
expect(events.role.unassigned).toBeCalledWith(savedUser, "role1")
|
||||||
expect(events.role.unassigned).toBeCalledWith("role2")
|
expect(events.role.unassigned).toBeCalledWith(savedUser, "role2")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should be able to update existing app roles", async () => {
|
it("should be able to update existing app roles", async () => {
|
||||||
|
@ -284,13 +287,14 @@ describe("/api/global/users", () => {
|
||||||
"app_456": "role2-edit",
|
"app_456": "role2-edit",
|
||||||
}
|
}
|
||||||
await updateUser(user)
|
await updateUser(user)
|
||||||
|
const savedUser = await config.getUser(user.email)
|
||||||
|
|
||||||
expect(events.user.created).not.toBeCalled()
|
expect(events.user.created).not.toBeCalled()
|
||||||
expect(events.user.updated).toBeCalledTimes(1)
|
expect(events.user.updated).toBeCalledTimes(1)
|
||||||
expect(events.role.unassigned).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).toBeCalledTimes(1)
|
||||||
expect(events.role.assigned).toBeCalledWith("role2-edit")
|
expect(events.role.assigned).toBeCalledWith(savedUser, "role2-edit")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,7 @@ export const save = async (
|
||||||
} else {
|
} else {
|
||||||
response = await putUserFn()
|
response = await putUserFn()
|
||||||
}
|
}
|
||||||
|
user._rev = response.rev
|
||||||
|
|
||||||
eventHelpers.handleSaveEvents(user, dbUser)
|
eventHelpers.handleSaveEvents(user, dbUser)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue