From 28131583705894486c94943be0a9c069a4a5c4ed Mon Sep 17 00:00:00 2001 From: Victor Dorneanu Date: Tue, 6 Aug 2024 13:47:51 +0000 Subject: [PATCH 1/3] feat(scans): First draft --- internal/api/graphql/gqlgen.yml | 6 +- internal/api/graphql/graph/generated.go | 1195 ++++++++++++++--- .../api/graphql/graph/model/models_gen.go | 82 ++ internal/api/graphql/graph/resolver/scan.go | 31 + .../api/graphql/graph/schema/scan.graphqls | 37 + internal/app/interface.go | 5 + internal/app/scan.go | 28 + internal/entity/scan.go | 33 + 8 files changed, 1255 insertions(+), 162 deletions(-) create mode 100644 internal/api/graphql/graph/resolver/scan.go create mode 100644 internal/api/graphql/graph/schema/scan.graphqls create mode 100644 internal/app/scan.go create mode 100644 internal/entity/scan.go diff --git a/internal/api/graphql/gqlgen.yml b/internal/api/graphql/gqlgen.yml index 706b32d0..6f41ea4e 100644 --- a/internal/api/graphql/gqlgen.yml +++ b/internal/api/graphql/gqlgen.yml @@ -190,4 +190,8 @@ models: supportGroups: resolver: true services: - resolver: true \ No newline at end of file + resolver: true + Scan: + fields: + users: + resolver: true diff --git a/internal/api/graphql/graph/generated.go b/internal/api/graphql/graph/generated.go index 0e0dfc25..eafd0de9 100644 --- a/internal/api/graphql/graph/generated.go +++ b/internal/api/graphql/graph/generated.go @@ -1,6 +1,3 @@ -// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors -// SPDX-License-Identifier: Apache-2.0 - // Code generated by github.com/99designs/gqlgen, DO NOT EDIT. package graph @@ -54,6 +51,7 @@ type ResolverRoot interface { IssueVariant() IssueVariantResolver Mutation() MutationResolver Query() QueryResolver + Scan() ScanResolver Service() ServiceResolver SupportGroup() SupportGroupResolver User() UserResolver @@ -438,6 +436,24 @@ type ComplexityRoot struct { Users func(childComplexity int, filter *model.UserFilter, first *int, after *string) int } + Scan struct { + ID func(childComplexity int) int + Scope func(childComplexity int) int + Type func(childComplexity int) int + Users func(childComplexity int, filter *model.UserFilter, first *int, after *string) int + } + + ScanConnection struct { + Edges func(childComplexity int) int + PageInfo func(childComplexity int) int + TotalCount func(childComplexity int) int + } + + ScanEdge struct { + Cursor func(childComplexity int) int + Node func(childComplexity int) int + } + Service struct { Activities func(childComplexity int, filter *model.ActivityFilter, first *int, after *string) int ComponentInstances func(childComplexity int, filter *model.ComponentInstanceFilter, first *int, after *string) int @@ -633,6 +649,9 @@ type QueryResolver interface { SupportGroups(ctx context.Context, filter *model.SupportGroupFilter, first *int, after *string) (*model.SupportGroupConnection, error) Users(ctx context.Context, filter *model.UserFilter, first *int, after *string) (*model.UserConnection, error) } +type ScanResolver interface { + Users(ctx context.Context, obj *model.Scan, filter *model.UserFilter, first *int, after *string) (*model.UserConnection, error) +} type ServiceResolver interface { Owners(ctx context.Context, obj *model.Service, filter *model.UserFilter, first *int, after *string) (*model.UserConnection, error) SupportGroups(ctx context.Context, obj *model.Service, filter *model.SupportGroupFilter, first *int, after *string) (*model.SupportGroupConnection, error) @@ -2883,6 +2902,74 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.Users(childComplexity, args["filter"].(*model.UserFilter), args["first"].(*int), args["after"].(*string)), true + case "Scan.id": + if e.complexity.Scan.ID == nil { + break + } + + return e.complexity.Scan.ID(childComplexity), true + + case "Scan.scope": + if e.complexity.Scan.Scope == nil { + break + } + + return e.complexity.Scan.Scope(childComplexity), true + + case "Scan.type": + if e.complexity.Scan.Type == nil { + break + } + + return e.complexity.Scan.Type(childComplexity), true + + case "Scan.users": + if e.complexity.Scan.Users == nil { + break + } + + args, err := ec.field_Scan_users_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Scan.Users(childComplexity, args["filter"].(*model.UserFilter), args["first"].(*int), args["after"].(*string)), true + + case "ScanConnection.edges": + if e.complexity.ScanConnection.Edges == nil { + break + } + + return e.complexity.ScanConnection.Edges(childComplexity), true + + case "ScanConnection.pageInfo": + if e.complexity.ScanConnection.PageInfo == nil { + break + } + + return e.complexity.ScanConnection.PageInfo(childComplexity), true + + case "ScanConnection.totalCount": + if e.complexity.ScanConnection.TotalCount == nil { + break + } + + return e.complexity.ScanConnection.TotalCount(childComplexity), true + + case "ScanEdge.cursor": + if e.complexity.ScanEdge.Cursor == nil { + break + } + + return e.complexity.ScanEdge.Cursor(childComplexity), true + + case "ScanEdge.node": + if e.complexity.ScanEdge.Node == nil { + break + } + + return e.complexity.ScanEdge.Node(childComplexity), true + case "Service.activities": if e.complexity.Service.Activities == nil { break @@ -3209,6 +3296,8 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputIssueRepositoryInput, ec.unmarshalInputIssueVariantFilter, ec.unmarshalInputIssueVariantInput, + ec.unmarshalInputScanFilter, + ec.unmarshalInputScanInput, ec.unmarshalInputServiceFilter, ec.unmarshalInputServiceInput, ec.unmarshalInputSeverityInput, @@ -3312,7 +3401,7 @@ func (ec *executionContext) introspectType(name string) (*introspection.Type, er return introspection.WrapTypeFromDef(ec.Schema(), ec.Schema().Types[name]), nil } -//go:embed "schema/activity.graphqls" "schema/common.graphqls" "schema/component.graphqls" "schema/component_instance.graphqls" "schema/component_version.graphqls" "schema/evidence.graphqls" "schema/issue.graphqls" "schema/issue_match.graphqls" "schema/issue_match_change.graphqls" "schema/issue_repository.graphqls" "schema/issue_variant.graphqls" "schema/mutation.graphqls" "schema/query.graphqls" "schema/service.graphqls" "schema/support_group.graphqls" "schema/user.graphqls" +//go:embed "schema/activity.graphqls" "schema/common.graphqls" "schema/component.graphqls" "schema/component_instance.graphqls" "schema/component_version.graphqls" "schema/evidence.graphqls" "schema/issue.graphqls" "schema/issue_match.graphqls" "schema/issue_match_change.graphqls" "schema/issue_repository.graphqls" "schema/issue_variant.graphqls" "schema/mutation.graphqls" "schema/query.graphqls" "schema/scan.graphqls" "schema/service.graphqls" "schema/support_group.graphqls" "schema/user.graphqls" var sourcesFS embed.FS func sourceData(filename string) string { @@ -3337,6 +3426,7 @@ var sources = []*ast.Source{ {Name: "schema/issue_variant.graphqls", Input: sourceData("schema/issue_variant.graphqls"), BuiltIn: false}, {Name: "schema/mutation.graphqls", Input: sourceData("schema/mutation.graphqls"), BuiltIn: false}, {Name: "schema/query.graphqls", Input: sourceData("schema/query.graphqls"), BuiltIn: false}, + {Name: "schema/scan.graphqls", Input: sourceData("schema/scan.graphqls"), BuiltIn: false}, {Name: "schema/service.graphqls", Input: sourceData("schema/service.graphqls"), BuiltIn: false}, {Name: "schema/support_group.graphqls", Input: sourceData("schema/support_group.graphqls"), BuiltIn: false}, {Name: "schema/user.graphqls", Input: sourceData("schema/user.graphqls"), BuiltIn: false}, @@ -5462,6 +5552,39 @@ func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs return args, nil } +func (ec *executionContext) field_Scan_users_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 *model.UserFilter + if tmp, ok := rawArgs["filter"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) + arg0, err = ec.unmarshalOUserFilter2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐUserFilter(ctx, tmp) + if err != nil { + return nil, err + } + } + args["filter"] = arg0 + var arg1 *int + if tmp, ok := rawArgs["first"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("first")) + arg1, err = ec.unmarshalOInt2ᚖint(ctx, tmp) + if err != nil { + return nil, err + } + } + args["first"] = arg1 + var arg2 *string + if tmp, ok := rawArgs["after"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("after")) + arg2, err = ec.unmarshalOString2ᚖstring(ctx, tmp) + if err != nil { + return nil, err + } + } + args["after"] = arg2 + return args, nil +} + func (ec *executionContext) field_Service_activities_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -19302,8 +19425,8 @@ func (ec *executionContext) fieldContext_Query___schema(_ context.Context, field return fc, nil } -func (ec *executionContext) _Service_id(ctx context.Context, field graphql.CollectedField, obj *model.Service) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Service_id(ctx, field) +func (ec *executionContext) _Scan_id(ctx context.Context, field graphql.CollectedField, obj *model.Scan) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Scan_id(ctx, field) if err != nil { return graphql.Null } @@ -19333,9 +19456,9 @@ func (ec *executionContext) _Service_id(ctx context.Context, field graphql.Colle return ec.marshalNID2string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Service_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Scan_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Service", + Object: "Scan", Field: field, IsMethod: false, IsResolver: false, @@ -19346,8 +19469,8 @@ func (ec *executionContext) fieldContext_Service_id(_ context.Context, field gra return fc, nil } -func (ec *executionContext) _Service_name(ctx context.Context, field graphql.CollectedField, obj *model.Service) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Service_name(ctx, field) +func (ec *executionContext) _Scan_type(ctx context.Context, field graphql.CollectedField, obj *model.Scan) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Scan_type(ctx, field) if err != nil { return graphql.Null } @@ -19360,7 +19483,7 @@ func (ec *executionContext) _Service_name(ctx context.Context, field graphql.Col }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Name, nil + return obj.Type, nil }) if err != nil { ec.Error(ctx, err) @@ -19374,9 +19497,9 @@ func (ec *executionContext) _Service_name(ctx context.Context, field graphql.Col return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Service_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Scan_type(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Service", + Object: "Scan", Field: field, IsMethod: false, IsResolver: false, @@ -19387,8 +19510,8 @@ func (ec *executionContext) fieldContext_Service_name(_ context.Context, field g return fc, nil } -func (ec *executionContext) _Service_owners(ctx context.Context, field graphql.CollectedField, obj *model.Service) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Service_owners(ctx, field) +func (ec *executionContext) _Scan_scope(ctx context.Context, field graphql.CollectedField, obj *model.Scan) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Scan_scope(ctx, field) if err != nil { return graphql.Null } @@ -19401,7 +19524,7 @@ func (ec *executionContext) _Service_owners(ctx context.Context, field graphql.C }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Service().Owners(rctx, obj, fc.Args["filter"].(*model.UserFilter), fc.Args["first"].(*int), fc.Args["after"].(*string)) + return obj.Scope, nil }) if err != nil { ec.Error(ctx, err) @@ -19410,45 +19533,26 @@ func (ec *executionContext) _Service_owners(ctx context.Context, field graphql.C if resTmp == nil { return graphql.Null } - res := resTmp.(*model.UserConnection) + res := resTmp.(*string) fc.Result = res - return ec.marshalOUserConnection2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐUserConnection(ctx, field.Selections, res) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Service_owners(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Scan_scope(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Service", + Object: "Scan", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "totalCount": - return ec.fieldContext_UserConnection_totalCount(ctx, field) - case "edges": - return ec.fieldContext_UserConnection_edges(ctx, field) - case "pageInfo": - return ec.fieldContext_UserConnection_pageInfo(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type UserConnection", field.Name) + return nil, errors.New("field of type String does not have child fields") }, } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Service_owners_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } return fc, nil } -func (ec *executionContext) _Service_supportGroups(ctx context.Context, field graphql.CollectedField, obj *model.Service) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Service_supportGroups(ctx, field) +func (ec *executionContext) _Scan_users(ctx context.Context, field graphql.CollectedField, obj *model.Scan) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Scan_users(ctx, field) if err != nil { return graphql.Null } @@ -19461,7 +19565,7 @@ func (ec *executionContext) _Service_supportGroups(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Service().SupportGroups(rctx, obj, fc.Args["filter"].(*model.SupportGroupFilter), fc.Args["first"].(*int), fc.Args["after"].(*string)) + return ec.resolvers.Scan().Users(rctx, obj, fc.Args["filter"].(*model.UserFilter), fc.Args["first"].(*int), fc.Args["after"].(*string)) }) if err != nil { ec.Error(ctx, err) @@ -19470,27 +19574,27 @@ func (ec *executionContext) _Service_supportGroups(ctx context.Context, field gr if resTmp == nil { return graphql.Null } - res := resTmp.(*model.SupportGroupConnection) + res := resTmp.(*model.UserConnection) fc.Result = res - return ec.marshalOSupportGroupConnection2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐSupportGroupConnection(ctx, field.Selections, res) + return ec.marshalOUserConnection2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐUserConnection(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Service_supportGroups(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Scan_users(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Service", + Object: "Scan", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "totalCount": - return ec.fieldContext_SupportGroupConnection_totalCount(ctx, field) + return ec.fieldContext_UserConnection_totalCount(ctx, field) case "edges": - return ec.fieldContext_SupportGroupConnection_edges(ctx, field) + return ec.fieldContext_UserConnection_edges(ctx, field) case "pageInfo": - return ec.fieldContext_SupportGroupConnection_pageInfo(ctx, field) + return ec.fieldContext_UserConnection_pageInfo(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type SupportGroupConnection", field.Name) + return nil, fmt.Errorf("no field named %q was found under type UserConnection", field.Name) }, } defer func() { @@ -19500,15 +19604,15 @@ func (ec *executionContext) fieldContext_Service_supportGroups(ctx context.Conte } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Service_supportGroups_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Scan_users_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } return fc, nil } -func (ec *executionContext) _Service_activities(ctx context.Context, field graphql.CollectedField, obj *model.Service) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Service_activities(ctx, field) +func (ec *executionContext) _ScanConnection_totalCount(ctx context.Context, field graphql.CollectedField, obj *model.ScanConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ScanConnection_totalCount(ctx, field) if err != nil { return graphql.Null } @@ -19521,54 +19625,38 @@ func (ec *executionContext) _Service_activities(ctx context.Context, field graph }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Service().Activities(rctx, obj, fc.Args["filter"].(*model.ActivityFilter), fc.Args["first"].(*int), fc.Args["after"].(*string)) + return obj.TotalCount, 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.(*model.ActivityConnection) + res := resTmp.(int) fc.Result = res - return ec.marshalOActivityConnection2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐActivityConnection(ctx, field.Selections, res) + return ec.marshalNInt2int(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Service_activities(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ScanConnection_totalCount(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Service", + Object: "ScanConnection", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "totalCount": - return ec.fieldContext_ActivityConnection_totalCount(ctx, field) - case "edges": - return ec.fieldContext_ActivityConnection_edges(ctx, field) - case "pageInfo": - return ec.fieldContext_ActivityConnection_pageInfo(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type ActivityConnection", field.Name) + return nil, errors.New("field of type Int does not have child fields") }, } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Service_activities_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } return fc, nil } -func (ec *executionContext) _Service_issueRepositories(ctx context.Context, field graphql.CollectedField, obj *model.Service) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Service_issueRepositories(ctx, field) +func (ec *executionContext) _ScanConnection_edges(ctx context.Context, field graphql.CollectedField, obj *model.ScanConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ScanConnection_edges(ctx, field) if err != nil { return graphql.Null } @@ -19581,7 +19669,7 @@ func (ec *executionContext) _Service_issueRepositories(ctx context.Context, fiel }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Service().IssueRepositories(rctx, obj, fc.Args["filter"].(*model.IssueRepositoryFilter), fc.Args["first"].(*int), fc.Args["after"].(*string)) + return obj.Edges, nil }) if err != nil { ec.Error(ctx, err) @@ -19590,45 +19678,32 @@ func (ec *executionContext) _Service_issueRepositories(ctx context.Context, fiel if resTmp == nil { return graphql.Null } - res := resTmp.(*model.IssueRepositoryConnection) + res := resTmp.([]*model.ScanEdge) fc.Result = res - return ec.marshalOIssueRepositoryConnection2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐIssueRepositoryConnection(ctx, field.Selections, res) + return ec.marshalOScanEdge2ᚕᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐScanEdge(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Service_issueRepositories(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ScanConnection_edges(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Service", + Object: "ScanConnection", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "totalCount": - return ec.fieldContext_IssueRepositoryConnection_totalCount(ctx, field) - case "edges": - return ec.fieldContext_IssueRepositoryConnection_edges(ctx, field) - case "pageInfo": - return ec.fieldContext_IssueRepositoryConnection_pageInfo(ctx, field) + case "node": + return ec.fieldContext_ScanEdge_node(ctx, field) + case "cursor": + return ec.fieldContext_ScanEdge_cursor(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type IssueRepositoryConnection", field.Name) + return nil, fmt.Errorf("no field named %q was found under type ScanEdge", field.Name) }, } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Service_issueRepositories_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } return fc, nil } -func (ec *executionContext) _Service_componentInstances(ctx context.Context, field graphql.CollectedField, obj *model.Service) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Service_componentInstances(ctx, field) +func (ec *executionContext) _ScanConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *model.ScanConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ScanConnection_pageInfo(ctx, field) if err != nil { return graphql.Null } @@ -19641,7 +19716,7 @@ func (ec *executionContext) _Service_componentInstances(ctx context.Context, fie }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Service().ComponentInstances(rctx, obj, fc.Args["filter"].(*model.ComponentInstanceFilter), fc.Args["first"].(*int), fc.Args["after"].(*string)) + return obj.PageInfo, nil }) if err != nil { ec.Error(ctx, err) @@ -19650,33 +19725,508 @@ func (ec *executionContext) _Service_componentInstances(ctx context.Context, fie if resTmp == nil { return graphql.Null } - res := resTmp.(*model.ComponentInstanceConnection) + res := resTmp.(*model.PageInfo) fc.Result = res - return ec.marshalOComponentInstanceConnection2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐComponentInstanceConnection(ctx, field.Selections, res) + return ec.marshalOPageInfo2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐPageInfo(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Service_componentInstances(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_ScanConnection_pageInfo(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "Service", + Object: "ScanConnection", Field: field, - IsMethod: true, - IsResolver: true, + IsMethod: false, + IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "totalCount": - return ec.fieldContext_ComponentInstanceConnection_totalCount(ctx, field) - case "edges": - return ec.fieldContext_ComponentInstanceConnection_edges(ctx, field) - case "pageInfo": - return ec.fieldContext_ComponentInstanceConnection_pageInfo(ctx, field) + case "hasNextPage": + return ec.fieldContext_PageInfo_hasNextPage(ctx, field) + case "hasPreviousPage": + return ec.fieldContext_PageInfo_hasPreviousPage(ctx, field) + case "isValidPage": + return ec.fieldContext_PageInfo_isValidPage(ctx, field) + case "pageNumber": + return ec.fieldContext_PageInfo_pageNumber(ctx, field) + case "nextPageAfter": + return ec.fieldContext_PageInfo_nextPageAfter(ctx, field) + case "pages": + return ec.fieldContext_PageInfo_pages(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type ComponentInstanceConnection", field.Name) + return nil, fmt.Errorf("no field named %q was found under type PageInfo", field.Name) }, } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) + return fc, nil +} + +func (ec *executionContext) _ScanEdge_node(ctx context.Context, field graphql.CollectedField, obj *model.ScanEdge) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ScanEdge_node(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.Node, 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.(*model.Scan) + fc.Result = res + return ec.marshalNScan2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐScan(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ScanEdge_node(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ScanEdge", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Scan_id(ctx, field) + case "type": + return ec.fieldContext_Scan_type(ctx, field) + case "scope": + return ec.fieldContext_Scan_scope(ctx, field) + case "users": + return ec.fieldContext_Scan_users(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Scan", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _ScanEdge_cursor(ctx context.Context, field graphql.CollectedField, obj *model.ScanEdge) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ScanEdge_cursor(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.Cursor, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ScanEdge_cursor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ScanEdge", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Service_id(ctx context.Context, field graphql.CollectedField, obj *model.Service) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Service_id(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.ID, 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.(string) + fc.Result = res + return ec.marshalNID2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Service_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Service", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Service_name(ctx context.Context, field graphql.CollectedField, obj *model.Service) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Service_name(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.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Service_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Service", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Service_owners(ctx context.Context, field graphql.CollectedField, obj *model.Service) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Service_owners(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 ec.resolvers.Service().Owners(rctx, obj, fc.Args["filter"].(*model.UserFilter), fc.Args["first"].(*int), fc.Args["after"].(*string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*model.UserConnection) + fc.Result = res + return ec.marshalOUserConnection2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐUserConnection(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Service_owners(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Service", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "totalCount": + return ec.fieldContext_UserConnection_totalCount(ctx, field) + case "edges": + return ec.fieldContext_UserConnection_edges(ctx, field) + case "pageInfo": + return ec.fieldContext_UserConnection_pageInfo(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type UserConnection", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Service_owners_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Service_supportGroups(ctx context.Context, field graphql.CollectedField, obj *model.Service) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Service_supportGroups(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 ec.resolvers.Service().SupportGroups(rctx, obj, fc.Args["filter"].(*model.SupportGroupFilter), fc.Args["first"].(*int), fc.Args["after"].(*string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*model.SupportGroupConnection) + fc.Result = res + return ec.marshalOSupportGroupConnection2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐSupportGroupConnection(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Service_supportGroups(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Service", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "totalCount": + return ec.fieldContext_SupportGroupConnection_totalCount(ctx, field) + case "edges": + return ec.fieldContext_SupportGroupConnection_edges(ctx, field) + case "pageInfo": + return ec.fieldContext_SupportGroupConnection_pageInfo(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type SupportGroupConnection", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Service_supportGroups_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Service_activities(ctx context.Context, field graphql.CollectedField, obj *model.Service) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Service_activities(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 ec.resolvers.Service().Activities(rctx, obj, fc.Args["filter"].(*model.ActivityFilter), fc.Args["first"].(*int), fc.Args["after"].(*string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*model.ActivityConnection) + fc.Result = res + return ec.marshalOActivityConnection2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐActivityConnection(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Service_activities(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Service", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "totalCount": + return ec.fieldContext_ActivityConnection_totalCount(ctx, field) + case "edges": + return ec.fieldContext_ActivityConnection_edges(ctx, field) + case "pageInfo": + return ec.fieldContext_ActivityConnection_pageInfo(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ActivityConnection", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Service_activities_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Service_issueRepositories(ctx context.Context, field graphql.CollectedField, obj *model.Service) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Service_issueRepositories(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 ec.resolvers.Service().IssueRepositories(rctx, obj, fc.Args["filter"].(*model.IssueRepositoryFilter), fc.Args["first"].(*int), fc.Args["after"].(*string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*model.IssueRepositoryConnection) + fc.Result = res + return ec.marshalOIssueRepositoryConnection2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐIssueRepositoryConnection(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Service_issueRepositories(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Service", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "totalCount": + return ec.fieldContext_IssueRepositoryConnection_totalCount(ctx, field) + case "edges": + return ec.fieldContext_IssueRepositoryConnection_edges(ctx, field) + case "pageInfo": + return ec.fieldContext_IssueRepositoryConnection_pageInfo(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type IssueRepositoryConnection", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Service_issueRepositories_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Service_componentInstances(ctx context.Context, field graphql.CollectedField, obj *model.Service) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Service_componentInstances(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 ec.resolvers.Service().ComponentInstances(rctx, obj, fc.Args["filter"].(*model.ComponentInstanceFilter), fc.Args["first"].(*int), fc.Args["after"].(*string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*model.ComponentInstanceConnection) + fc.Result = res + return ec.marshalOComponentInstanceConnection2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐComponentInstanceConnection(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Service_componentInstances(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Service", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "totalCount": + return ec.fieldContext_ComponentInstanceConnection_totalCount(ctx, field) + case "edges": + return ec.fieldContext_ComponentInstanceConnection_edges(ctx, field) + case "pageInfo": + return ec.fieldContext_ComponentInstanceConnection_pageInfo(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ComponentInstanceConnection", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) } }() ctx = graphql.WithFieldContext(ctx, fc) @@ -23615,75 +24165,143 @@ func (ec *executionContext) unmarshalInputIssueVariantFilter(ctx context.Context asMap[k] = v } - fieldsInOrder := [...]string{"secondaryName"} + fieldsInOrder := [...]string{"secondaryName"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "secondaryName": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("secondaryName")) + data, err := ec.unmarshalOString2ᚕᚖstring(ctx, v) + if err != nil { + return it, err + } + it.SecondaryName = data + } + } + + return it, nil +} + +func (ec *executionContext) unmarshalInputIssueVariantInput(ctx context.Context, obj interface{}) (model.IssueVariantInput, error) { + var it model.IssueVariantInput + asMap := map[string]interface{}{} + for k, v := range obj.(map[string]interface{}) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"secondaryName", "description", "issueRepositoryId", "issueId", "severity"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "secondaryName": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("secondaryName")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.SecondaryName = data + case "description": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("description")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.Description = data + case "issueRepositoryId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("issueRepositoryId")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.IssueRepositoryID = data + case "issueId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("issueId")) + data, err := ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + it.IssueID = data + case "severity": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("severity")) + data, err := ec.unmarshalOSeverityInput2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐSeverityInput(ctx, v) + if err != nil { + return it, err + } + it.Severity = data + } + } + + return it, nil +} + +func (ec *executionContext) unmarshalInputScanFilter(ctx context.Context, obj interface{}) (model.ScanFilter, error) { + var it model.ScanFilter + asMap := map[string]interface{}{} + for k, v := range obj.(map[string]interface{}) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"status"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { continue } switch k { - case "secondaryName": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("secondaryName")) + case "status": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("status")) data, err := ec.unmarshalOString2ᚕᚖstring(ctx, v) if err != nil { return it, err } - it.SecondaryName = data + it.Status = data } } return it, nil } -func (ec *executionContext) unmarshalInputIssueVariantInput(ctx context.Context, obj interface{}) (model.IssueVariantInput, error) { - var it model.IssueVariantInput +func (ec *executionContext) unmarshalInputScanInput(ctx context.Context, obj interface{}) (model.ScanInput, error) { + var it model.ScanInput asMap := map[string]interface{}{} for k, v := range obj.(map[string]interface{}) { asMap[k] = v } - fieldsInOrder := [...]string{"secondaryName", "description", "issueRepositoryId", "issueId", "severity"} + fieldsInOrder := [...]string{"type", "scope", "status"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { continue } switch k { - case "secondaryName": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("secondaryName")) - data, err := ec.unmarshalOString2ᚖstring(ctx, v) - if err != nil { - return it, err - } - it.SecondaryName = data - case "description": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("description")) - data, err := ec.unmarshalOString2ᚖstring(ctx, v) - if err != nil { - return it, err - } - it.Description = data - case "issueRepositoryId": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("issueRepositoryId")) + case "type": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("type")) data, err := ec.unmarshalOString2ᚖstring(ctx, v) if err != nil { return it, err } - it.IssueRepositoryID = data - case "issueId": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("issueId")) + it.Type = data + case "scope": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("scope")) data, err := ec.unmarshalOString2ᚖstring(ctx, v) if err != nil { return it, err } - it.IssueID = data - case "severity": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("severity")) - data, err := ec.unmarshalOSeverityInput2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐSeverityInput(ctx, v) + it.Scope = data + case "status": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("status")) + data, err := ec.unmarshalOScanStatusValues2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐScanStatusValues(ctx, v) if err != nil { return it, err } - it.Severity = data + it.Status = data } } @@ -24013,6 +24631,13 @@ func (ec *executionContext) _Connection(ctx context.Context, sel ast.SelectionSe return graphql.Null } return ec._IssueVariantConnection(ctx, sel, obj) + case model.ScanConnection: + return ec._ScanConnection(ctx, sel, &obj) + case *model.ScanConnection: + if obj == nil { + return graphql.Null + } + return ec._ScanConnection(ctx, sel, obj) case model.ServiceConnection: return ec._ServiceConnection(ctx, sel, &obj) case *model.ServiceConnection: @@ -24113,6 +24738,13 @@ func (ec *executionContext) _Edge(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } return ec._IssueVariantEdge(ctx, sel, obj) + case model.ScanEdge: + return ec._ScanEdge(ctx, sel, &obj) + case *model.ScanEdge: + if obj == nil { + return graphql.Null + } + return ec._ScanEdge(ctx, sel, obj) case model.ServiceEdge: return ec._ServiceEdge(ctx, sel, &obj) case *model.ServiceEdge: @@ -24213,6 +24845,13 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } return ec._IssueVariant(ctx, sel, obj) + case model.Scan: + return ec._Scan(ctx, sel, &obj) + case *model.Scan: + if obj == nil { + return graphql.Null + } + return ec._Scan(ctx, sel, obj) case model.Service: return ec._Service(ctx, sel, &obj) case *model.Service: @@ -27695,6 +28334,166 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return out } +var scanImplementors = []string{"Scan", "Node"} + +func (ec *executionContext) _Scan(ctx context.Context, sel ast.SelectionSet, obj *model.Scan) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, scanImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Scan") + case "id": + out.Values[i] = ec._Scan_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "type": + out.Values[i] = ec._Scan_type(ctx, field, obj) + case "scope": + out.Values[i] = ec._Scan_scope(ctx, field, obj) + case "users": + field := field + + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Scan_users(ctx, field, obj) + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var scanConnectionImplementors = []string{"ScanConnection", "Connection"} + +func (ec *executionContext) _ScanConnection(ctx context.Context, sel ast.SelectionSet, obj *model.ScanConnection) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, scanConnectionImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ScanConnection") + case "totalCount": + out.Values[i] = ec._ScanConnection_totalCount(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "edges": + out.Values[i] = ec._ScanConnection_edges(ctx, field, obj) + case "pageInfo": + out.Values[i] = ec._ScanConnection_pageInfo(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var scanEdgeImplementors = []string{"ScanEdge", "Edge"} + +func (ec *executionContext) _ScanEdge(ctx context.Context, sel ast.SelectionSet, obj *model.ScanEdge) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, scanEdgeImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ScanEdge") + case "node": + out.Values[i] = ec._ScanEdge_node(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "cursor": + out.Values[i] = ec._ScanEdge_cursor(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var serviceImplementors = []string{"Service", "Node"} func (ec *executionContext) _Service(ctx context.Context, sel ast.SelectionSet, obj *model.Service) graphql.Marshaler { @@ -29106,6 +29905,16 @@ func (ec *executionContext) unmarshalNIssueVariantInput2githubᚗwdfᚗsapᚗcor return res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) marshalNScan2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐScan(ctx context.Context, sel ast.SelectionSet, v *model.Scan) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._Scan(ctx, sel, v) +} + func (ec *executionContext) marshalNService2githubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐService(ctx context.Context, sel ast.SelectionSet, v model.Service) graphql.Marshaler { return ec._Service(ctx, sel, &v) } @@ -30525,6 +31334,70 @@ func (ec *executionContext) marshalOPageInfo2ᚖgithubᚗwdfᚗsapᚗcorpᚋcc return ec._PageInfo(ctx, sel, v) } +func (ec *executionContext) marshalOScanEdge2ᚕᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐScanEdge(ctx context.Context, sel ast.SelectionSet, v []*model.ScanEdge) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalOScanEdge2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐScanEdge(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + return ret +} + +func (ec *executionContext) marshalOScanEdge2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐScanEdge(ctx context.Context, sel ast.SelectionSet, v *model.ScanEdge) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._ScanEdge(ctx, sel, v) +} + +func (ec *executionContext) unmarshalOScanStatusValues2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐScanStatusValues(ctx context.Context, v interface{}) (*model.ScanStatusValues, error) { + if v == nil { + return nil, nil + } + var res = new(model.ScanStatusValues) + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOScanStatusValues2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐScanStatusValues(ctx context.Context, sel ast.SelectionSet, v *model.ScanStatusValues) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return v +} + func (ec *executionContext) marshalOService2ᚖgithubᚗwdfᚗsapᚗcorpᚋccᚋheurekaᚋinternalᚋapiᚋgraphqlᚋgraphᚋmodelᚐService(ctx context.Context, sel ast.SelectionSet, v *model.Service) graphql.Marshaler { if v == nil { return graphql.Null diff --git a/internal/api/graphql/graph/model/models_gen.go b/internal/api/graphql/graph/model/models_gen.go index 816b0852..996e7cd6 100644 --- a/internal/api/graphql/graph/model/models_gen.go +++ b/internal/api/graphql/graph/model/models_gen.go @@ -566,6 +566,45 @@ type PageInfo struct { type Query struct { } +type Scan struct { + ID string `json:"id"` + Type *string `json:"type,omitempty"` + Scope *string `json:"scope,omitempty"` + Users *UserConnection `json:"users,omitempty"` +} + +func (Scan) IsNode() {} +func (this Scan) GetID() string { return this.ID } + +type ScanConnection struct { + TotalCount int `json:"totalCount"` + Edges []*ScanEdge `json:"edges,omitempty"` + PageInfo *PageInfo `json:"pageInfo,omitempty"` +} + +func (ScanConnection) IsConnection() {} +func (this ScanConnection) GetTotalCount() int { return this.TotalCount } +func (this ScanConnection) GetPageInfo() *PageInfo { return this.PageInfo } + +type ScanEdge struct { + Node *Scan `json:"node"` + Cursor *string `json:"cursor,omitempty"` +} + +func (ScanEdge) IsEdge() {} +func (this ScanEdge) GetNode() Node { return *this.Node } +func (this ScanEdge) GetCursor() *string { return this.Cursor } + +type ScanFilter struct { + Status []*string `json:"status,omitempty"` +} + +type ScanInput struct { + Type *string `json:"type,omitempty"` + Scope *string `json:"scope,omitempty"` + Status *ScanStatusValues `json:"status,omitempty"` +} + type Service struct { ID string `json:"id"` Name *string `json:"name,omitempty"` @@ -961,6 +1000,49 @@ func (e IssueTypes) MarshalGQL(w io.Writer) { fmt.Fprint(w, strconv.Quote(e.String())) } +type ScanStatusValues string + +const ( + ScanStatusValuesInProgress ScanStatusValues = "inProgress" + ScanStatusValuesFail ScanStatusValues = "fail" + ScanStatusValuesSuccess ScanStatusValues = "success" +) + +var AllScanStatusValues = []ScanStatusValues{ + ScanStatusValuesInProgress, + ScanStatusValuesFail, + ScanStatusValuesSuccess, +} + +func (e ScanStatusValues) IsValid() bool { + switch e { + case ScanStatusValuesInProgress, ScanStatusValuesFail, ScanStatusValuesSuccess: + return true + } + return false +} + +func (e ScanStatusValues) String() string { + return string(e) +} + +func (e *ScanStatusValues) UnmarshalGQL(v interface{}) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + + *e = ScanStatusValues(str) + if !e.IsValid() { + return fmt.Errorf("%s is not a valid ScanStatusValues", str) + } + return nil +} + +func (e ScanStatusValues) MarshalGQL(w io.Writer) { + fmt.Fprint(w, strconv.Quote(e.String())) +} + type SeverityValues string const ( diff --git a/internal/api/graphql/graph/resolver/scan.go b/internal/api/graphql/graph/resolver/scan.go new file mode 100644 index 00000000..89dacdc5 --- /dev/null +++ b/internal/api/graphql/graph/resolver/scan.go @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + +package resolver + +// This file will be automatically regenerated based on the schema, any resolver implementations +// will be copied through when generating and any unknown code will be moved to the end. +// Code generated by github.com/99designs/gqlgen version v0.17.49 + +import ( + "context" + + "github.wdf.sap.corp/cc/heureka/internal/api/graphql/graph" + "github.wdf.sap.corp/cc/heureka/internal/api/graphql/graph/baseResolver" + "github.wdf.sap.corp/cc/heureka/internal/api/graphql/graph/model" +) + +// Users is the resolver for the users field. +func (r *scanResolver) Users(ctx context.Context, obj *model.Scan, filter *model.UserFilter, first *int, after *string) (*model.UserConnection, error) { + return baseResolver.UserBaseResolver(r.App, ctx, filter, first, after, + &model.NodeParent{ + Parent: obj, + ParentName: model.UserNodeName, + }) + +} + +// Scan returns graph.ScanResolver implementation. +func (r *Resolver) Scan() graph.ScanResolver { return &scanResolver{r} } + +type scanResolver struct{ *Resolver } diff --git a/internal/api/graphql/graph/schema/scan.graphqls b/internal/api/graphql/graph/schema/scan.graphqls new file mode 100644 index 00000000..9f431524 --- /dev/null +++ b/internal/api/graphql/graph/schema/scan.graphqls @@ -0,0 +1,37 @@ +# SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +# SPDX-License-Identifier: Apache-2.0 + +type Scan implements Node { + id: ID! + type: String + scope: String + users(filter: UserFilter, first: Int, after: String): UserConnection +} + +input ScanInput { + type: String + scope: String + status: ScanStatusValues +} + +input ScanFilter { + # TODO: Filter also by ID + status: [String] +} + +enum ScanStatusValues { + inProgress + fail + success +} + +type ScanConnection implements Connection { + totalCount: Int! + edges: [ScanEdge] + pageInfo: PageInfo +} + +type ScanEdge implements Edge { + node: Scan! + cursor: String +} diff --git a/internal/app/interface.go b/internal/app/interface.go index 82961c27..8da0da46 100644 --- a/internal/app/interface.go +++ b/internal/app/interface.go @@ -94,6 +94,11 @@ type Heureka interface { UpdateComponentVersion(*entity.ComponentVersion) (*entity.ComponentVersion, error) DeleteComponentVersion(int64) error + ListScans(*entity.ScanFilter, *entity.ListOptions) (*entity.List[entity.ScanResult], error) + CreateScan(*entity.Scan) (*entity.Scan, error) + UpdateScan(*entity.Scan) (*entity.Scan, error) + DeleteScan(int64) error + GetSeverity(*entity.SeverityFilter) (*entity.Severity, error) Shutdown() error } diff --git a/internal/app/scan.go b/internal/app/scan.go new file mode 100644 index 00000000..6b26e4fd --- /dev/null +++ b/internal/app/scan.go @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + +package app + +import ( + "github.wdf.sap.corp/cc/heureka/internal/entity" +) + +// TODO: Implement me +func (h *HeurekaApp) ListScans(filter *entity.ScanFilter, options *entity.ListOptions) (*entity.List[entity.ScanResult], error) { + return nil, nil +} + +// TODO: Implement me +func (h *HeurekaApp) CreateScan(scan *entity.Scan) (*entity.Scan, error) { + return nil, nil +} + +// TODO: Implement me +func (h *HeurekaApp) UpdateScan(component *entity.Scan) (*entity.Scan, error) { + return nil, nil +} + +// TODO: Implement me +func (h *HeurekaApp) DeleteScan(id int64) error { + return nil +} diff --git a/internal/entity/scan.go b/internal/entity/scan.go new file mode 100644 index 00000000..b9168d69 --- /dev/null +++ b/internal/entity/scan.go @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + +package entity + +import "time" + +type ScanType string + +const ( + ScanTypeInProgress ScanType = "InProgress" + ScanTypeFail ScanType = "Fail" + ScanTypeSuccess ScanType = "Success" +) + +type Scan struct { + Id int64 `json:"id"` + Type ScanType `json:"type"` + Scope string `json:"scope"` + StartedAt time.Time `json:"started_at"` + FinishedAt time.Time `json:"finished_at"` +} + +type ScanResult struct { + WithCursor + *Scan +} + +type ScanFilter struct { + Paginated + Id []*int64 `json:"id"` + Scope []*string `json:"scope"` +} From c5b3db68d0da47e5ee26b08133356795baa61427 Mon Sep 17 00:00:00 2001 From: Victor Dorneanu Date: Fri, 9 Aug 2024 10:21:50 +0000 Subject: [PATCH 2/3] feat(scans): Implement some of the DB layer components --- internal/database/mariadb/entity.go | 21 +++- internal/database/mariadb/scan.go | 135 +++++++++++++++++++++++++ internal/database/mariadb/scan_test.go | 69 +++++++++++++ internal/entity/common.go | 1 + internal/entity/scan.go | 17 ++++ 5 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 internal/database/mariadb/scan.go create mode 100644 internal/database/mariadb/scan_test.go diff --git a/internal/database/mariadb/entity.go b/internal/database/mariadb/entity.go index fcecacac..49ccaa54 100644 --- a/internal/database/mariadb/entity.go +++ b/internal/database/mariadb/entity.go @@ -73,7 +73,8 @@ type DatabaseRow interface { ActivityHasIssueRow | ActivityHasServiceRow | IssueRepositoryServiceRow | - IssueMatchChangeRow + IssueMatchChangeRow | + ScanRow } type IssueRow struct { @@ -732,3 +733,21 @@ type IssueRepositoryServiceRow struct { DeletedAt sql.NullTime `db:"issuerepositoryservice_deleted_at" json:"deleted_at,omitempty"` UpdatedAt sql.NullTime `db:"issuerepositoryservice_updated_at" json:"updated_at"` } + +type ScanRow struct { + ScanId sql.NullInt64 `db:"scan_id" json:"scan_id"` + ScanType sql.NullString `db:"scan_type" json:"scan_type"` + Scope sql.NullString `db:"scope" json:"scope"` + StartedAt sql.NullTime `db:"created_at" json:"created_at"` + FinishedAt sql.NullTime `db:"finished_at" json:"finished_at"` +} + +func (sr *ScanRow) AsScan() entity.Scan { + return entity.Scan{ + Id: GetInt64Value(sr.ScanId), + Type: entity.NewScanTypeValue(GetStringValue(sr.ScanType)), + Scope: GetStringValue(sr.Scope), + StartedAt: GetTimeValue(sr.StartedAt), + FinishedAt: GetTimeValue(sr.FinishedAt), + } +} diff --git a/internal/database/mariadb/scan.go b/internal/database/mariadb/scan.go new file mode 100644 index 00000000..83dacf8b --- /dev/null +++ b/internal/database/mariadb/scan.go @@ -0,0 +1,135 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + +package mariadb + +import ( + "fmt" + + "github.com/jmoiron/sqlx" + "github.com/sirupsen/logrus" + "github.wdf.sap.corp/cc/heureka/internal/entity" +) + +// GetScans ... +func (s *SqlDatabase) GetScans(filter *entity.ScanFilter) ([]entity.Scan, error) { + l := logrus.WithFields(logrus.Fields{ + "event": "database.GetComponents", + }) + + baseQuery := ` + SELECT S.* FROM Scans S + %s + %s + %s GROUP BY C.scan_id ORDER BY C.scan_id LIMIT ? + ` + filter = s.ensureScanFilter(filter) + + stmt, filterParameters, err := s.buildScanStatement(baseQuery, filter, true, l) + + if err != nil { + return nil, err + } + + defer stmt.Close() + + return performListScan( + stmt, + filterParameters, + l, + func(l []entity.Scan, e ScanRow) []entity.Scan { + return append(l, e.AsScan()) + }, + ) + +} + +// ensureScanFilter ... +func (s *SqlDatabase) ensureScanFilter(f *entity.ScanFilter) *entity.ScanFilter { + var first = 1000 + var after int64 = 0 + if f == nil { + return &entity.ScanFilter{ + Paginated: entity.Paginated{ + First: &first, + After: &after, + }, + Id: nil, + Scope: nil, + } + } + if f.After == nil { + f.After = &after + } + if f.First == nil { + f.First = &first + } + return f +} + +// getScanFilterString ... +func (s *SqlDatabase) getScanFilterString(filter *entity.ScanFilter) string { + var fl []string + fl = append(fl, buildFilterQuery(filter.Id, "S.scan_id = ?", OP_OR)) + fl = append(fl, "S.scan_finished_at IS NULL") + + return combineFilterQueries(fl, OP_AND) +} + +func (s *SqlDatabase) getScanJoins(filter *entity.ScanFilter) string { + joins := "" + if len(filter.Id) > 0 { + joins = fmt.Sprintf("%s\n%s", joins, "LEFT JOIN Users U on S.scan_id = U.user_id") + } + return joins +} + +// buildScanStatement ... +func (s *SqlDatabase) buildScanStatement(baseQuery string, filter *entity.ScanFilter, withCursor bool, l *logrus.Entry) (*sqlx.Stmt, []interface{}, error) { + var query string + filter = s.ensureScanFilter(filter) + l.WithFields(logrus.Fields{"filter": filter}) + + filterStr := s.getScanFilterString(filter) + joins := s.getScanJoins(filter) + cursor := getCursor(filter.Paginated, filterStr, "S.scan_id > ?") + + whereClause := "" + if filterStr != "" || withCursor { + whereClause = fmt.Sprintf("WHERE %s", filterStr) + } + + // construct final query + if withCursor { + query = fmt.Sprintf(baseQuery, joins, whereClause, cursor.Statement) + } else { + query = fmt.Sprintf(baseQuery, joins, whereClause) + } + + //construct prepared statement and if where clause does exist add parameters + var stmt *sqlx.Stmt + var err error + + stmt, err = s.db.Preparex(query) + if err != nil { + msg := ERROR_MSG_PREPARED_STMT + l.WithFields( + logrus.Fields{ + "error": err, + "query": query, + "stmt": stmt, + }).Error(msg) + return nil, nil, fmt.Errorf("%s", msg) + } + + //adding parameters + var filterParameters []interface{} + filterParameters = buildQueryParameters(filterParameters, filter.Id) + if withCursor { + filterParameters = append(filterParameters, cursor.Value) + filterParameters = append(filterParameters, cursor.Limit) + } + + return stmt, filterParameters, nil + +} diff --git a/internal/database/mariadb/scan_test.go b/internal/database/mariadb/scan_test.go new file mode 100644 index 00000000..bdb79c6b --- /dev/null +++ b/internal/database/mariadb/scan_test.go @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + +package mariadb_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/samber/lo" + "github.wdf.sap.corp/cc/heureka/internal/database/mariadb" + "github.wdf.sap.corp/cc/heureka/internal/database/mariadb/test" +) + +var _ = Describe("Scan", Label("database", "Scan"), func() { + + var db *mariadb.SqlDatabase + var seeder *test.DatabaseSeeder + BeforeEach(func() { + var err error + db = dbm.NewTestSchema() + seeder, err = test.NewDatabaseSeeder(dbm.DbConfig()) + Expect(err).To(BeNil(), "Database Seeder Setup should work") + }) + + When("Getting All Scan IDs", Label("GetScans"), func() { + Context("and the database is empty", func() { + It("can perform the query", func() { + res, err := db.GetScans(nil) + + By("throwing no error", func() { + Expect(err).To(BeNil()) + }) + By("returning an empty list", func() { + Expect(res).To(BeEmpty()) + }) + }) + }) + Context("and we have 20 Components in the database", func() { + var seedCollection *test.SeedCollection + var ids []int64 + BeforeEach(func() { + seedCollection = seeder.SeedDbWithNFakeData(10) + + for _, c := range seedCollection.ScanRows { + ids = append(ids, c.Id.Int64) + } + }) + Context("and using no filter", func() { + It("can fetch the items correctly", func() { + res, err := db.GetScans(nil) + + By("throwing no error", func() { + Expect(err).Should(BeNil()) + }) + + By("returning the correct number of results", func() { + Expect(len(res)).Should(BeIdenticalTo(len(seedCollection.ComponentRows))) + }) + + By("returning the correct fields", func() { + for _, r := range res { + Expect(lo.Contains(ids, r)).To(BeTrue()) + } + }) + }) + }) + }) + }) +}) diff --git a/internal/entity/common.go b/internal/entity/common.go index a561ccfb..68e967b9 100644 --- a/internal/entity/common.go +++ b/internal/entity/common.go @@ -41,6 +41,7 @@ type HeurekaEntity interface { Issue | IssueMatch | IssueMatchChange | + Scan | HeurekaFilter } diff --git a/internal/entity/scan.go b/internal/entity/scan.go index b9168d69..02febffa 100644 --- a/internal/entity/scan.go +++ b/internal/entity/scan.go @@ -11,8 +11,25 @@ const ( ScanTypeInProgress ScanType = "InProgress" ScanTypeFail ScanType = "Fail" ScanTypeSuccess ScanType = "Success" + ScanTypeUnknown ScanType = "Unknown" ) +func (e ScanType) String() string { + return string(e) +} + +func NewScanTypeValue(s string) ScanType { + switch s { + case ScanTypeInProgress.String(): + return ScanTypeInProgress + case ScanTypeFail.String(): + return ScanTypeFail + case ScanTypeSuccess.String(): + return ScanTypeSuccess + } + return ScanTypeUnknown +} + type Scan struct { Id int64 `json:"id"` Type ScanType `json:"type"` From 050bbc0d262b9786e899a28b3ef6223197ca6e7c Mon Sep 17 00:00:00 2001 From: Victor Dorneanu Date: Fri, 9 Aug 2024 13:46:13 +0000 Subject: [PATCH 3/3] feat(scans): Add DB related tests --- internal/database/mariadb/test/fixture.go | 9 +++++++ internal/database/mariadb/test/scan.go | 33 +++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 internal/database/mariadb/test/scan.go diff --git a/internal/database/mariadb/test/fixture.go b/internal/database/mariadb/test/fixture.go index bf4d2795..18fe5827 100644 --- a/internal/database/mariadb/test/fixture.go +++ b/internal/database/mariadb/test/fixture.go @@ -43,6 +43,7 @@ type SeedCollection struct { IssueMatchEvidenceRows []mariadb.IssueMatchEvidenceRow IssueMatchChangeRows []mariadb.IssueMatchChangeRow IssueRepositoryServiceRows []mariadb.IssueRepositoryServiceRow + ScanRows []mariadb.ScanRow } func (s *SeedCollection) GetIssueVariantsByIssueId(id int64) []mariadb.IssueVariantRow { @@ -809,6 +810,14 @@ func (s *DatabaseSeeder) SeedIssueMatchChanges(num int, issueMatches []mariadb.I return rows } +// SeedScans ... +func (s *DatabaseSeeder) SeedScans(num int, scans []mariadb.ScanRow) []mariadb.ScanRow { + var rows []mariadb.ScanRow + for i := 0; i < num; i++ { + srs := NewFakeScan() + } +} + func (s *DatabaseSeeder) InsertFakeIssueMatchEvidence(ime mariadb.IssueMatchEvidenceRow) (int64, error) { query := ` INSERT INTO IssueMatchEvidence ( diff --git a/internal/database/mariadb/test/scan.go b/internal/database/mariadb/test/scan.go new file mode 100644 index 00000000..995dbda6 --- /dev/null +++ b/internal/database/mariadb/test/scan.go @@ -0,0 +1,33 @@ +package test + +import ( + "database/sql" + + "github.com/brianvoe/gofakeit/v7" + "github.wdf.sap.corp/cc/heureka/internal/database/mariadb" +) + +func NewFakeScan() mariadb.ScanRow { + scanTypes := []string{"inProgress", "fail", "success"} + // TODO: Add missing fields here + + return mariadb.ScanRow{ + ScanId: sql.NullInt64{Int64: gofakeit.Int64(), Valid: true}, + ScanType: sql.NullString{ + String: gofakeit.RandomString(scanTypes), + Valid: true, + }, + Scope: sql.NullString{String: gofakeit.RandomString(scanTypes), Valid: true}, + } +} + +// InsertFakeScan inserts a new Scan into the tables +func (s *DatabaseSeeder) InsertFakeScan(scan mariadb.ScanRow) (int64, error) { + query := ` + INSERT INTO Scans ( + scan_id, + ) VALUES ( + :scan_id, + )` + return s.ExecPreparedNamed(query, scan) +}