package domain import ( "context" "fmt" "strings" "gitlab.com/unboundsoftware/eventsourced/eventsourced" "gitlab.com/unboundsoftware/schemas/hash" ) type AddOrganization struct { Name string Initiator string } func (a AddOrganization) Validate(_ context.Context, aggregate eventsourced.Aggregate) error { if aggregate.Identity() != nil { return fmt.Errorf("organization already exists") } if len(a.Name) == 0 { return fmt.Errorf("name is required") } return nil } func (a AddOrganization) Event(context.Context) eventsourced.Event { return &OrganizationAdded{ Name: a.Name, Initiator: a.Initiator, } } var _ eventsourced.Command = AddOrganization{} type AddAPIKey struct { Name string Key string Refs []string Read bool Publish bool Initiator string } func (a AddAPIKey) Validate(_ context.Context, aggregate eventsourced.Aggregate) error { if aggregate.Identity() == nil { return fmt.Errorf("organization does not exist") } for _, k := range aggregate.(*Organization).APIKeys { if k.Name == a.Name { return fmt.Errorf("a key named '%s' already exist", a.Name) } } return nil } func (a AddAPIKey) Event(context.Context) eventsourced.Event { // Hash the API key using bcrypt for secure storage // Note: We can't return an error here, but bcrypt errors are extremely rare // (only if system runs out of memory or bcrypt cost is invalid) // We use a fixed cost of 12 which is always valid hashedKey, err := hash.APIKey(a.Key) if err != nil { // This should never happen with bcrypt cost 12, but if it does, // we'll store an empty hash which will fail validation later hashedKey = "" } return &APIKeyAdded{ Name: a.Name, Key: hashedKey, Refs: a.Refs, Read: a.Read, Publish: a.Publish, Initiator: a.Initiator, } } var _ eventsourced.Command = AddAPIKey{} type UpdateSubGraph struct { OrganizationId string Ref string Service string Url *string WSUrl *string Sdl string Initiator string } func (u UpdateSubGraph) Validate(_ context.Context, aggregate eventsourced.Aggregate) error { switch a := aggregate.(type) { case *SubGraph: if strings.TrimSpace(u.Ref) == "" { return fmt.Errorf("ref is missing") } if strings.TrimSpace(u.Service) == "" { return fmt.Errorf("service is missing") } if strings.TrimSpace(u.Sdl) == "" { return fmt.Errorf("SDL is missing") } if (u.Url == nil || strings.TrimSpace(*u.Url) == "") && a.Url == nil { return fmt.Errorf("url is missing") } default: return fmt.Errorf("aggregate is not a SubGraph") } return nil } func (u UpdateSubGraph) Event(context.Context) eventsourced.Event { return &SubGraphUpdated{ OrganizationId: u.OrganizationId, Ref: u.Ref, Service: u.Service, Url: u.Url, WSUrl: u.WSUrl, Sdl: u.Sdl, Initiator: u.Initiator, } } var _ eventsourced.Command = UpdateSubGraph{}