Finish global migration
This commit is contained in:
parent
8ae358d237
commit
63dd69f5b3
|
@ -71,6 +71,7 @@ jest.mock("../../../events", () => {
|
||||||
},
|
},
|
||||||
rows: {
|
rows: {
|
||||||
imported: jest.fn(),
|
imported: jest.fn(),
|
||||||
|
created: jest.fn(),
|
||||||
},
|
},
|
||||||
screen: {
|
screen: {
|
||||||
created: jest.fn(),
|
created: jest.fn(),
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
import * as users from "./global/users"
|
||||||
|
import * as rows from "./global/rows"
|
||||||
|
import * as configs from "./global/configs"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date:
|
* Date:
|
||||||
* May 2022
|
* May 2022
|
||||||
|
@ -6,4 +10,8 @@
|
||||||
* Backfill global events.
|
* 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)
|
||||||
|
}
|
||||||
|
|
|
@ -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",
|
|
|
@ -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<Config[]> => {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,5 +10,7 @@ export const backfill = async () => {
|
||||||
const appIds = allApps ? allApps.map((app: { appId: any }) => app.appId) : []
|
const appIds = allApps ? allApps.map((app: { appId: any }) => app.appId) : []
|
||||||
const rows: Row[] = await getUniqueRows(appIds)
|
const rows: Row[] = await getUniqueRows(appIds)
|
||||||
const rowCount = rows ? rows.length : 0
|
const rowCount = rows ? rows.length : 0
|
||||||
events.rows.created(rowCount)
|
if (rowCount) {
|
||||||
|
events.rows.created(rowCount)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
// USER_CREATED = "user:created",
|
|
||||||
// USER_PERMISSION_ADMIN_ASSIGNED = "user:admin:assigned",
|
|
||||||
// USER_PERMISSION_BUILDER_ASSIGNED = "user:builder:assigned",
|
|
|
@ -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<User[]> => {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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 TestConfig from "../../tests/utilities/TestConfiguration"
|
||||||
import structures from "../../tests/utilities/structures"
|
import structures from "../../tests/utilities/structures"
|
||||||
import { MIGRATIONS } from "../"
|
import { MIGRATIONS } from "../"
|
||||||
|
import * as helpers from "./helpers"
|
||||||
|
|
||||||
jest.setTimeout(100000)
|
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)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -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,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -121,6 +121,7 @@ class TestConfiguration {
|
||||||
async globalUser({
|
async globalUser({
|
||||||
id = GLOBAL_USER_ID,
|
id = GLOBAL_USER_ID,
|
||||||
builder = true,
|
builder = true,
|
||||||
|
admin = false,
|
||||||
email = EMAIL,
|
email = EMAIL,
|
||||||
roles,
|
roles,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
|
@ -147,6 +148,11 @@ class TestConfiguration {
|
||||||
} else {
|
} else {
|
||||||
user.builder = { global: false }
|
user.builder = { global: false }
|
||||||
}
|
}
|
||||||
|
if (admin) {
|
||||||
|
user.admin = { global: true }
|
||||||
|
} else {
|
||||||
|
user.admin = { global: false }
|
||||||
|
}
|
||||||
const resp = await db.put(user)
|
const resp = await db.put(user)
|
||||||
return {
|
return {
|
||||||
_rev: resp._rev,
|
_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 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)
|
await userCache.invalidateUser(globalId)
|
||||||
return {
|
return {
|
||||||
...resp,
|
...resp,
|
||||||
|
@ -277,7 +291,7 @@ class TestConfiguration {
|
||||||
|
|
||||||
// APP
|
// APP
|
||||||
|
|
||||||
async createApp(appName, opts = { deploy: true }) {
|
async createApp(appName) {
|
||||||
// create dev app
|
// create dev app
|
||||||
// clear any old app
|
// clear any old app
|
||||||
this.appId = null
|
this.appId = null
|
||||||
|
@ -287,11 +301,9 @@ class TestConfiguration {
|
||||||
await context.updateAppId(this.appId)
|
await context.updateAppId(this.appId)
|
||||||
|
|
||||||
// create production app
|
// create production app
|
||||||
if (opts.deploy) {
|
this.prodApp = await this.deploy()
|
||||||
this.prodApp = await this.deploy()
|
this.prodAppId = this.prodApp.appId
|
||||||
this.prodAppId = this.prodApp.appId
|
this.allApps.push(this.prodApp)
|
||||||
this.allApps.push(this.prodApp)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.allApps.push(this.app)
|
this.allApps.push(this.app)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Document } from "./document"
|
import { Document } from "../document"
|
||||||
|
|
||||||
export interface App extends Document {
|
export interface App extends Document {
|
||||||
appId: string
|
appId: string
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Document } from "./document"
|
import { Document } from "../document"
|
||||||
|
|
||||||
export interface Automation extends Document {
|
export interface Automation extends Document {
|
||||||
definition: {
|
definition: {
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
import { Document } from "./document"
|
import { Document } from "../document"
|
||||||
|
|
||||||
export interface Datasource extends Document {}
|
export interface Datasource extends Document {}
|
||||||
|
|
|
@ -7,6 +7,6 @@ export * from "./role"
|
||||||
export * from "./table"
|
export * from "./table"
|
||||||
export * from "./screen"
|
export * from "./screen"
|
||||||
export * from "./view"
|
export * from "./view"
|
||||||
export * from "./document"
|
export * from "../document"
|
||||||
export * from "./row"
|
export * from "./row"
|
||||||
export * from "./user"
|
export * from "./user"
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
import { Document } from "./document"
|
import { Document } from "../document"
|
||||||
|
|
||||||
export interface Layout extends Document {}
|
export interface Layout extends Document {}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Document } from "./document"
|
import { Document } from "../document"
|
||||||
|
|
||||||
export interface Query extends Document {
|
export interface Query extends Document {
|
||||||
datasourceId: string
|
datasourceId: string
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
import { Document } from "./document"
|
import { Document } from "../document"
|
||||||
|
|
||||||
export interface Role extends Document {}
|
export interface Role extends Document {}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
import { Document } from "./document"
|
import { Document } from "../document"
|
||||||
|
|
||||||
export interface Row extends Document {}
|
export interface Row extends Document {}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
import { Document } from "./document"
|
import { Document } from "../document"
|
||||||
|
|
||||||
export interface Screen extends Document {}
|
export interface Screen extends Document {}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Document } from "./document"
|
import { Document } from "../document"
|
||||||
import { View } from "./view"
|
import { View } from "./view"
|
||||||
|
|
||||||
export interface Table extends Document {
|
export interface Table extends Document {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Document } from "./document"
|
import { Document } from "../document"
|
||||||
|
|
||||||
export interface UserMetadata extends Document {
|
export interface UserMetadata extends Document {
|
||||||
roleId: string
|
roleId: string
|
||||||
|
|
|
@ -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",
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
export interface User {
|
import { Document } from "../document"
|
||||||
|
|
||||||
|
export interface User extends Document {
|
||||||
roles: UserRoles
|
roles: UserRoles
|
||||||
|
builder?: {
|
||||||
|
global: boolean
|
||||||
|
}
|
||||||
|
admin?: {
|
||||||
|
global: boolean
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserRoles {
|
export interface UserRoles {
|
||||||
|
|
Loading…
Reference in New Issue