73eae98929
schemas / vulnerabilities (pull_request) Successful in 2m15s
schemas / check-release (pull_request) Successful in 2m17s
schemas / check (pull_request) Successful in 4m48s
pre-commit / pre-commit (pull_request) Successful in 5m58s
schemas / build (pull_request) Successful in 3m36s
schemas / deploy-prod (pull_request) Has been skipped
- Update git remote to git.unbound.se - Add Gitea workflows: ci.yaml, pre-commit.yaml, release.yaml, goreleaser.yaml - Delete .gitlab-ci.yml - Update Go module path to gitea.unbound.se/unboundsoftware/schemas - Update all imports to new module path - Update Docker registry to oci.unbound.se - Update .goreleaser.yml for Gitea releases with internal cluster URL - Remove GitLab CI linter from pre-commit config - Use shared release workflow with tag_only for versioning Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
208 lines
4.8 KiB
Go
208 lines
4.8 KiB
Go
package domain
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
|
|
|
"gitea.unbound.se/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 AddUserToOrganization struct {
|
|
UserId string
|
|
Initiator string
|
|
}
|
|
|
|
func (a AddUserToOrganization) Validate(_ context.Context, aggregate eventsourced.Aggregate) error {
|
|
if aggregate.Identity() == nil {
|
|
return fmt.Errorf("organization does not exist")
|
|
}
|
|
if len(a.UserId) == 0 {
|
|
return fmt.Errorf("userId is required")
|
|
}
|
|
// Check if user is already in the organization
|
|
org := aggregate.(*Organization)
|
|
for _, user := range org.Users {
|
|
if user == a.UserId {
|
|
return fmt.Errorf("user is already a member of this organization")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a AddUserToOrganization) Event(context.Context) eventsourced.Event {
|
|
return &UserAddedToOrganization{
|
|
UserId: a.UserId,
|
|
Initiator: a.Initiator,
|
|
}
|
|
}
|
|
|
|
var _ eventsourced.Command = AddUserToOrganization{}
|
|
|
|
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 RemoveAPIKey struct {
|
|
KeyName string
|
|
Initiator string
|
|
}
|
|
|
|
func (r RemoveAPIKey) Validate(_ context.Context, aggregate eventsourced.Aggregate) error {
|
|
if aggregate.Identity() == nil {
|
|
return fmt.Errorf("organization does not exist")
|
|
}
|
|
org := aggregate.(*Organization)
|
|
found := false
|
|
for _, k := range org.APIKeys {
|
|
if k.Name == r.KeyName {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
return fmt.Errorf("API key '%s' not found", r.KeyName)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r RemoveAPIKey) Event(context.Context) eventsourced.Event {
|
|
return &APIKeyRemoved{
|
|
KeyName: r.KeyName,
|
|
Initiator: r.Initiator,
|
|
}
|
|
}
|
|
|
|
var _ eventsourced.Command = RemoveAPIKey{}
|
|
|
|
type RemoveOrganization struct {
|
|
Initiator string
|
|
}
|
|
|
|
func (r RemoveOrganization) Validate(_ context.Context, aggregate eventsourced.Aggregate) error {
|
|
if aggregate.Identity() == nil {
|
|
return fmt.Errorf("organization does not exist")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r RemoveOrganization) Event(context.Context) eventsourced.Event {
|
|
return &OrganizationRemoved{
|
|
Initiator: r.Initiator,
|
|
}
|
|
}
|
|
|
|
var _ eventsourced.Command = RemoveOrganization{}
|
|
|
|
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{}
|