openmeter / app /event.go
Leon4gr45's picture
Upload folder using huggingface_hub
429334c verified
Raw
History Blame Contribute Delete
5.29 kB
package app
import (
"context"
"encoding/json"
"fmt"
"reflect"
"github.com/oklog/ulid/v2"
"github.com/openmeterio/openmeter/openmeter/event/metadata"
"github.com/openmeterio/openmeter/openmeter/session"
)
// EventAppParser should be implemented by the app's meta contents to be parsable from an EventApp
type EventAppParser interface {
FromEventAppData(EventApp) error
}
type EventAppData map[string]any
// NewEventAppData creates a new EventAppData from a given value
// TODO[later]: we need to refactor apps to be able to handle serialization more gracefully, e.g. having a proper
// union type for app instead of the interface
func NewEventAppData(v any) (EventAppData, error) {
jsonBytes, err := json.Marshal(v)
if err != nil {
return nil, err
}
var data EventAppData
if err := json.Unmarshal(jsonBytes, &data); err != nil {
return nil, err
}
return data, nil
}
// ParseInto parses the EventAppData into a given value, the value must be a pointer
func (e EventAppData) ParseInto(v any) error {
if rv := reflect.ValueOf(v); rv.Kind() != reflect.Pointer || rv.IsNil() {
return fmt.Errorf("target must be a non-nil pointer")
}
jsonBytes, err := json.Marshal(e)
if err != nil {
return err
}
if err := json.Unmarshal(jsonBytes, v); err != nil {
return err
}
return nil
}
type EventApp struct {
AppBase
AppData EventAppData `json:"appData"`
}
func NewEventApp(app App) (EventApp, error) {
appBase := app.GetAppBase()
appData, err := app.GetEventAppData()
if err != nil {
return EventApp{}, err
}
return EventApp{
AppBase: appBase,
AppData: appData,
}, nil
}
const (
AppEventSubsystem metadata.EventSubsystem = "app"
AppCreateEventName metadata.EventName = "app.created"
AppUpdateEventName metadata.EventName = "app.updated"
AppDeleteEventName metadata.EventName = "app.deleted"
)
// NewAppCreateEvent creates a new app create event
// TODO[later]: We should use eventApp instead of AppBase, but the creation flow is somewhat tricky to change as the flow
// is that the app calls the AppCreate without having the configuration presisted.
func NewAppCreateEvent(ctx context.Context, appBase AppBase) AppCreateEvent {
return AppCreateEvent{
AppBase: appBase,
UserID: session.GetSessionUserID(ctx),
}
}
// AppCreateEvent is an event that is emitted when an app is created
type AppCreateEvent struct {
AppBase
UserID *string `json:"userId,omitempty"`
}
func (e AppCreateEvent) EventName() string {
return metadata.GetEventName(metadata.EventType{
Subsystem: AppEventSubsystem,
Name: AppCreateEventName,
Version: "v1",
})
}
func (e AppCreateEvent) EventMetadata() metadata.EventMetadata {
resourcePath := metadata.ComposeResourcePath(e.AppBase.Namespace, metadata.EntityApp, e.AppBase.ID)
return metadata.EventMetadata{
ID: ulid.Make().String(),
Source: resourcePath,
Subject: resourcePath,
Time: e.AppBase.CreatedAt,
}
}
func (e AppCreateEvent) Validate() error {
if e.AppBase.ID == "" {
return fmt.Errorf("app base is required")
}
return nil
}
// NewAppUpdateEvent creates a new app update event
func NewAppUpdateEvent(ctx context.Context, app App) (AppUpdateEvent, error) {
eventApp, err := NewEventApp(app)
if err != nil {
return AppUpdateEvent{}, err
}
return AppUpdateEvent{
EventApp: eventApp,
UserID: session.GetSessionUserID(ctx),
}, nil
}
// AppUpdateEvent is an event that is emitted when an app is updated
type AppUpdateEvent struct {
EventApp
UserID *string `json:"userId,omitempty"`
}
func (e AppUpdateEvent) EventName() string {
return metadata.GetEventName(metadata.EventType{
Subsystem: AppEventSubsystem,
Name: AppUpdateEventName,
Version: "v2",
})
}
func (e AppUpdateEvent) EventMetadata() metadata.EventMetadata {
appBase := e.AppBase.GetAppBase()
resourcePath := metadata.ComposeResourcePath(appBase.Namespace, metadata.EntityApp, appBase.ID)
return metadata.EventMetadata{
ID: ulid.Make().String(),
Source: resourcePath,
Subject: resourcePath,
Time: appBase.UpdatedAt,
}
}
func (e AppUpdateEvent) Validate() error {
if e.AppBase.ID == "" {
return fmt.Errorf("app base is required")
}
return nil
}
// NewAppDeleteEvent creates a new app delete event
func NewAppDeleteEvent(ctx context.Context, app AppBase, appData EventAppData) AppDeleteEvent {
return AppDeleteEvent{
EventApp: EventApp{
AppBase: app,
AppData: appData,
},
UserID: session.GetSessionUserID(ctx),
}
}
// AppDeleteEvent is an event that is emitted when an app is deleted
type AppDeleteEvent struct {
EventApp
UserID *string `json:"userId,omitempty"`
}
func (e AppDeleteEvent) EventName() string {
return metadata.GetEventName(metadata.EventType{
Subsystem: AppEventSubsystem,
Name: AppDeleteEventName,
Version: "v2",
})
}
func (e AppDeleteEvent) EventMetadata() metadata.EventMetadata {
resourcePath := metadata.ComposeResourcePath(e.AppBase.Namespace, metadata.EntityApp, e.AppBase.ID)
return metadata.EventMetadata{
ID: ulid.Make().String(),
Source: resourcePath,
Subject: resourcePath,
Time: *e.AppBase.DeletedAt,
}
}
func (e AppDeleteEvent) Validate() error {
if e.AppBase.ID == "" {
return fmt.Errorf("app base is required")
}
return nil
}