timestamp support, logging updates, migration progress indicators, identification updates
This commit is contained in:
parent
b3f04e7e8f
commit
6a4c601ae8
|
@ -6,6 +6,7 @@ const {
|
||||||
updateAppId,
|
updateAppId,
|
||||||
doInAppContext,
|
doInAppContext,
|
||||||
doInUserContext,
|
doInUserContext,
|
||||||
|
doInTenant,
|
||||||
} = require("./src/context")
|
} = require("./src/context")
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -16,4 +17,5 @@ module.exports = {
|
||||||
updateAppId,
|
updateAppId,
|
||||||
doInAppContext,
|
doInAppContext,
|
||||||
doInUserContext,
|
doInUserContext,
|
||||||
|
doInTenant,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,12 @@ import { Event } from "@budibase/types"
|
||||||
import { processors } from "./processors"
|
import { processors } from "./processors"
|
||||||
import * as identification from "./identification"
|
import * as identification from "./identification"
|
||||||
|
|
||||||
export const publishEvent = async (event: Event, properties: any) => {
|
export const publishEvent = async (
|
||||||
|
event: Event,
|
||||||
|
properties: any,
|
||||||
|
timestamp?: string | number
|
||||||
|
) => {
|
||||||
// in future this should use async events via a distributed queue.
|
// in future this should use async events via a distributed queue.
|
||||||
const identity = await identification.getCurrentIdentity()
|
const identity = await identification.getCurrentIdentity()
|
||||||
await processors.processEvent(event, identity, properties)
|
await processors.processEvent(event, identity, properties, timestamp)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,18 +23,22 @@ export const getCurrentIdentity = async (): Promise<Identity> => {
|
||||||
const user: SessionUser | undefined = context.getUser()
|
const user: SessionUser | undefined = context.getUser()
|
||||||
let tenantId = context.getTenantId()
|
let tenantId = context.getTenantId()
|
||||||
let id: string
|
let id: string
|
||||||
|
let type: IdentityType
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
id = user._id
|
id = user._id
|
||||||
|
type = IdentityType.USER
|
||||||
} else {
|
} else {
|
||||||
const global = await getGlobalIdentifiers(tenantId)
|
const global = await getGlobalIdentifiers(tenantId)
|
||||||
id = global.id
|
id = global.id
|
||||||
tenantId = global.tenantId
|
tenantId = global.tenantId
|
||||||
|
type = IdentityType.TENANT
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
tenantId,
|
tenantId,
|
||||||
|
type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,14 +117,13 @@ export const identifyAccount = async (account: Account) => {
|
||||||
let id = account.accountId
|
let id = account.accountId
|
||||||
const tenantId = account.tenantId
|
const tenantId = account.tenantId
|
||||||
const hosting = account.hosting
|
const hosting = account.hosting
|
||||||
let type = IdentityType.ACCOUNT
|
let type = IdentityType.USER
|
||||||
let providerType = isSSOAccount(account) ? account.providerType : undefined
|
let providerType = isSSOAccount(account) ? account.providerType : undefined
|
||||||
|
|
||||||
if (isCloudAccount(account)) {
|
if (isCloudAccount(account)) {
|
||||||
if (account.budibaseUserId) {
|
if (account.budibaseUserId) {
|
||||||
// use the budibase user as the id if set
|
// use the budibase user as the id if set
|
||||||
id = account.budibaseUserId
|
id = account.budibaseUserId
|
||||||
type = IdentityType.USER
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,13 +16,14 @@ export default class AnalyticsProcessor implements EventProcessor {
|
||||||
async processEvent(
|
async processEvent(
|
||||||
event: Event,
|
event: Event,
|
||||||
identity: Identity,
|
identity: Identity,
|
||||||
properties: any
|
properties: any,
|
||||||
|
timestamp?: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!(await analytics.enabled())) {
|
if (!(await analytics.enabled())) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (this.posthog) {
|
if (this.posthog) {
|
||||||
this.posthog.processEvent(event, identity, properties)
|
this.posthog.processEvent(event, identity, properties, timestamp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Event, Identity } from "@budibase/types"
|
import { Event, Identity } from "@budibase/types"
|
||||||
import { EventProcessor } from "./types"
|
import { EventProcessor } from "./types"
|
||||||
|
import env from "../../environment"
|
||||||
|
|
||||||
export default class LoggingProcessor implements EventProcessor {
|
export default class LoggingProcessor implements EventProcessor {
|
||||||
async processEvent(
|
async processEvent(
|
||||||
|
@ -7,8 +8,11 @@ export default class LoggingProcessor implements EventProcessor {
|
||||||
identity: Identity,
|
identity: Identity,
|
||||||
properties: any
|
properties: any
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
if (env.SELF_HOSTED && !env.isDev()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
console.log(
|
console.log(
|
||||||
`[audit] [tenant=${identity.tenantId}] [identity=${identity.id}] ${event}`
|
`[audit] [tenant=${identity.tenantId}] [identityType=${identity.type}] [identity=${identity.id}] ${event}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,14 @@ export default class PosthogProcessor implements EventProcessor {
|
||||||
async processEvent(
|
async processEvent(
|
||||||
event: Event,
|
event: Event,
|
||||||
identity: Identity,
|
identity: Identity,
|
||||||
properties: any
|
properties: any,
|
||||||
|
timestamp?: string | number
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
this.posthog.capture({ distinctId: identity.id, event, properties })
|
const payload: any = { distinctId: identity.id, event, properties }
|
||||||
|
if (timestamp) {
|
||||||
|
payload.timestamp = new Date(timestamp)
|
||||||
|
}
|
||||||
|
this.posthog.capture(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
async identify(identity: Identity) {
|
async identify(identity: Identity) {
|
||||||
|
|
|
@ -12,10 +12,11 @@ export default class Processor implements EventProcessor {
|
||||||
async processEvent(
|
async processEvent(
|
||||||
event: Event,
|
event: Event,
|
||||||
identity: Identity,
|
identity: Identity,
|
||||||
properties: any
|
properties: any,
|
||||||
|
timestamp?: string | number
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
for (const eventProcessor of this.processors) {
|
for (const eventProcessor of this.processors) {
|
||||||
await eventProcessor.processEvent(event, identity, properties)
|
await eventProcessor.processEvent(event, identity, properties, timestamp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,11 @@ export enum EventProcessorType {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EventProcessor {
|
export interface EventProcessor {
|
||||||
processEvent(event: Event, identity: Identity, properties: any): Promise<void>
|
processEvent(
|
||||||
|
event: Event,
|
||||||
|
identity: Identity,
|
||||||
|
properties: any,
|
||||||
|
timestamp?: string | number
|
||||||
|
): Promise<void>
|
||||||
shutdown(): void
|
shutdown(): void
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,13 @@ exports.runMigration = async (migration, options = {}) => {
|
||||||
const tenantId = getTenantId()
|
const tenantId = getTenantId()
|
||||||
const migrationType = migration.type
|
const migrationType = migration.type
|
||||||
const migrationName = migration.name
|
const migrationName = migration.name
|
||||||
|
const silent = migration.silent
|
||||||
|
|
||||||
|
const log = message => {
|
||||||
|
if (!silent) {
|
||||||
|
console.log(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// get the db to store the migration in
|
// get the db to store the migration in
|
||||||
let dbNames
|
let dbNames
|
||||||
|
@ -45,8 +52,14 @@ exports.runMigration = async (migration, options = {}) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const length = dbNames.length
|
||||||
|
let count = 0
|
||||||
|
|
||||||
// run the migration against each db
|
// run the migration against each db
|
||||||
for (const dbName of dbNames) {
|
for (const dbName of dbNames) {
|
||||||
|
count++
|
||||||
|
const lengthStatement = length > 1 ? `[${count}/${length}]` : ""
|
||||||
|
|
||||||
await doWithDB(dbName, async db => {
|
await doWithDB(dbName, async db => {
|
||||||
try {
|
try {
|
||||||
const doc = await exports.getMigrationsDoc(db)
|
const doc = await exports.getMigrationsDoc(db)
|
||||||
|
@ -58,7 +71,7 @@ exports.runMigration = async (migration, options = {}) => {
|
||||||
options.force[migrationType] &&
|
options.force[migrationType] &&
|
||||||
options.force[migrationType].includes(migrationName)
|
options.force[migrationType].includes(migrationName)
|
||||||
) {
|
) {
|
||||||
console.log(
|
log(
|
||||||
`[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Forcing`
|
`[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Forcing`
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -67,12 +80,12 @@ exports.runMigration = async (migration, options = {}) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(
|
log(
|
||||||
`[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Running`
|
`[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Running ${lengthStatement}`
|
||||||
)
|
)
|
||||||
// run the migration with tenant context
|
// run the migration with tenant context
|
||||||
await migration.fn(db)
|
await migration.fn(db)
|
||||||
console.log(
|
log(
|
||||||
`[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Complete`
|
`[Tenant: ${tenantId}] [Migration: ${migrationName}] [DB: ${dbName}] Complete`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -91,7 +104,6 @@ exports.runMigration = async (migration, options = {}) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.runMigrations = async (migrations, options = {}) => {
|
exports.runMigrations = async (migrations, options = {}) => {
|
||||||
console.log("Running migrations")
|
|
||||||
let tenantIds
|
let tenantIds
|
||||||
if (environment.MULTI_TENANCY) {
|
if (environment.MULTI_TENANCY) {
|
||||||
if (!options.tenantIds || !options.tenantIds.length) {
|
if (!options.tenantIds || !options.tenantIds.length) {
|
||||||
|
@ -105,8 +117,19 @@ exports.runMigrations = async (migrations, options = {}) => {
|
||||||
tenantIds = [DEFAULT_TENANT_ID]
|
tenantIds = [DEFAULT_TENANT_ID]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tenantIds.length > 1) {
|
||||||
|
console.log(`Checking migrations for ${tenantIds.length} tenants`)
|
||||||
|
} else {
|
||||||
|
console.log("Checking migrations")
|
||||||
|
}
|
||||||
|
|
||||||
|
let count = 0
|
||||||
// for all tenants
|
// for all tenants
|
||||||
for (const tenantId of tenantIds) {
|
for (const tenantId of tenantIds) {
|
||||||
|
count++
|
||||||
|
if (tenantIds.length > 1) {
|
||||||
|
console.log(`Progress [${count}/${tenantIds.length}]`)
|
||||||
|
}
|
||||||
// for all migrations
|
// for all migrations
|
||||||
for (const migration of migrations) {
|
for (const migration of migrations) {
|
||||||
// run the migration
|
// run the migration
|
||||||
|
|
|
@ -6,6 +6,7 @@ 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"
|
import * as screens from "./app/screens"
|
||||||
|
import * as global from "./global"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date:
|
* Date:
|
||||||
|
@ -16,6 +17,13 @@ import * as screens from "./app/screens"
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const run = async (appDb: any) => {
|
export const run = async (appDb: any) => {
|
||||||
|
if (await global.isComplete()) {
|
||||||
|
// make sure new apps aren't backfilled
|
||||||
|
// return if the global migration for this tenant is complete
|
||||||
|
// which runs after the app migrations
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
await apps.backfill(appDb)
|
await apps.backfill(appDb)
|
||||||
await automations.backfill(appDb)
|
await automations.backfill(appDb)
|
||||||
await datasources.backfill(appDb)
|
await datasources.backfill(appDb)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import * as users from "./global/users"
|
import * as users from "./global/users"
|
||||||
import * as rows from "./global/rows"
|
import * as rows from "./global/rows"
|
||||||
import * as configs from "./global/configs"
|
import * as configs from "./global/configs"
|
||||||
|
import { tenancy, events, migrations } from "@budibase/backend-core"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date:
|
* Date:
|
||||||
|
@ -11,7 +12,16 @@ import * as configs from "./global/configs"
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const run = async (db: any) => {
|
export const run = async (db: any) => {
|
||||||
|
const tenantId = tenancy.getTenantId()
|
||||||
|
await events.identification.identifyTenant(tenantId)
|
||||||
|
|
||||||
await users.backfill(db)
|
await users.backfill(db)
|
||||||
await rows.backfill()
|
await rows.backfill()
|
||||||
await configs.backfill(db)
|
await configs.backfill(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isComplete = async (): Promise<boolean> => {
|
||||||
|
const globalDb = tenancy.getGlobalDB()
|
||||||
|
const migrationsDoc = await migrations.getMigrationsDoc(globalDb)
|
||||||
|
return !!migrationsDoc.event_global_backfill
|
||||||
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ export const backfill = async (globalDb: any) => {
|
||||||
const users = await getUsers(globalDb)
|
const users = await getUsers(globalDb)
|
||||||
|
|
||||||
for (const user of users) {
|
for (const user of users) {
|
||||||
|
await events.identification.identifyUser(user)
|
||||||
await events.user.created(user)
|
await events.user.created(user)
|
||||||
|
|
||||||
if (user.admin?.global) {
|
if (user.admin?.global) {
|
||||||
|
|
|
@ -7,12 +7,14 @@ import * as appUrls from "./functions/appUrls"
|
||||||
import * as developerQuota from "./functions/developerQuota"
|
import * as developerQuota from "./functions/developerQuota"
|
||||||
import * as publishedAppsQuota from "./functions/publishedAppsQuota"
|
import * as publishedAppsQuota from "./functions/publishedAppsQuota"
|
||||||
import * as backfill from "./functions/backfill"
|
import * as backfill from "./functions/backfill"
|
||||||
|
import env from "../environment"
|
||||||
|
|
||||||
export interface Migration {
|
export interface Migration {
|
||||||
type: string
|
type: string
|
||||||
name: string
|
name: string
|
||||||
opts?: object
|
opts?: object
|
||||||
fn: Function
|
fn: Function
|
||||||
|
silent?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,16 +60,18 @@ export const MIGRATIONS: Migration[] = [
|
||||||
name: "published_apps_quota",
|
name: "published_apps_quota",
|
||||||
fn: publishedAppsQuota.run,
|
fn: publishedAppsQuota.run,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
type: migrations.MIGRATION_TYPES.GLOBAL,
|
|
||||||
name: "event_global_backfill",
|
|
||||||
fn: backfill.global.run,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: migrations.MIGRATION_TYPES.APP,
|
type: migrations.MIGRATION_TYPES.APP,
|
||||||
name: "event_app_backfill",
|
name: "event_app_backfill",
|
||||||
opts: { all: true },
|
opts: { all: true },
|
||||||
fn: backfill.app.run,
|
fn: backfill.app.run,
|
||||||
|
silent: !!env.SELF_HOSTED, // reduce noisy logging
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: migrations.MIGRATION_TYPES.GLOBAL,
|
||||||
|
name: "event_global_backfill",
|
||||||
|
fn: backfill.global.run,
|
||||||
|
silent: !!env.SELF_HOSTED, // reduce noisy logging
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,17 @@ import { Hosting } from "../core"
|
||||||
|
|
||||||
export enum IdentityType {
|
export enum IdentityType {
|
||||||
USER = "user", // cloud and self hosted users
|
USER = "user", // cloud and self hosted users
|
||||||
ACCOUNT = "account", // self hosted accounts
|
|
||||||
TENANT = "tenant", // cloud and self hosted tenants
|
TENANT = "tenant", // cloud and self hosted tenants
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Identity {
|
export interface Identity {
|
||||||
id: string
|
id: string
|
||||||
tenantId: string
|
tenantId: string
|
||||||
|
type: IdentityType
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TenantIdentity extends Identity {
|
export interface TenantIdentity extends Identity {
|
||||||
hosting: Hosting
|
hosting: Hosting
|
||||||
type: IdentityType
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserIdentity extends TenantIdentity {
|
export interface UserIdentity extends TenantIdentity {
|
||||||
|
|
Loading…
Reference in New Issue