From 698e8d879924fae6f0661a8c8b7bcd582f46d8df Mon Sep 17 00:00:00 2001 From: Grady Ward Date: Thu, 31 Aug 2023 09:33:12 -0600 Subject: [PATCH] Golang Initial Domain Types Signed-off-by: Grady Ward --- pacta/BUILD.bazel | 21 ++ pacta/clone_test.go | 202 +++++++++++++ pacta/enum_test.go | 53 ++++ pacta/pacta.go | 688 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 964 insertions(+) create mode 100644 pacta/BUILD.bazel create mode 100644 pacta/clone_test.go create mode 100644 pacta/enum_test.go create mode 100644 pacta/pacta.go diff --git a/pacta/BUILD.bazel b/pacta/BUILD.bazel new file mode 100644 index 0000000..c104590 --- /dev/null +++ b/pacta/BUILD.bazel @@ -0,0 +1,21 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "pacta", + srcs = ["pacta.go"], + importpath = "github.com/RMI/pacta/pacta", + visibility = ["//visibility:public"], +) + +go_test( + name = "pacta_test", + srcs = [ + "clone_test.go", + "enum_test.go", + ], + embed = [":pacta"], + deps = [ + "@com_github_google_go_cmp//cmp", + "@com_github_google_go_cmp//cmp/cmpopts", + ], +) diff --git a/pacta/clone_test.go b/pacta/clone_test.go new file mode 100644 index 0000000..47e4d81 --- /dev/null +++ b/pacta/clone_test.go @@ -0,0 +1,202 @@ +package pacta + +import ( + "fmt" + "math/rand" + "reflect" + "testing" + "time" + "unicode" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" +) + +func TestClonePACTAVersion(t *testing.T) { testClone(t, &PACTAVersion{}) } +func TestCloneUser(t *testing.T) { testClone(t, &User{}) } +func TestCloneInitiative(t *testing.T) { testClone(t, &Initiative{}) } +func TestCloneInitiativeInvitation(t *testing.T) { testClone(t, &InitiativeInvitation{}) } +func TestCloneInitiativeUserRelationship(t *testing.T) { testClone(t, &InitiativeUserRelationship{}) } +func TestCloneBlob(t *testing.T) { testClone(t, &Blob{}) } +func TestCloneOwner(t *testing.T) { testClone(t, &Owner{}) } +func TestCloneIncompleteUpload(t *testing.T) { testClone(t, &IncompleteUpload{}) } +func TestClonePortfolio(t *testing.T) { testClone(t, &Portfolio{}) } +func TestClonePortfolioGroup(t *testing.T) { testClone(t, &PortfolioGroup{}) } +func TestClonePortfolioGroupMembership(t *testing.T) { testClone(t, &PortfolioGroupMembership{}) } +func TestClonePortfolioSnapshot(t *testing.T) { testClone(t, &PortfolioSnapshot{}) } +func TestCloneAnalysis(t *testing.T) { testClone(t, &Analysis{}) } +func TestCloneAnalysisArtifact(t *testing.T) { testClone(t, &AnalysisArtifact{}) } +func TestCloneAuditLog(t *testing.T) { testClone(t, &AuditLog{}) } +func TestClonePortfolioInitiativeMembership(t *testing.T) { + testClone(t, &PortfolioInitiativeMembership{}) +} + +func testClone[C cloneable[C]](t *testing.T, c C) { + r := rand.New(rand.NewSource(0)) + t.Helper() + original := c + populateStruct(r, original, []string{fmt.Sprintf("%T", original)}) + cloned := original.Clone() + + if diff := cmp.Diff(original, cloned, cmpopts.EquateEmpty()); diff != "" { + t.Errorf("unexpected diff:\n%s", diff) + } + + checkPointers(t, reflect.ValueOf(original).Elem(), reflect.ValueOf(cloned).Elem()) + + var cNil C + clonedNil := cNil.Clone() + if clonedNil != cNil { + t.Errorf("expected nil, got %v", clonedNil) + } +} + +// checkPointers makes sure that our Clone() implementations are doing deep +// copies. Keeping pointers to the same substructs after cloning runs the risk +// of accidentally mutating the original data. +func checkPointers(t *testing.T, v1, v2 reflect.Value) { + t.Helper() + + switch v1.Kind() { + case reflect.Ptr: + if v1.IsNil() || v2.IsNil() { + t.Errorf("v1 = %v, v2 = %v", v1.Pointer(), v2.Pointer()) + return + } + + // If the field value is a pointer, check if it's a deep copy + if v1.Pointer() == v2.Pointer() { + t.Error("pointer addresses are identical, not a deep copy") + } + case reflect.Struct: + for i := 0; i < v1.NumField(); i++ { + if isFieldUnexported(v1.Type(), i) { + continue + } + field1 := v1.Field(i) + field2 := v2.Field(i) + // If the field value is a struct, recursively check its fields + checkPointers(t, field1, field2) + } + case reflect.Slice: + for j := 0; j < v1.Len(); j++ { + checkPointers(t, v1.Index(j), v2.Index(j)) + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64, + reflect.String, reflect.Bool: + // We don't care about primitives, they're handled by our standard cmp.Diff check. + } +} + +func isFieldUnexported(typ reflect.Type, idx int) bool { + structField := typ.Field(idx) + return !unicode.IsUpper(rune(structField.Name[0])) +} + +func populateStruct(r *rand.Rand, s interface{}, seenTypes []string) { + value := reflect.ValueOf(s).Elem() + ts := fmt.Sprintf("%T", s) + newST := append([]string{ts}, seenTypes...) + for i := 0; i < value.NumField(); i++ { + fieldValue := value.Field(i) + populateField(r, fieldValue, newST) + } +} + +func populateField(r *rand.Rand, fieldValue reflect.Value, seenTypes []string) { + if !fieldValue.CanSet() { + return + } + if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() { + fieldValue.Set(reflect.New(fieldValue.Type().Elem())) + fieldValue = fieldValue.Elem() + } + + switch fieldValue.Kind() { + case reflect.Struct: + ts := fmt.Sprintf("%T", fieldValue.Addr().Interface()) + // panic("oh no") + if fieldValue.Type() == reflect.TypeOf(time.Time{}) { + fieldValue.Set(reflect.ValueOf(getRandomTime(r))) + } + seen := false + for _, st := range seenTypes { + if ts == st { + seen = true + } + } + if !seen { + populateStruct(r, fieldValue.Addr().Interface(), seenTypes) + } + case reflect.Map: + if fieldValue.IsNil() { + fieldValue.Set(reflect.MakeMap(fieldValue.Type())) + } + populateMap(r, fieldValue, seenTypes) + case reflect.Ptr: + populateField(r, fieldValue.Elem(), seenTypes) + default: + setRandomValue(r, fieldValue, seenTypes) + } +} + +func setRandomValue(r *rand.Rand, fieldValue reflect.Value, seenTypes []string) { + switch fieldValue.Kind() { + case reflect.String: + fieldValue.SetString(fmt.Sprintf("%d", r.Int())) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + fieldValue.SetInt(r.Int63()) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + fieldValue.SetUint(r.Uint64()) + case reflect.Float32, reflect.Float64: + fieldValue.SetFloat(r.Float64()) + case reflect.Bool: + fieldValue.SetBool(true) + case reflect.Slice: + setRandomSliceValue(r, fieldValue, seenTypes) + default: + panic(fmt.Sprintf("unsupported field value: %+v %+v", fieldValue.Kind(), fieldValue)) + } +} + +func setRandomSliceValue(r *rand.Rand, fieldValue reflect.Value, seenTypes []string) { + length := r.Intn(10) + 1 + slice := reflect.MakeSlice(fieldValue.Type(), length, length) + + for i := 0; i < length; i++ { + populateField(r, slice.Index(i), seenTypes) + } + + fieldValue.Set(slice) +} + +func populateMap(r *rand.Rand, mapValue reflect.Value, seenTypes []string) { + mapKeyType := mapValue.Type().Key() + mapValueType := mapValue.Type().Elem() + + length := r.Intn(10) + 1 + + for i := 0; i < length; i++ { + key := reflect.New(mapKeyType).Elem() + value := reflect.New(mapValueType).Elem() + + populateField(r, key, seenTypes) + populateField(r, value, seenTypes) + + mapValue.SetMapIndex(key, value) + } +} + +func getRandomTime(r *rand.Rand) time.Time { + year := r.Intn(20) + 2000 + month := time.Month(r.Intn(12) + 1) + day := r.Intn(28) + 1 + hour := r.Intn(24) + minute := r.Intn(60) + second := r.Intn(60) + nanosecond := r.Intn(1000000000) + + return time.Date(year, month, day, hour, minute, second, nanosecond, time.UTC) +} diff --git a/pacta/enum_test.go b/pacta/enum_test.go new file mode 100644 index 0000000..ae35e73 --- /dev/null +++ b/pacta/enum_test.go @@ -0,0 +1,53 @@ +package pacta + +import "testing" + +func TestParseAuthMechanism(t *testing.T) { + testParseEnum(t, AuthnMechanismValues, ParseAuthnMechanism) +} + +func TestParseLanguage(t *testing.T) { + testParseEnum(t, LanguageValues, ParseLanguage) +} + +func TestParseFileType(t *testing.T) { + testParseEnum(t, FileTypeValues, ParseFileType) +} + +func TestParseFailureCode(t *testing.T) { + testParseEnum(t, FailureCodeValues, ParseFailureCode) +} + +func TestParseAnalysisType(t *testing.T) { + testParseEnum(t, AnalysisTypeValues, ParseAnalysisType) +} + +func TestParseAuditLogAction(t *testing.T) { + testParseEnum(t, AuditLogActionValues, ParseAuditLogAction) +} + +func TestParseAuditLogActorType(t *testing.T) { + testParseEnum(t, AuditLogActorTypeValues, ParseAuditLogActorType) +} + +func TestParseAuditLogTargetType(t *testing.T) { + testParseEnum(t, AuditLogTargetTypeValues, ParseAuditLogTargetType) +} + +func testParseEnum[E ~string](t *testing.T, es []E, fn func(string) (E, error)) { + t.Helper() + for _, e := range es { + s := string(e) + e2, err := fn(s) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if e != e2 { + t.Errorf("expected %v, got %v", e, e2) + } + } + e, err := fn("invalid") + if err == nil { + t.Errorf("expected error, got %v", e) + } +} diff --git a/pacta/pacta.go b/pacta/pacta.go new file mode 100644 index 0000000..e97f02d --- /dev/null +++ b/pacta/pacta.go @@ -0,0 +1,688 @@ +// Package pacta contains domain types for the PACTA ecosystem +package pacta + +import ( + "fmt" + "time" +) + +type AuthnMechanism string + +const ( + AuthnMechanism_EmailAndPass AuthnMechanism = "EMAIL_AND_PASS" +) + +var AuthnMechanismValues = []AuthnMechanism{ + AuthnMechanism_EmailAndPass, +} + +func ParseAuthnMechanism(s string) (AuthnMechanism, error) { + if s == "EMAIL_AND_PASS" { + return AuthnMechanism_EmailAndPass, nil + } + return "", fmt.Errorf("unknown AuthnMechanism: %q", s) +} + +type Language string + +const ( + Language_EN Language = "en" + Language_DE Language = "de" + Language_FR Language = "fr" + Language_ES Language = "es" + Language_Unspecified Language = "unspecified" +) + +var LanguageValues = []Language{ + Language_EN, + Language_DE, + Language_FR, + Language_ES, +} + +func ParseLanguage(s string) (Language, error) { + switch s { + case "en": + return Language_EN, nil + case "de": + return Language_DE, nil + case "fr": + return Language_FR, nil + case "es": + return Language_ES, nil + case "unspecified": + return Language_Unspecified, nil + } + return "", fmt.Errorf("unknown Language: %q", s) +} + +type PACTAVersionID string +type PACTAVersion struct { + ID PACTAVersionID + Name string + Description string + Digest string + CreatedAt time.Time + IsDefault bool +} + +func (o *PACTAVersion) Clone() *PACTAVersion { + if o == nil { + return nil + } + return &PACTAVersion{ + ID: o.ID, + Name: o.Name, + Description: o.Description, + Digest: o.Digest, + CreatedAt: o.CreatedAt, + IsDefault: o.IsDefault, + } +} + +type UserID string +type User struct { + ID UserID + AuthnMechanism AuthnMechanism + AuthnID string + EnteredEmail string + CanonicalEmail string + Admin bool + SuperAdmin bool + Name string + PreferredLanguage Language + CountryCode string + CreatedAt time.Time +} + +func (o *User) Clone() *User { + if o == nil { + return nil + } + return &User{ + ID: o.ID, + AuthnMechanism: o.AuthnMechanism, + AuthnID: o.AuthnID, + EnteredEmail: o.EnteredEmail, + CanonicalEmail: o.CanonicalEmail, + Admin: o.Admin, + SuperAdmin: o.SuperAdmin, + Name: o.Name, + PreferredLanguage: o.PreferredLanguage, + CountryCode: o.CountryCode, + CreatedAt: o.CreatedAt, + } +} + +type InitiativeID string +type Initiative struct { + ID InitiativeID + Name string + Affiliation string + PublicDescription string + InternalDescription string + RequiresInvitationToJoin bool + IsAcceptingNewMembers bool + IsAcceptingNewPortfolios bool + PACTAVersion *PACTAVersion + Language Language + CreatedAt time.Time + UserRelationships []*InitiativeUserRelationship + PortfolioRelationships []*PortfolioInitiativeMembership + Invitations []*InitiativeInvitation +} + +func (o *Initiative) Clone() *Initiative { + if o == nil { + return nil + } + return &Initiative{ + ID: o.ID, + Name: o.Name, + Affiliation: o.Affiliation, + PublicDescription: o.PublicDescription, + InternalDescription: o.InternalDescription, + RequiresInvitationToJoin: o.RequiresInvitationToJoin, + IsAcceptingNewMembers: o.IsAcceptingNewMembers, + IsAcceptingNewPortfolios: o.IsAcceptingNewPortfolios, + PACTAVersion: o.PACTAVersion.Clone(), + Language: o.Language, + CreatedAt: o.CreatedAt, + UserRelationships: cloneAll(o.UserRelationships), + PortfolioRelationships: cloneAll(o.PortfolioRelationships), + Invitations: cloneAll(o.Invitations), + } +} + +type InitiativeInvitationID string +type InitiativeInvitation struct { + ID InitiativeInvitationID + Initiative *Initiative + CreatedAt time.Time + UsedAt time.Time + UsedBy *User +} + +func (o *InitiativeInvitation) Clone() *InitiativeInvitation { + if o == nil { + return nil + } + return &InitiativeInvitation{ + ID: o.ID, + Initiative: o.Initiative.Clone(), + CreatedAt: o.CreatedAt, + UsedAt: o.UsedAt, + UsedBy: o.UsedBy.Clone(), + } +} + +type InitiativeUserRelationship struct { + Initiative *Initiative + User *User + Manager bool + Member bool + UpdatedAt time.Time +} + +func (o *InitiativeUserRelationship) Clone() *InitiativeUserRelationship { + if o == nil { + return nil + } + return &InitiativeUserRelationship{ + Initiative: o.Initiative.Clone(), + User: o.User.Clone(), + Manager: o.Manager, + Member: o.Member, + UpdatedAt: o.UpdatedAt, + } +} + +type FileType string + +const ( + FileType_CSV = "csv" + FileType_YAML = "yaml" + FileType_ZIP = "zip" + FileType_HTML = "html" +) + +var FileTypeValues = []FileType{ + FileType_CSV, + FileType_YAML, + FileType_ZIP, + FileType_HTML, +} + +func ParseFileType(s string) (FileType, error) { + switch s { + case "csv": + return FileType_CSV, nil + case "yaml": + return FileType_YAML, nil + case "zip": + return FileType_ZIP, nil + case "html": + return FileType_HTML, nil + } + return "", fmt.Errorf("unknown FileType: %q", s) +} + +type BlobURI string +type BlobID string +type Blob struct { + ID BlobID + BlobURI BlobURI + FileType FileType + FileName string + CreatedAt time.Time +} + +func (o *Blob) Clone() *Blob { + if o == nil { + return nil + } + return &Blob{ + ID: o.ID, + BlobURI: o.BlobURI, + FileType: o.FileType, + FileName: o.FileName, + CreatedAt: o.CreatedAt, + } +} + +type OwnerID string +type Owner struct { + ID OwnerID + User *User + Initiative *Initiative +} + +func (o *Owner) Clone() *Owner { + if o == nil { + return nil + } + return &Owner{ + ID: o.ID, + User: o.User.Clone(), + Initiative: o.Initiative.Clone(), + } +} + +type FailureCode string + +const ( + FailureCode_Unknown FailureCode = "UNKNOWN" +) + +var FailureCodeValues = []FailureCode{ + FailureCode_Unknown, +} + +func ParseFailureCode(s string) (FailureCode, error) { + switch s { + case "UNKNOWN": + return FailureCode_Unknown, nil + } + return "", fmt.Errorf("unknown FailureCode: %q", s) +} + +type IncompleteUploadID string +type IncompleteUpload struct { + ID IncompleteUploadID + Name string + Description string + CreatedAt time.Time + HoldingsDate time.Time + RanAt time.Time + CompletedAt time.Time + FailureCode FailureCode + FailureMessage string + AdminDebugEnabled bool + Owner *Owner + Blob *Blob +} + +func (o *IncompleteUpload) Clone() *IncompleteUpload { + if o == nil { + return nil + } + return &IncompleteUpload{ + ID: o.ID, + Name: o.Name, + Description: o.Description, + CreatedAt: o.CreatedAt, + HoldingsDate: o.HoldingsDate, + RanAt: o.RanAt, + CompletedAt: o.CompletedAt, + FailureCode: o.FailureCode, + FailureMessage: o.FailureMessage, + AdminDebugEnabled: o.AdminDebugEnabled, + Owner: o.Owner.Clone(), + Blob: o.Blob.Clone(), + } +} + +type PortfolioID string +type Portfolio struct { + ID PortfolioID + Name string + Description string + CreatedAt time.Time + HoldingsDate time.Time + Owner *Owner + Blob *Blob + AdminDebugEnabled bool + NumberOfRows int +} + +func (o *Portfolio) Clone() *Portfolio { + if o == nil { + return nil + } + return &Portfolio{ + ID: o.ID, + Name: o.Name, + Description: o.Description, + CreatedAt: o.CreatedAt, + HoldingsDate: o.HoldingsDate, + Owner: o.Owner.Clone(), + Blob: o.Blob.Clone(), + AdminDebugEnabled: o.AdminDebugEnabled, + NumberOfRows: o.NumberOfRows, + } +} + +type PortfolioGroupID string +type PortfolioGroup struct { + ID PortfolioGroupID + Owner *Owner + Name string + Description string + CreatedAt time.Time +} + +func (o *PortfolioGroup) Clone() *PortfolioGroup { + if o == nil { + return nil + } + return &PortfolioGroup{ + ID: o.ID, + Owner: o.Owner.Clone(), + Name: o.Name, + Description: o.Description, + CreatedAt: o.CreatedAt, + } +} + +type PortfolioGroupMembership struct { + PortfolioGroupID PortfolioGroupID + PortfolioID PortfolioID + CreatedAt time.Time +} + +func (o *PortfolioGroupMembership) Clone() *PortfolioGroupMembership { + if o == nil { + return nil + } + return &PortfolioGroupMembership{ + PortfolioGroupID: o.PortfolioGroupID, + PortfolioID: o.PortfolioID, + CreatedAt: o.CreatedAt, + } +} + +type PortfolioInitiativeMembership struct { + Portfolio *Portfolio + Initiative *Initiative + CreatedAt time.Time + AddedBy *User +} + +func (o *PortfolioInitiativeMembership) Clone() *PortfolioInitiativeMembership { + if o == nil { + return nil + } + return &PortfolioInitiativeMembership{ + Portfolio: o.Portfolio.Clone(), + Initiative: o.Initiative.Clone(), + CreatedAt: o.CreatedAt, + AddedBy: o.AddedBy.Clone(), + } +} + +type PortfolioSnapshotID string +type PortfolioSnapshot struct { + ID PortfolioSnapshotID + PortfolioID PortfolioID + PortfolioGroupID PortfolioGroupID + InitiatiativeID InitiativeID + PortfolioIDs []PortfolioID +} + +func (o *PortfolioSnapshot) Clone() *PortfolioSnapshot { + if o == nil { + return nil + } + pids := make([]PortfolioID, len(o.PortfolioIDs)) + copy(pids, o.PortfolioIDs) + return &PortfolioSnapshot{ + ID: o.ID, + PortfolioID: o.PortfolioID, + PortfolioGroupID: o.PortfolioGroupID, + InitiatiativeID: o.InitiatiativeID, + PortfolioIDs: pids, + } +} + +type AnalysisType string + +const ( + AnalysisType_Audit AnalysisType = "AUDIT" + AnalysisType_Report AnalysisType = "REPORT" +) + +var AnalysisTypeValues = []AnalysisType{ + AnalysisType_Audit, + AnalysisType_Report, +} + +func ParseAnalysisType(s string) (AnalysisType, error) { + switch s { + case "AUDIT": + return AnalysisType_Audit, nil + case "REPORT": + return AnalysisType_Report, nil + } + return "", fmt.Errorf("unknown AnalysisType: %q", s) +} + +type AnalysisID string +type Analysis struct { + ID AnalysisID + AnalysisType AnalysisType + Owner *Owner + PACTAVersion *PACTAVersion + PortfolioSnapshot *PortfolioSnapshot + Name string + Description string + CreatedAt time.Time + RanAt time.Time + CompletedAt time.Time + FailureCode FailureCode + FailureMessage string +} + +func (o *Analysis) Clone() *Analysis { + if o == nil { + return nil + } + return &Analysis{ + ID: o.ID, + AnalysisType: o.AnalysisType, + Owner: o.Owner.Clone(), + PACTAVersion: o.PACTAVersion.Clone(), + PortfolioSnapshot: o.PortfolioSnapshot.Clone(), + Name: o.Name, + Description: o.Description, + CreatedAt: o.CreatedAt, + RanAt: o.RanAt, + CompletedAt: o.CompletedAt, + FailureCode: o.FailureCode, + FailureMessage: o.FailureMessage, + } +} + +type AnalysisArtifactID string +type AnalysisArtifact struct { + ID AnalysisArtifactID + AnalysisID AnalysisID + Blob *Blob + AdminDebugEnabled bool + SharedToPublic bool +} + +func (o *AnalysisArtifact) Clone() *AnalysisArtifact { + if o == nil { + return nil + } + return &AnalysisArtifact{ + ID: o.ID, + AnalysisID: o.AnalysisID, + Blob: o.Blob.Clone(), + AdminDebugEnabled: o.AdminDebugEnabled, + SharedToPublic: o.SharedToPublic, + } +} + +type AuditLogAction string + +const ( + AuditLogAction_Create AuditLogAction = "CREATE" + AuditLogAction_Update AuditLogAction = "UPDATE" + AuditLogAction_Delete AuditLogAction = "DELETE" + AuditLogAction_AddTo AuditLogAction = "ADD_TO" + AuditLogAction_RemoveFrom AuditLogAction = "REMOVE_FROM" + AuditLogAction_EnableAdminDebug AuditLogAction = "ENABLE_ADMIN_DEBUG" + AuditLogAction_DisableAdminDebug AuditLogAction = "DISABLE_ADMIN_DEBUG" + AuditLogAction_Download AuditLogAction = "DOWNLOAD" + AuditLogAction_EnableSharing AuditLogAction = "ENABLE_SHARING" + AuditLogAction_DisableSharing AuditLogAction = "DISABLE_SHARING" +) + +var AuditLogActionValues = []AuditLogAction{ + AuditLogAction_Create, + AuditLogAction_Update, + AuditLogAction_Delete, + AuditLogAction_AddTo, + AuditLogAction_RemoveFrom, + AuditLogAction_EnableAdminDebug, + AuditLogAction_DisableAdminDebug, + AuditLogAction_Download, + AuditLogAction_EnableSharing, + AuditLogAction_DisableSharing, +} + +func ParseAuditLogAction(s string) (AuditLogAction, error) { + switch s { + case "CREATE": + return AuditLogAction_Create, nil + case "UPDATE": + return AuditLogAction_Update, nil + case "DELETE": + return AuditLogAction_Delete, nil + case "ADD_TO": + return AuditLogAction_AddTo, nil + case "REMOVE_FROM": + return AuditLogAction_RemoveFrom, nil + case "ENABLE_ADMIN_DEBUG": + return AuditLogAction_EnableAdminDebug, nil + case "DISABLE_ADMIN_DEBUG": + return AuditLogAction_DisableAdminDebug, nil + case "DOWNLOAD": + return AuditLogAction_Download, nil + case "ENABLE_SHARING": + return AuditLogAction_EnableSharing, nil + case "DISABLE_SHARING": + return AuditLogAction_DisableSharing, nil + } + return "", fmt.Errorf("unknown AuditLogAction: %q", s) +} + +type AuditLogActorType string + +const ( + AuditLogActorType_User AuditLogActorType = "USER" + AuditLogActorType_InitiativeAdmin AuditLogActorType = "INITIATIVE_ADMIN" + AuditLogActorType_Admin AuditLogActorType = "ADMIN" + AuditLogActorType_SuperAdmin AuditLogActorType = "SUPER_ADMIN" + AuditLogActorType_System AuditLogActorType = "SYSTEM" +) + +var AuditLogActorTypeValues = []AuditLogActorType{ + AuditLogActorType_User, + AuditLogActorType_InitiativeAdmin, + AuditLogActorType_Admin, + AuditLogActorType_SuperAdmin, + AuditLogActorType_System, +} + +func ParseAuditLogActorType(s string) (AuditLogActorType, error) { + switch s { + case "USER": + return AuditLogActorType_User, nil + case "INITIATIVE_ADMIN": + return AuditLogActorType_InitiativeAdmin, nil + case "ADMIN": + return AuditLogActorType_Admin, nil + case "SUPER_ADMIN": + return AuditLogActorType_SuperAdmin, nil + case "SYSTEM": + return AuditLogActorType_System, nil + } + return "", fmt.Errorf("unknown AuditLogActorType: %q", s) +} + +type AuditLogTargetType string + +const ( + AuditLogTargetType_User AuditLogTargetType = "USER" + AuditLogTargetType_Portfolio AuditLogTargetType = "PORTFOLIO" + AuditLogTargetType_IncompleteUpload AuditLogTargetType = "INCOMPLETE_UPLOAD" + AuditLogTargetType_PortfolioGroup AuditLogTargetType = "PORTFOLIO_GROUP" + AuditLogTargetType_Initiative AuditLogTargetType = "INITIATIVE" + AuditLogTargetType_PACTAVersion AuditLogTargetType = "PACTA_VERSION" + AuditLogTargetType_Analysis AuditLogTargetType = "ANALYSIS" +) + +var AuditLogTargetTypeValues = []AuditLogTargetType{ + AuditLogTargetType_User, + AuditLogTargetType_Portfolio, + AuditLogTargetType_IncompleteUpload, + AuditLogTargetType_PortfolioGroup, + AuditLogTargetType_Initiative, + AuditLogTargetType_PACTAVersion, + AuditLogTargetType_Analysis, +} + +func ParseAuditLogTargetType(s string) (AuditLogTargetType, error) { + switch s { + case "USER": + return AuditLogTargetType_User, nil + case "PORTFOLIO": + return AuditLogTargetType_Portfolio, nil + case "INCOMPLETE_UPLOAD": + return AuditLogTargetType_IncompleteUpload, nil + case "PORTFOLIO_GROUP": + return AuditLogTargetType_PortfolioGroup, nil + case "INITIATIVE": + return AuditLogTargetType_Initiative, nil + case "PACTA_VERSION": + return AuditLogTargetType_PACTAVersion, nil + case "ANALYSIS": + return AuditLogTargetType_Analysis, nil + } + return "", fmt.Errorf("unknown AuditLogTargetType: %q", s) +} + +type AuditLogID string +type AuditLog struct { + ID AuditLogID + CreatedAt time.Time + ActorType AuditLogActorType + ActorID string + Action AuditLogAction + PrimaryTargetType AuditLogTargetType + PrimaryTargetID string + SecondaryTargetType AuditLogTargetType + SecondaryTargetID string +} + +func (o *AuditLog) Clone() *AuditLog { + if o == nil { + return nil + } + return &AuditLog{ + ID: o.ID, + CreatedAt: o.CreatedAt, + ActorType: o.ActorType, + ActorID: o.ActorID, + Action: o.Action, + PrimaryTargetType: o.PrimaryTargetType, + PrimaryTargetID: o.PrimaryTargetID, + SecondaryTargetType: o.SecondaryTargetType, + SecondaryTargetID: o.SecondaryTargetID, + } +} + +type cloneable[T any] interface { + comparable + Clone() T +} + +func cloneAll[T cloneable[T]](in []T) []T { + out := make([]T, len(in)) + for i, t := range in { + out[i] = t.Clone() + } + return out +}