budibase/packages/backend-core/src/events/backfill.ts

184 lines
4.9 KiB
TypeScript

import {
Event,
BackfillMetadata,
CachedEvent,
SSOCreatedEvent,
AutomationCreatedEvent,
AutomationStepCreatedEvent,
DatasourceCreatedEvent,
LayoutCreatedEvent,
QueryCreatedEvent,
RoleCreatedEvent,
ScreenCreatedEvent,
TableCreatedEvent,
ViewCreatedEvent,
ViewCalculationCreatedEvent,
ViewFilterCreatedEvent,
AppPublishedEvent,
UserCreatedEvent,
RoleAssignedEvent,
UserPermissionAssignedEvent,
AppCreatedEvent,
} from "@budibase/types"
import * as context from "../context"
import { CacheKeys } from "../cache/generic"
import * as cache from "../cache/generic"
// LIFECYCLE
export const start = async (events: Event[]) => {
const metadata: BackfillMetadata = {
eventWhitelist: events,
}
return saveBackfillMetadata(metadata)
}
export const recordEvent = async (event: Event, properties: any) => {
const eventKey = getEventKey(event, properties)
// don't use a ttl - cleaned up by migration
// don't use tenancy - already in the key
await cache.store(eventKey, properties, undefined, { useTenancy: false })
}
export const end = async () => {
await deleteBackfillMetadata()
await clearEvents()
}
// CRUD
const getBackfillMetadata = async (): Promise<BackfillMetadata | null> => {
return cache.get(CacheKeys.BACKFILL_METADATA)
}
const saveBackfillMetadata = async (
backfill: BackfillMetadata
): Promise<void> => {
// no TTL - deleted by backfill
return cache.store(CacheKeys.BACKFILL_METADATA, backfill)
}
const deleteBackfillMetadata = async (): Promise<void> => {
await cache.delete(CacheKeys.BACKFILL_METADATA)
}
const clearEvents = async () => {
// wildcard
const pattern = getEventKey()
const keys = await cache.keys(pattern)
for (const key of keys) {
// delete each key
// don't use tenancy, already in the key
await cache.delete(key, { useTenancy: false })
}
}
// HELPERS
export const isBackfillingEvent = async (event: Event) => {
const backfill = await getBackfillMetadata()
const events = backfill?.eventWhitelist
if (events && events.includes(event)) {
return true
} else {
return false
}
}
export const isAlreadySent = async (event: Event, properties: any) => {
const eventKey = getEventKey(event, properties)
const cachedEvent: CachedEvent = await cache.get(eventKey, {
useTenancy: false,
})
return !!cachedEvent
}
const CUSTOM_PROPERTY_SUFFIX: any = {
// APP EVENTS
[Event.AUTOMATION_CREATED]: (properties: AutomationCreatedEvent) => {
return properties.automationId
},
[Event.AUTOMATION_STEP_CREATED]: (properties: AutomationStepCreatedEvent) => {
return properties.stepId
},
[Event.DATASOURCE_CREATED]: (properties: DatasourceCreatedEvent) => {
return properties.datasourceId
},
[Event.LAYOUT_CREATED]: (properties: LayoutCreatedEvent) => {
return properties.layoutId
},
[Event.QUERY_CREATED]: (properties: QueryCreatedEvent) => {
return properties.queryId
},
[Event.ROLE_CREATED]: (properties: RoleCreatedEvent) => {
return properties.roleId
},
[Event.SCREEN_CREATED]: (properties: ScreenCreatedEvent) => {
return properties.screenId
},
[Event.TABLE_CREATED]: (properties: TableCreatedEvent) => {
return properties.tableId
},
[Event.VIEW_CREATED]: (properties: ViewCreatedEvent) => {
return properties.tableId // best uniqueness
},
[Event.VIEW_CALCULATION_CREATED]: (
properties: ViewCalculationCreatedEvent
) => {
return properties.tableId // best uniqueness
},
[Event.VIEW_FILTER_CREATED]: (properties: ViewFilterCreatedEvent) => {
return properties.tableId // best uniqueness
},
[Event.APP_CREATED]: (properties: AppCreatedEvent) => {
return properties.appId // best uniqueness
},
[Event.APP_PUBLISHED]: (properties: AppPublishedEvent) => {
return properties.appId // best uniqueness
},
// GLOBAL EVENTS
[Event.AUTH_SSO_CREATED]: (properties: SSOCreatedEvent) => {
return properties.type
},
[Event.AUTH_SSO_ACTIVATED]: (properties: SSOCreatedEvent) => {
return properties.type
},
[Event.USER_CREATED]: (properties: UserCreatedEvent) => {
return properties.userId
},
[Event.USER_PERMISSION_ADMIN_ASSIGNED]: (
properties: UserPermissionAssignedEvent
) => {
return properties.userId
},
[Event.USER_PERMISSION_BUILDER_ASSIGNED]: (
properties: UserPermissionAssignedEvent
) => {
return properties.userId
},
[Event.ROLE_ASSIGNED]: (properties: RoleAssignedEvent) => {
return `${properties.roleId}-${properties.userId}`
},
}
const getEventKey = (event?: Event, properties?: any) => {
let eventKey: string
const tenantId = context.getTenantId()
if (event) {
eventKey = `${CacheKeys.EVENTS}:${tenantId}:${event}`
// use some properties to make the key more unique
const custom = CUSTOM_PROPERTY_SUFFIX[event]
const suffix = custom ? custom(properties) : undefined
if (suffix) {
eventKey = `${eventKey}:${suffix}`
}
} else {
eventKey = `${CacheKeys.EVENTS}:${tenantId}:*`
}
return eventKey
}