From 1d15c77767742473a4a4519c673656ef61109556 Mon Sep 17 00:00:00 2001 From: michalkrzyz Date: Thu, 1 Aug 2024 13:36:13 +0200 Subject: [PATCH] feat(authN): change SapID to UniqueUserId (#85) (#93) * feat(authN): change SapID to UniqueUserId (#85) Change name of variable SapId to UniqueUserId Add user Type variable to distinguish technical user and human user * Automatic application of license header --------- Co-authored-by: License Bot Co-authored-by: David Rochow Co-authored-by: Michael Reimsbach --- internal/api/graphql/graph/generated.go | 177 ++++++++++++++---- internal/api/graphql/graph/model/models.go | 12 +- .../api/graphql/graph/model/models_gen.go | 11 +- .../queryCollection/activity/full.graphql | 8 +- .../evidence/directRelations.graphql | 5 +- .../graph/queryCollection/issue/full.graphql | 5 +- .../service/directRelations.graphql | 5 +- .../supportGroup/directRelations.graphql | 5 +- .../graph/queryCollection/user/create.graphql | 5 +- .../user/directRelations.graphql | 5 +- .../graph/queryCollection/user/update.graphql | 5 +- .../api/graphql/graph/schema/service.graphqls | 5 +- .../api/graphql/graph/schema/user.graphqls | 8 +- internal/app/user.go | 4 +- internal/app/user_test.go | 6 +- internal/database/mariadb/entity.go | 37 ++-- internal/database/mariadb/init/schema.sql | 9 +- internal/database/mariadb/test/fixture.go | 25 ++- internal/database/mariadb/user.go | 22 ++- internal/database/mariadb/user_test.go | 81 +++++++- internal/e2e/evidence_query_test.go | 3 +- internal/e2e/service_query_test.go | 3 +- internal/e2e/support_group_query_test.go | 3 +- internal/e2e/user_query_test.go | 14 +- internal/entity/test/user.go | 15 +- internal/entity/user.go | 50 +++-- 26 files changed, 397 insertions(+), 131 deletions(-) diff --git a/internal/api/graphql/graph/generated.go b/internal/api/graphql/graph/generated.go index c9c991b3..747122ac 100644 --- a/internal/api/graphql/graph/generated.go +++ b/internal/api/graphql/graph/generated.go @@ -496,9 +496,10 @@ type ComplexityRoot struct { User struct { ID func(childComplexity int) int Name func(childComplexity int) int - SapID func(childComplexity int) int Services func(childComplexity int, filter *model.ServiceFilter, first *int, after *string) int SupportGroups func(childComplexity int, filter *model.SupportGroupFilter, first *int, after *string) int + Type func(childComplexity int) int + UniqueUserID func(childComplexity int) int } UserConnection struct { @@ -3182,13 +3183,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.User.Name(childComplexity), true - case "User.sapID": - if e.complexity.User.SapID == nil { - break - } - - return e.complexity.User.SapID(childComplexity), true - case "User.services": if e.complexity.User.Services == nil { break @@ -3213,6 +3207,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.User.SupportGroups(childComplexity, args["filter"].(*model.SupportGroupFilter), args["first"].(*int), args["after"].(*string)), true + case "User.type": + if e.complexity.User.Type == nil { + break + } + + return e.complexity.User.Type(childComplexity), true + + case "User.uniqueUserId": + if e.complexity.User.UniqueUserID == nil { + break + } + + return e.complexity.User.UniqueUserID(childComplexity), true + case "UserConnection.edges": if e.complexity.UserConnection.Edges == nil { break @@ -9905,8 +9913,10 @@ func (ec *executionContext) fieldContext_Evidence_author(_ context.Context, fiel switch field.Name { case "id": return ec.fieldContext_User_id(ctx, field) - case "sapID": - return ec.fieldContext_User_sapID(ctx, field) + case "uniqueUserId": + return ec.fieldContext_User_uniqueUserId(ctx, field) + case "type": + return ec.fieldContext_User_type(ctx, field) case "name": return ec.fieldContext_User_name(ctx, field) case "supportGroups": @@ -11653,8 +11663,10 @@ func (ec *executionContext) fieldContext_IssueMatch_user(_ context.Context, fiel switch field.Name { case "id": return ec.fieldContext_User_id(ctx, field) - case "sapID": - return ec.fieldContext_User_sapID(ctx, field) + case "uniqueUserId": + return ec.fieldContext_User_uniqueUserId(ctx, field) + case "type": + return ec.fieldContext_User_type(ctx, field) case "name": return ec.fieldContext_User_name(ctx, field) case "supportGroups": @@ -14495,8 +14507,10 @@ func (ec *executionContext) fieldContext_Mutation_createUser(ctx context.Context switch field.Name { case "id": return ec.fieldContext_User_id(ctx, field) - case "sapID": - return ec.fieldContext_User_sapID(ctx, field) + case "uniqueUserId": + return ec.fieldContext_User_uniqueUserId(ctx, field) + case "type": + return ec.fieldContext_User_type(ctx, field) case "name": return ec.fieldContext_User_name(ctx, field) case "supportGroups": @@ -14562,8 +14576,10 @@ func (ec *executionContext) fieldContext_Mutation_updateUser(ctx context.Context switch field.Name { case "id": return ec.fieldContext_User_id(ctx, field) - case "sapID": - return ec.fieldContext_User_sapID(ctx, field) + case "uniqueUserId": + return ec.fieldContext_User_uniqueUserId(ctx, field) + case "type": + return ec.fieldContext_User_type(ctx, field) case "name": return ec.fieldContext_User_name(ctx, field) case "supportGroups": @@ -21096,8 +21112,8 @@ func (ec *executionContext) fieldContext_User_id(_ context.Context, field graphq return fc, nil } -func (ec *executionContext) _User_sapID(ctx context.Context, field graphql.CollectedField, obj *model.User) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_User_sapID(ctx, field) +func (ec *executionContext) _User_uniqueUserId(ctx context.Context, field graphql.CollectedField, obj *model.User) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_User_uniqueUserId(ctx, field) if err != nil { return graphql.Null } @@ -21110,7 +21126,7 @@ func (ec *executionContext) _User_sapID(ctx context.Context, field graphql.Colle }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.SapID, nil + return obj.UniqueUserID, nil }) if err != nil { ec.Error(ctx, err) @@ -21124,7 +21140,7 @@ func (ec *executionContext) _User_sapID(ctx context.Context, field graphql.Colle return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_User_sapID(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_User_uniqueUserId(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "User", Field: field, @@ -21137,6 +21153,50 @@ func (ec *executionContext) fieldContext_User_sapID(_ context.Context, field gra return fc, nil } +func (ec *executionContext) _User_type(ctx context.Context, field graphql.CollectedField, obj *model.User) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_User_type(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Type, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_User_type(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "User", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _User_name(ctx context.Context, field graphql.CollectedField, obj *model.User) (ret graphql.Marshaler) { fc, err := ec.fieldContext_User_name(ctx, field) if err != nil { @@ -21485,8 +21545,10 @@ func (ec *executionContext) fieldContext_UserEdge_node(_ context.Context, field switch field.Name { case "id": return ec.fieldContext_User_id(ctx, field) - case "sapID": - return ec.fieldContext_User_sapID(ctx, field) + case "uniqueUserId": + return ec.fieldContext_User_uniqueUserId(ctx, field) + case "type": + return ec.fieldContext_User_type(ctx, field) case "name": return ec.fieldContext_User_name(ctx, field) case "supportGroups": @@ -24147,7 +24209,7 @@ func (ec *executionContext) unmarshalInputServiceFilter(ctx context.Context, obj asMap[k] = v } - fieldsInOrder := [...]string{"serviceName", "userSapID", "userName", "supportGroupName"} + fieldsInOrder := [...]string{"serviceName", "uniqueUserID", "type", "userName", "supportGroupName"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -24161,13 +24223,20 @@ func (ec *executionContext) unmarshalInputServiceFilter(ctx context.Context, obj return it, err } it.ServiceName = data - case "userSapID": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("userSapID")) + case "uniqueUserID": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("uniqueUserID")) data, err := ec.unmarshalOString2ᚕᚖstring(ctx, v) if err != nil { return it, err } - it.UserSapID = data + it.UniqueUserID = data + case "type": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("type")) + data, err := ec.unmarshalOInt2ᚕᚖint(ctx, v) + if err != nil { + return it, err + } + it.Type = data case "userName": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("userName")) data, err := ec.unmarshalOString2ᚕᚖstring(ctx, v) @@ -24344,20 +24413,27 @@ func (ec *executionContext) unmarshalInputUserInput(ctx context.Context, obj int asMap[k] = v } - fieldsInOrder := [...]string{"sapID", "name"} + fieldsInOrder := [...]string{"uniqueUserId", "type", "name"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { continue } switch k { - case "sapID": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("sapID")) + case "uniqueUserId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("uniqueUserId")) data, err := ec.unmarshalOString2ᚖstring(ctx, v) if err != nil { return it, err } - it.SapID = data + it.UniqueUserID = data + case "type": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("type")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.Type = data case "name": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) data, err := ec.unmarshalOString2ᚖstring(ctx, v) @@ -28855,8 +28931,13 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } - case "sapID": - out.Values[i] = ec._User_sapID(ctx, field, obj) + case "uniqueUserId": + out.Values[i] = ec._User_uniqueUserId(ctx, field, obj) + case "type": + out.Values[i] = ec._User_type(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } case "name": out.Values[i] = ec._User_name(ctx, field, obj) case "supportGroups": @@ -30512,6 +30593,38 @@ func (ec *executionContext) marshalOFloat2ᚖfloat64(ctx context.Context, sel as return graphql.WrapContextMarshaler(ctx, res) } +func (ec *executionContext) unmarshalOInt2ᚕᚖint(ctx context.Context, v interface{}) ([]*int, error) { + if v == nil { + return nil, nil + } + var vSlice []interface{} + if v != nil { + vSlice = graphql.CoerceList(v) + } + var err error + res := make([]*int, len(vSlice)) + for i := range vSlice { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) + res[i], err = ec.unmarshalOInt2ᚖint(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) marshalOInt2ᚕᚖint(ctx context.Context, sel ast.SelectionSet, v []*int) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + for i := range v { + ret[i] = ec.marshalOInt2ᚖint(ctx, sel, v[i]) + } + + return ret +} + func (ec *executionContext) unmarshalOInt2ᚖint(ctx context.Context, v interface{}) (*int, error) { if v == nil { return nil, nil diff --git a/internal/api/graphql/graph/model/models.go b/internal/api/graphql/graph/model/models.go index 9853eece..88c9728f 100644 --- a/internal/api/graphql/graph/model/models.go +++ b/internal/api/graphql/graph/model/models.go @@ -316,16 +316,18 @@ func NewIssueVariantEntity(issueVariant *IssueVariantInput) entity.IssueVariant func NewUser(user *entity.User) User { return User{ - ID: fmt.Sprintf("%d", user.Id), - SapID: &user.SapID, - Name: &user.Name, + ID: fmt.Sprintf("%d", user.Id), + UniqueUserID: &user.UniqueUserID, + Name: &user.Name, + Type: int(user.Type), } } func NewUserEntity(user *UserInput) entity.User { return entity.User{ - Name: lo.FromPtr(user.Name), - SapID: lo.FromPtr(user.SapID), + Name: lo.FromPtr(user.Name), + UniqueUserID: lo.FromPtr(user.UniqueUserID), + Type: entity.GetUserTypeFromString(lo.FromPtr(user.Type)), } } diff --git a/internal/api/graphql/graph/model/models_gen.go b/internal/api/graphql/graph/model/models_gen.go index df91e326..575222c2 100644 --- a/internal/api/graphql/graph/model/models_gen.go +++ b/internal/api/graphql/graph/model/models_gen.go @@ -601,7 +601,8 @@ func (this ServiceEdge) GetCursor() *string { return this.Cursor } type ServiceFilter struct { ServiceName []*string `json:"serviceName,omitempty"` - UserSapID []*string `json:"userSapID,omitempty"` + UniqueUserID []*string `json:"uniqueUserID,omitempty"` + Type []*int `json:"type,omitempty"` UserName []*string `json:"userName,omitempty"` SupportGroupName []*string `json:"supportGroupName,omitempty"` } @@ -667,7 +668,8 @@ type SupportGroupInput struct { type User struct { ID string `json:"id"` - SapID *string `json:"sapID,omitempty"` + UniqueUserID *string `json:"uniqueUserId,omitempty"` + Type int `json:"type"` Name *string `json:"name,omitempty"` SupportGroups *SupportGroupConnection `json:"supportGroups,omitempty"` Services *ServiceConnection `json:"services,omitempty"` @@ -701,8 +703,9 @@ type UserFilter struct { } type UserInput struct { - SapID *string `json:"sapID,omitempty"` - Name *string `json:"name,omitempty"` + UniqueUserID *string `json:"uniqueUserId,omitempty"` + Type *string `json:"type,omitempty"` + Name *string `json:"name,omitempty"` } type ActivityStatusValues string diff --git a/internal/api/graphql/graph/queryCollection/activity/full.graphql b/internal/api/graphql/graph/queryCollection/activity/full.graphql index 06caa0d2..15bfff0d 100644 --- a/internal/api/graphql/graph/queryCollection/activity/full.graphql +++ b/internal/api/graphql/graph/queryCollection/activity/full.graphql @@ -23,7 +23,8 @@ query ($filter: ActivityFilter, $first: Int, $after: String) { edges { node { id - sapID + uniqueUserId + type name } cursor @@ -165,7 +166,8 @@ query ($filter: ActivityFilter, $first: Int, $after: String) { description author { id - sapID + uniqueUserId + type name } activity { @@ -260,4 +262,4 @@ query ($filter: ActivityFilter, $first: Int, $after: String) { nextPageAfter } } -} \ No newline at end of file +} diff --git a/internal/api/graphql/graph/queryCollection/evidence/directRelations.graphql b/internal/api/graphql/graph/queryCollection/evidence/directRelations.graphql index 07eab514..a429ef2d 100644 --- a/internal/api/graphql/graph/queryCollection/evidence/directRelations.graphql +++ b/internal/api/graphql/graph/queryCollection/evidence/directRelations.graphql @@ -16,7 +16,8 @@ query ($filter: EvidenceFilter, $first: Int, $after: String) { authorId author { id - sapID + uniqueUserId + type name } activityId @@ -57,4 +58,4 @@ query ($filter: EvidenceFilter, $first: Int, $after: String) { } } } -} \ No newline at end of file +} diff --git a/internal/api/graphql/graph/queryCollection/issue/full.graphql b/internal/api/graphql/graph/queryCollection/issue/full.graphql index a16a1ef9..0fbfcda0 100644 --- a/internal/api/graphql/graph/queryCollection/issue/full.graphql +++ b/internal/api/graphql/graph/queryCollection/issue/full.graphql @@ -107,7 +107,8 @@ query ($filter: IssueFilter, $first: Int, $after: String) { edges { node { id - sapID + uniqueUserId + type name } cursor @@ -157,4 +158,4 @@ query ($filter: IssueFilter, $first: Int, $after: String) { nextPageAfter } } -} \ No newline at end of file +} diff --git a/internal/api/graphql/graph/queryCollection/service/directRelations.graphql b/internal/api/graphql/graph/queryCollection/service/directRelations.graphql index 3fc36f07..16e131dc 100644 --- a/internal/api/graphql/graph/queryCollection/service/directRelations.graphql +++ b/internal/api/graphql/graph/queryCollection/service/directRelations.graphql @@ -18,7 +18,8 @@ query ($filter: ServiceFilter, $first: Int, $after: String) { edges { node { id - sapID + uniqueUserId + type name } cursor @@ -102,4 +103,4 @@ query ($filter: ServiceFilter, $first: Int, $after: String) { } } } -} \ No newline at end of file +} diff --git a/internal/api/graphql/graph/queryCollection/supportGroup/directRelations.graphql b/internal/api/graphql/graph/queryCollection/supportGroup/directRelations.graphql index db35ed88..b18731ad 100644 --- a/internal/api/graphql/graph/queryCollection/supportGroup/directRelations.graphql +++ b/internal/api/graphql/graph/queryCollection/supportGroup/directRelations.graphql @@ -18,7 +18,8 @@ query ($filter: SupportGroupFilter, $first: Int, $after: String) { edges { node { id - sapID + uniqueUserId + type name } cursor @@ -59,4 +60,4 @@ query ($filter: SupportGroupFilter, $first: Int, $after: String) { } } } -} \ No newline at end of file +} diff --git a/internal/api/graphql/graph/queryCollection/user/create.graphql b/internal/api/graphql/graph/queryCollection/user/create.graphql index 34a95408..193b8b29 100644 --- a/internal/api/graphql/graph/queryCollection/user/create.graphql +++ b/internal/api/graphql/graph/queryCollection/user/create.graphql @@ -6,7 +6,8 @@ mutation ($input: UserInput!) { input: $input ) { id - sapID + uniqueUserId + type name } -} \ No newline at end of file +} diff --git a/internal/api/graphql/graph/queryCollection/user/directRelations.graphql b/internal/api/graphql/graph/queryCollection/user/directRelations.graphql index 6a5ce142..745da0ba 100644 --- a/internal/api/graphql/graph/queryCollection/user/directRelations.graphql +++ b/internal/api/graphql/graph/queryCollection/user/directRelations.graphql @@ -12,7 +12,8 @@ query ($filter: UserFilter, $first: Int, $after: String) { edges { node { id - sapID + uniqueUserId + type name supportGroups { totalCount @@ -59,4 +60,4 @@ query ($filter: UserFilter, $first: Int, $after: String) { } } } -} \ No newline at end of file +} diff --git a/internal/api/graphql/graph/queryCollection/user/update.graphql b/internal/api/graphql/graph/queryCollection/user/update.graphql index e9d0a1ba..e8f84423 100644 --- a/internal/api/graphql/graph/queryCollection/user/update.graphql +++ b/internal/api/graphql/graph/queryCollection/user/update.graphql @@ -8,7 +8,8 @@ mutation ($id: ID!, $input: UserInput!) { ) { __typename id - sapID + uniqueUserId + type name } -} \ No newline at end of file +} diff --git a/internal/api/graphql/graph/schema/service.graphqls b/internal/api/graphql/graph/schema/service.graphqls index ad619ae0..41ce6991 100644 --- a/internal/api/graphql/graph/schema/service.graphqls +++ b/internal/api/graphql/graph/schema/service.graphqls @@ -29,7 +29,8 @@ type ServiceEdge implements Edge { input ServiceFilter { serviceName: [String] - userSapID: [String] + uniqueUserID: [String] + type: [Int] userName: [String] supportGroupName: [String] -} \ No newline at end of file +} diff --git a/internal/api/graphql/graph/schema/user.graphqls b/internal/api/graphql/graph/schema/user.graphqls index 6e698f84..e2b9e7bb 100644 --- a/internal/api/graphql/graph/schema/user.graphqls +++ b/internal/api/graphql/graph/schema/user.graphqls @@ -3,14 +3,16 @@ type User implements Node { id:ID! - sapID: String + uniqueUserId: String + type: Int! name: String supportGroups(filter: SupportGroupFilter, first: Int, after: String): SupportGroupConnection services(filter: ServiceFilter, first: Int, after: String): ServiceConnection } input UserInput { - sapID: String + uniqueUserId: String + type: String name: String } @@ -28,4 +30,4 @@ type UserEdge implements Edge { input UserFilter { userName: [String], supportGroupIds: [String], -} \ No newline at end of file +} diff --git a/internal/app/user.go b/internal/app/user.go index f13d71e5..a52cc695 100644 --- a/internal/app/user.go +++ b/internal/app/user.go @@ -73,7 +73,7 @@ func (h *HeurekaApp) ListUsers(filter *entity.UserFilter, options *entity.ListOp func (h *HeurekaApp) CreateUser(user *entity.User) (*entity.User, error) { f := &entity.UserFilter{ - SapID: []*string{&user.SapID}, + UniqueUserID: []*string{&user.UniqueUserID}, } l := logrus.WithFields(logrus.Fields{ @@ -89,7 +89,7 @@ func (h *HeurekaApp) CreateUser(user *entity.User) (*entity.User, error) { } if len(users.Elements) > 0 { - return nil, heurekaError(fmt.Sprintf("Duplicated entry %s for sapId.", user.SapID)) + return nil, heurekaError(fmt.Sprintf("Duplicated entry %s for UniqueUserID.", user.UniqueUserID)) } newUser, err := h.database.CreateUser(user) diff --git a/internal/app/user_test.go b/internal/app/user_test.go index 1a6952e8..5993d912 100644 --- a/internal/app/user_test.go +++ b/internal/app/user_test.go @@ -110,7 +110,7 @@ var _ = Describe("When creating User", Label("app", "CreateUser"), func() { }) It("creates user", func() { - filter.SapID = []*string{&user.SapID} + filter.UniqueUserID = []*string{&user.UniqueUserID} db.On("CreateUser", &user).Return(&user, nil) db.On("GetUsers", filter).Return([]entity.User{}, nil) heureka = app.NewHeurekaApp(db) @@ -119,7 +119,7 @@ var _ = Describe("When creating User", Label("app", "CreateUser"), func() { Expect(newUser.Id).NotTo(BeEquivalentTo(0)) By("setting fields", func() { Expect(newUser.Name).To(BeEquivalentTo(user.Name)) - Expect(newUser.SapID).To(BeEquivalentTo(user.SapID)) + Expect(newUser.UniqueUserID).To(BeEquivalentTo(user.UniqueUserID)) }) }) }) @@ -156,7 +156,7 @@ var _ = Describe("When updating User", Label("app", "UpdateUser"), func() { Expect(err).To(BeNil(), "no error should be thrown") By("setting fields", func() { Expect(updatedUser.Name).To(BeEquivalentTo(user.Name)) - Expect(updatedUser.SapID).To(BeEquivalentTo(user.SapID)) + Expect(updatedUser.UniqueUserID).To(BeEquivalentTo(user.UniqueUserID)) }) }) }) diff --git a/internal/database/mariadb/entity.go b/internal/database/mariadb/entity.go index 638df461..fcecacac 100644 --- a/internal/database/mariadb/entity.go +++ b/internal/database/mariadb/entity.go @@ -42,6 +42,14 @@ func GetTimeValue(v sql.NullTime) time.Time { } } +func GetUserTypeValue(v sql.NullInt64) entity.UserType { + if v.Valid { + return entity.UserType(v.Int64) + } else { + return entity.InvalidUserType + } +} + type DatabaseRow interface { IssueRow | GetIssuesByRow | @@ -550,29 +558,32 @@ func (cir *ComponentInstanceRow) FromComponentInstance(ci *entity.ComponentInsta } type UserRow struct { - Id sql.NullInt64 `db:"user_id" json:"id"` - Name sql.NullString `db:"user_name" json:"ccrn"` - SapID sql.NullString `db:"user_sapID" json:"sapId"` - CreatedAt sql.NullTime `db:"user_created_at" json:"created_at"` - DeletedAt sql.NullTime `db:"user_deleted_at" json:"deleted_at,omitempty"` - UpdatedAt sql.NullTime `db:"user_updated_at" json:"updated_at"` + Id sql.NullInt64 `db:"user_id" json:"id"` + Name sql.NullString `db:"user_name" json:"ccrn"` + UniqueUserID sql.NullString `db:"user_unique_user_id" json:"unique_user_id"` + Type sql.NullInt64 `db:"user_type" json:"type"` + CreatedAt sql.NullTime `db:"user_created_at" json:"created_at"` + DeletedAt sql.NullTime `db:"user_deleted_at" json:"deleted_at,omitempty"` + UpdatedAt sql.NullTime `db:"user_updated_at" json:"updated_at"` } func (ur *UserRow) AsUser() entity.User { return entity.User{ - Id: GetInt64Value(ur.Id), - Name: GetStringValue(ur.Name), - SapID: GetStringValue(ur.SapID), - CreatedAt: GetTimeValue(ur.CreatedAt), - DeletedAt: GetTimeValue(ur.DeletedAt), - UpdatedAt: GetTimeValue(ur.UpdatedAt), + Id: GetInt64Value(ur.Id), + Name: GetStringValue(ur.Name), + UniqueUserID: GetStringValue(ur.UniqueUserID), + Type: GetUserTypeValue(ur.Type), + CreatedAt: GetTimeValue(ur.CreatedAt), + DeletedAt: GetTimeValue(ur.DeletedAt), + UpdatedAt: GetTimeValue(ur.UpdatedAt), } } func (ur *UserRow) FromUser(u *entity.User) { ur.Id = sql.NullInt64{Int64: u.Id, Valid: true} ur.Name = sql.NullString{String: u.Name, Valid: true} - ur.SapID = sql.NullString{String: u.SapID, Valid: true} + ur.UniqueUserID = sql.NullString{String: u.UniqueUserID, Valid: true} + ur.Type = sql.NullInt64{Int64: int64(u.Type), Valid: true} ur.CreatedAt = sql.NullTime{Time: u.CreatedAt, Valid: true} ur.DeletedAt = sql.NullTime{Time: u.DeletedAt, Valid: true} ur.UpdatedAt = sql.NullTime{Time: u.UpdatedAt, Valid: true} diff --git a/internal/database/mariadb/init/schema.sql b/internal/database/mariadb/init/schema.sql index c2fcd71a..1ee75400 100644 --- a/internal/database/mariadb/init/schema.sql +++ b/internal/database/mariadb/init/schema.sql @@ -123,14 +123,15 @@ create table if not exists User user_id int unsigned auto_increment primary key, user_name varchar(256) not null, - user_sapID varchar(64) not null, + user_unique_user_id varchar(64) not null, + user_type int unsigned, user_created_at timestamp default current_timestamp() not null, user_deleted_at timestamp null, user_updated_at timestamp default current_timestamp() not null on update current_timestamp(), constraint id_UNIQUE unique (user_id), - constraint sapID_UNIQUE - unique (user_sapID) + constraint unique_user_id_UNIQUE + unique (user_unique_user_id) ); create table if not exists Evidence @@ -366,4 +367,4 @@ create table if not exists IssueRepositoryService constraint fk_service_issue_repository foreign key (issuerepositoryservice_service_id) references Service (service_id) on update cascade -); \ No newline at end of file +); diff --git a/internal/database/mariadb/test/fixture.go b/internal/database/mariadb/test/fixture.go index f86df6cd..bf4d2795 100644 --- a/internal/database/mariadb/test/fixture.go +++ b/internal/database/mariadb/test/fixture.go @@ -952,10 +952,12 @@ func (s *DatabaseSeeder) InsertFakeUser(user mariadb.UserRow) (int64, error) { query := ` INSERT INTO User ( user_name, - user_sapID + user_unique_user_id, + user_type ) VALUES ( :user_name, - :user_sapID + :user_unique_user_id, + :user_type )` return s.ExecPreparedNamed(query, user) } @@ -1209,11 +1211,24 @@ func NewFakeComponentInstance() mariadb.ComponentInstanceRow { } } +var nextUserType entity.UserType = entity.HumanUserType + +func getNextUserType() int64 { + userType := nextUserType + if userType == entity.HumanUserType { + nextUserType = entity.TechnicalUserType + } else { + nextUserType = entity.HumanUserType + } + return int64(userType) +} + func NewFakeUser() mariadb.UserRow { - sapId := fmt.Sprintf("I%d", gofakeit.IntRange(100000, 999999)) + uniqueUserId := fmt.Sprintf("I%d", gofakeit.IntRange(100000, 999999)) return mariadb.UserRow{ - Name: sql.NullString{String: gofakeit.Name(), Valid: true}, - SapID: sql.NullString{String: sapId, Valid: true}, + Name: sql.NullString{String: gofakeit.Name(), Valid: true}, + UniqueUserID: sql.NullString{String: uniqueUserId, Valid: true}, + Type: sql.NullInt64{Int64: getNextUserType(), Valid: true}, } } diff --git a/internal/database/mariadb/user.go b/internal/database/mariadb/user.go index 9b2991f4..e812999b 100644 --- a/internal/database/mariadb/user.go +++ b/internal/database/mariadb/user.go @@ -16,7 +16,8 @@ func (s *SqlDatabase) getUserFilterString(filter *entity.UserFilter) string { var fl []string fl = append(fl, buildFilterQuery(filter.Id, "U.user_id = ?", OP_OR)) fl = append(fl, buildFilterQuery(filter.Name, "U.user_name = ?", OP_OR)) - fl = append(fl, buildFilterQuery(filter.SapID, "U.user_sapID = ?", OP_OR)) + fl = append(fl, buildFilterQuery(filter.UniqueUserID, "U.user_unique_user_id = ?", OP_OR)) + fl = append(fl, buildFilterQuery(filter.Type, "U.user_type = ?", OP_OR)) fl = append(fl, buildFilterQuery(filter.SupportGroupId, "SGU.supportgroupuser_support_group_id = ?", OP_OR)) fl = append(fl, buildFilterQuery(filter.ServiceId, "O.owner_service_id = ?", OP_OR)) fl = append(fl, "U.user_deleted_at IS NULL") @@ -35,7 +36,8 @@ func (s *SqlDatabase) ensureUserFilter(f *entity.UserFilter) *entity.UserFilter }, Id: nil, Name: nil, - SapID: nil, + UniqueUserID: nil, + Type: nil, SupportGroupId: nil, ServiceId: nil, } @@ -54,8 +56,11 @@ func (s *SqlDatabase) getUserUpdateFields(user *entity.User) string { if user.Name != "" { fl = append(fl, "user_name = :user_name") } - if user.SapID != "" { - fl = append(fl, "user_sapID = :user_sapID") + if user.UniqueUserID != "" { + fl = append(fl, "user_unique_user_id = :user_unique_user_id") + } + if user.Type != entity.InvalidUserType { + fl = append(fl, "user_type = :user_type") } return strings.Join(fl, ", ") @@ -117,7 +122,8 @@ func (s *SqlDatabase) buildUserStatement(baseQuery string, filter *entity.UserFi var filterParameters []interface{} filterParameters = buildQueryParameters(filterParameters, filter.Id) filterParameters = buildQueryParameters(filterParameters, filter.Name) - filterParameters = buildQueryParameters(filterParameters, filter.SapID) + filterParameters = buildQueryParameters(filterParameters, filter.UniqueUserID) + filterParameters = buildQueryParameters(filterParameters, filter.Type) filterParameters = buildQueryParameters(filterParameters, filter.SupportGroupId) filterParameters = buildQueryParameters(filterParameters, filter.ServiceId) if withCursor { @@ -213,10 +219,12 @@ func (s *SqlDatabase) CreateUser(user *entity.User) (*entity.User, error) { query := ` INSERT INTO User ( user_name, - user_sapID + user_unique_user_id, + user_type ) VALUES ( :user_name, - :user_sapID + :user_unique_user_id, + :user_type ) ` diff --git a/internal/database/mariadb/user_test.go b/internal/database/mariadb/user_test.go index 226c349d..ce2d2ab4 100644 --- a/internal/database/mariadb/user_test.go +++ b/internal/database/mariadb/user_test.go @@ -147,7 +147,8 @@ var _ = Describe("User", Label("database", "User"), func() { for _, row := range seedCollection.UserRows { if r.Id == row.Id.Int64 { Expect(r.Name).Should(BeEquivalentTo(row.Name.String), "Name matches") - Expect(r.SapID).Should(BeEquivalentTo(row.SapID.String), "SAP ID matches") + Expect(r.UniqueUserID).Should(BeEquivalentTo(row.UniqueUserID.String), "Unique User ID matches") + Expect(r.Type).Should(BeEquivalentTo(row.Type.Int64), "Type matches") Expect(r.CreatedAt.Unix()).ShouldNot(BeEquivalentTo(row.CreatedAt.Time.Unix()), "CreatedAt got set") Expect(r.UpdatedAt.Unix()).ShouldNot(BeEquivalentTo(row.UpdatedAt.Time.Unix()), "UpdatedAt got set") } @@ -257,7 +258,7 @@ var _ = Describe("User", Label("database", "User"), func() { It("can filter by a single sap id", func() { row := seedCollection.UserRows[rand.Intn(len(seedCollection.UserRows))] - filter := &entity.UserFilter{SapID: []*string{&row.SapID.String}} + filter := &entity.UserFilter{UniqueUserID: []*string{&row.UniqueUserID.String}} entries, err := db.GetUsers(filter) @@ -269,10 +270,43 @@ var _ = Describe("User", Label("database", "User"), func() { }) By("returning expected number of results", func() { for _, entry := range entries { - Expect(entry.SapID).To(BeEquivalentTo(row.SapID.String)) + Expect(entry.UniqueUserID).To(BeEquivalentTo(row.UniqueUserID.String)) } }) }) + It("can filter by user type", func() { + humanUserTypeFilter := &entity.UserFilter{Type: []entity.UserType{entity.HumanUserType}} + humanUserEntries, cErr := db.GetUsers(humanUserTypeFilter) + By("throwing no error when filtering human user type", func() { + Expect(cErr).To(BeNil()) + }) + By("returning some results for human user type", func() { + Expect(humanUserEntries).NotTo(BeEmpty()) + }) + By("returning expected number of human users", func() { + for _, entry := range humanUserEntries { + Expect(entry.Type).To(BeEquivalentTo(entity.HumanUserType)) + } + }) + + technicalUserTypeFilter := &entity.UserFilter{Type: []entity.UserType{entity.TechnicalUserType}} + technicalUserEntries, tErr := db.GetUsers(technicalUserTypeFilter) + By("throwing no error when filtering technical user type", func() { + Expect(tErr).To(BeNil()) + }) + By("returning some results for technical user type", func() { + Expect(technicalUserEntries).NotTo(BeEmpty()) + }) + By("returning expected number of technical users", func() { + for _, entry := range technicalUserEntries { + Expect(entry.Type).To(BeEquivalentTo(entity.TechnicalUserType)) + } + }) + + By("number of human and technical user types should match number of all users", func() { + Expect(len(humanUserEntries) + len(technicalUserEntries)).To(BeEquivalentTo(len(seedCollection.UserRows))) + }) + }) }) }) }) @@ -367,7 +401,8 @@ var _ = Describe("User", Label("database", "User"), func() { By("setting fields", func() { Expect(u[0].Id).To(BeEquivalentTo(user.Id)) Expect(u[0].Name).To(BeEquivalentTo(user.Name)) - Expect(u[0].SapID).To(BeEquivalentTo(user.SapID)) + Expect(u[0].UniqueUserID).To(BeEquivalentTo(user.UniqueUserID)) + Expect(u[0].Type).To(BeEquivalentTo(user.Type)) }) }) It("does not insert user with existing sap id", func() { @@ -415,13 +450,42 @@ var _ = Describe("User", Label("database", "User"), func() { By("setting fields", func() { Expect(u[0].Id).To(BeEquivalentTo(user.Id)) Expect(u[0].Name).To(BeEquivalentTo(user.Name)) - Expect(u[0].SapID).To(BeEquivalentTo(user.SapID)) + Expect(u[0].UniqueUserID).To(BeEquivalentTo(user.UniqueUserID)) + Expect(u[0].Type).To(BeEquivalentTo(user.Type)) + }) + }) + It("can update unique user id correctly", func() { + user := seedCollection.UserRows[0].AsUser() + + user.UniqueUserID = "D13377331" + err := db.UpdateUser(&user) + + By("throwing no error", func() { + Expect(err).To(BeNil()) + }) + + userFilter := &entity.UserFilter{ + Id: []*int64{&user.Id}, + } + + u, err := db.GetUsers(userFilter) + By("throwing no error", func() { + Expect(err).To(BeNil()) + }) + By("returning user", func() { + Expect(len(u)).To(BeEquivalentTo(1)) + }) + By("setting fields", func() { + Expect(u[0].Id).To(BeEquivalentTo(user.Id)) + Expect(u[0].Name).To(BeEquivalentTo(user.Name)) + Expect(u[0].UniqueUserID).To(BeEquivalentTo(user.UniqueUserID)) + Expect(u[0].Type).To(BeEquivalentTo(user.Type)) }) }) - It("can update sapId correctly", func() { + It("can update user type correctly", func() { user := seedCollection.UserRows[0].AsUser() - user.SapID = "D13377331" + user.Type = entity.TechnicalUserType err := db.UpdateUser(&user) By("throwing no error", func() { @@ -442,7 +506,8 @@ var _ = Describe("User", Label("database", "User"), func() { By("setting fields", func() { Expect(u[0].Id).To(BeEquivalentTo(user.Id)) Expect(u[0].Name).To(BeEquivalentTo(user.Name)) - Expect(u[0].SapID).To(BeEquivalentTo(user.SapID)) + Expect(u[0].UniqueUserID).To(BeEquivalentTo(user.UniqueUserID)) + Expect(u[0].Type).To(BeEquivalentTo(user.Type)) }) }) }) diff --git a/internal/e2e/evidence_query_test.go b/internal/e2e/evidence_query_test.go index 2ebda05a..89df4228 100644 --- a/internal/e2e/evidence_query_test.go +++ b/internal/e2e/evidence_query_test.go @@ -174,7 +174,8 @@ var _ = Describe("Getting Evidences via API", Label("e2e", "Evidences"), func() author := evidence.Node.Author Expect(author.ID).ToNot(BeNil(), "author has a ID set") - Expect(author.SapID).ToNot(BeNil(), "author has a sapId set") + Expect(author.UniqueUserID).ToNot(BeNil(), "author has a uniqueUserId set") + Expect(author.Type).ToNot(BeNil(), "author has a type set") Expect(author.Name).ToNot(BeNil(), "author has a name set") activity := evidence.Node.Activity diff --git a/internal/e2e/service_query_test.go b/internal/e2e/service_query_test.go index e805837b..a9c60620 100644 --- a/internal/e2e/service_query_test.go +++ b/internal/e2e/service_query_test.go @@ -156,7 +156,8 @@ var _ = Describe("Getting Services via API", Label("e2e", "Services"), func() { for _, owner := range service.Node.Owners.Edges { Expect(owner.Node.ID).ToNot(BeNil(), "owner has a ID set") Expect(owner.Node.Name).ToNot(BeNil(), "owner has a name set") - Expect(owner.Node.SapID).ToNot(BeNil(), "owner has a sapId set") + Expect(owner.Node.UniqueUserID).ToNot(BeNil(), "owner has a uniqueUserId set") + Expect(owner.Node.Type).ToNot(BeNil(), "owner has a type set") _, ownerFound := lo.Find(seedCollection.OwnerRows, func(row mariadb.OwnerRow) bool { return fmt.Sprintf("%d", row.UserId.Int64) == owner.Node.ID && // correct owner diff --git a/internal/e2e/support_group_query_test.go b/internal/e2e/support_group_query_test.go index 997af015..12b5fc73 100644 --- a/internal/e2e/support_group_query_test.go +++ b/internal/e2e/support_group_query_test.go @@ -190,7 +190,8 @@ var _ = Describe("Getting SupportGroups via API", Label("e2e", "SupportGroups"), for _, user := range sg.Node.Users.Edges { Expect(user.Node.ID).ToNot(BeNil(), "user has a ID set") Expect(user.Node.Name).ToNot(BeNil(), "user has a name set") - Expect(user.Node.SapID).ToNot(BeNil(), "user has a sapId set") + Expect(user.Node.UniqueUserID).ToNot(BeNil(), "user has a uniqueUserId set") + Expect(user.Node.Type).ToNot(BeNil(), "user has a type set") _, userFound := lo.Find(seedCollection.SupportGroupUserRows, func(row mariadb.SupportGroupUserRow) bool { return fmt.Sprintf("%d", row.SupportGroupId.Int64) == sg.Node.ID && // correct support group diff --git a/internal/e2e/user_query_test.go b/internal/e2e/user_query_test.go index 996b0b0a..4f2316d7 100644 --- a/internal/e2e/user_query_test.go +++ b/internal/e2e/user_query_test.go @@ -157,7 +157,8 @@ var _ = Describe("Getting Users via API", Label("e2e", "Users"), func() { for _, user := range respData.Users.Edges { Expect(user.Node.ID).ToNot(BeNil(), "user has a ID set") Expect(user.Node.Name).ToNot(BeNil(), "user has a name set") - Expect(user.Node.SapID).ToNot(BeNil(), "user has a sapId set") + Expect(user.Node.UniqueUserID).ToNot(BeNil(), "user has a uniqueUserId set") + Expect(user.Node.Type).ToNot(BeNil(), "user has a type set") for _, service := range user.Node.Services.Edges { Expect(service.Node.ID).ToNot(BeNil(), "Service has a ID set") @@ -239,8 +240,9 @@ var _ = Describe("Creating User via API", Label("e2e", "Users"), func() { req := graphql.NewRequest(str) req.Var("input", map[string]string{ - "sapID": user.SapID, - "name": user.Name, + "uniqueUserId": user.UniqueUserID, + "type": entity.GetUserTypeString(user.Type), + "name": user.Name, }) req.Header.Set("Cache-Control", "no-cache") @@ -254,7 +256,8 @@ var _ = Describe("Creating User via API", Label("e2e", "Users"), func() { } Expect(*respData.User.Name).To(Equal(user.Name)) - Expect(*respData.User.SapID).To(Equal(user.SapID)) + Expect(*respData.User.UniqueUserID).To(Equal(user.UniqueUserID)) + Expect(entity.UserType(respData.User.Type)).To(Equal(user.Type)) }) }) }) @@ -321,7 +324,8 @@ var _ = Describe("Updating User via API", Label("e2e", "Users"), func() { } Expect(*respData.User.Name).To(Equal(user.Name)) - Expect(*respData.User.SapID).To(Equal(user.SapID)) + Expect(*respData.User.UniqueUserID).To(Equal(user.UniqueUserID)) + Expect(entity.UserType(respData.User.Type)).To(Equal(user.Type)) }) }) }) diff --git a/internal/entity/test/user.go b/internal/entity/test/user.go index b68e60d0..a2555250 100644 --- a/internal/entity/test/user.go +++ b/internal/entity/test/user.go @@ -11,14 +11,15 @@ import ( ) func NewFakeUserEntity() entity.User { - sapId := fmt.Sprintf("I%d", gofakeit.IntRange(100000, 999999)) + uniqueUserId := fmt.Sprintf("I%d", gofakeit.IntRange(100000, 999999)) return entity.User{ - Id: int64(gofakeit.Number(1, 10000000)), - Name: gofakeit.Name(), - SapID: sapId, - CreatedAt: gofakeit.Date(), - DeletedAt: gofakeit.Date(), - UpdatedAt: gofakeit.Date(), + Id: int64(gofakeit.Number(1, 10000000)), + Name: gofakeit.Name(), + UniqueUserID: uniqueUserId, + Type: entity.HumanUserType, + CreatedAt: gofakeit.Date(), + DeletedAt: gofakeit.Date(), + UpdatedAt: gofakeit.Date(), } } diff --git a/internal/entity/user.go b/internal/entity/user.go index c2c6dbfb..324a7782 100644 --- a/internal/entity/user.go +++ b/internal/entity/user.go @@ -5,22 +5,32 @@ package entity import "time" +type UserType int + +const ( + InvalidUserType UserType = 0 + HumanUserType UserType = 1 + TechnicalUserType UserType = 2 +) + type User struct { - Id int64 `json:"id"` - Name string `json:"name"` - SapID string `json:"sapId"` - CreatedAt time.Time `json:"created_at"` - DeletedAt time.Time `json:"deleted_at,omitempty"` - UpdatedAt time.Time `json:"updated_at"` + Id int64 `json:"id"` + Name string `json:"name"` + UniqueUserID string `json:"uniqueUserId"` + Type UserType `json:"type"` + CreatedAt time.Time `json:"created_at"` + DeletedAt time.Time `json:"deleted_at,omitempty"` + UpdatedAt time.Time `json:"updated_at"` } type UserFilter struct { Paginated - Name []*string `json:"name"` - SapID []*string `json:"sapId"` - Id []*int64 `json:"id"` - SupportGroupId []*int64 `json:"support_group_id"` - ServiceId []*int64 `json:"service_id"` + Name []*string `json:"name"` + UniqueUserID []*string `json:"uniqueUserId"` + Type []UserType `json:"type"` + Id []*int64 `json:"id"` + SupportGroupId []*int64 `json:"support_group_id"` + ServiceId []*int64 `json:"service_id"` } type UserAggregations struct { @@ -31,3 +41,21 @@ type UserResult struct { *UserAggregations *User } + +func GetUserTypeFromString(uts string) UserType { + if uts == "user" { + return HumanUserType + } else if uts == "technical" { + return TechnicalUserType + } + return InvalidUserType +} + +func GetUserTypeString(ut UserType) string { + if ut == HumanUserType { + return "user" + } else if ut == TechnicalUserType { + return "technical" + } + return "" +}