diff --git a/Makefile b/Makefile index aa985113..516da37b 100644 --- a/Makefile +++ b/Makefile @@ -75,13 +75,13 @@ GINKGO := go run github.com/onsi/ginkgo/v2/ginkgo test-all: mockery gqlgen $(GINKGO) -r -test-e2e: +test-e2e: gqlgen $(GINKGO) -r internal/e2e -test-app: +test-app: gqlgen $(GINKGO) -r internal/app -test-db: +test-db: gqlgen $(GINKGO) -r internal/database/mariadb fmt: diff --git a/internal/api/graphql/graph/model/models.go b/internal/api/graphql/graph/model/models.go index 404a89a5..184ec176 100644 --- a/internal/api/graphql/graph/model/models.go +++ b/internal/api/graphql/graph/model/models.go @@ -224,6 +224,33 @@ func NewIssueEntity(issue *IssueInput) entity.Issue { } } +func NewScannerRunEntity(sr *ScannerRunInput) entity.ScannerRun { + + return entity.ScannerRun{ + RunID: -1, + UUID: lo.FromPtr(sr.UUID), + Tag: lo.FromPtr(sr.Tag), + Completed: false, + StartRun: time.Now(), + EndRun: time.Now(), + } +} + +func NewScannerRun(sr *entity.ScannerRun) ScannerRun { + startRun := sr.StartRun.Format(time.RFC3339) + endRun := sr.EndRun.Format(time.RFC3339) + + return ScannerRun{ + ID: fmt.Sprintf("%d", sr.RunID), + UUID: sr.UUID, + Tag: sr.Tag, + Completed: sr.Completed, + StartRun: startRun, + EndRun: + endRun, + } +} + func NewIssueMatch(im *entity.IssueMatch) IssueMatch { status := IssueMatchStatusValue(im.Status.String()) targetRemediationDate := im.TargetRemediationDate.Format(time.RFC3339) diff --git a/internal/api/graphql/graph/queryCollection/scannerRun/create.graphql b/internal/api/graphql/graph/queryCollection/scannerRun/create.graphql new file mode 100644 index 00000000..8dd3d1ce --- /dev/null +++ b/internal/api/graphql/graph/queryCollection/scannerRun/create.graphql @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +# SPDX-License-Identifier: Apache-2.0 + +mutation ($input: ScannerRunInput!) { + createScannerRun ( + input: $input + ) { + id + tag + uuid + } +} \ No newline at end of file diff --git a/internal/api/graphql/graph/resolver/mutation.go b/internal/api/graphql/graph/resolver/mutation.go index bc36105e..033f5216 100644 --- a/internal/api/graphql/graph/resolver/mutation.go +++ b/internal/api/graphql/graph/resolver/mutation.go @@ -887,6 +887,16 @@ func (r *mutationResolver) RemoveIssueFromActivity(ctx context.Context, activity return &a, nil } +func (r *mutationResolver) CreateScannerRun(ctx context.Context, input model.ScannerRunInput) (*model.ScannerRun, error) { + scannerRun := model.NewScannerRunEntity(&input) + newScannerRun, err := r.App.CreateScannerRun(&scannerRun) + if err != nil { + return nil, baseResolver.NewResolverError("CreateScannerRunMutationResolver", "Internal Error - when creating scannerRun") + } + sr := model.NewScannerRun(newScannerRun) + return &sr, nil +} + func (r *Resolver) Mutation() graph.MutationResolver { return &mutationResolver{r} } type mutationResolver struct{ *Resolver } diff --git a/internal/api/graphql/graph/schema/mutation.graphqls b/internal/api/graphql/graph/schema/mutation.graphqls index 30f8b71e..ad3ca089 100644 --- a/internal/api/graphql/graph/schema/mutation.graphqls +++ b/internal/api/graphql/graph/schema/mutation.graphqls @@ -69,4 +69,6 @@ type Mutation { removeServiceFromActivity(activityId: ID!, serviceId: ID!): Activity! addIssueToActivity(activityId: ID!, issueId: ID!): Activity! removeIssueFromActivity(activityId: ID!, issueId: ID!): Activity! + + createScannerRun(input: ScannerRunInput!): ScannerRun! } \ No newline at end of file diff --git a/internal/api/graphql/graph/schema/scanner_run.graphqls b/internal/api/graphql/graph/schema/scanner_run.graphqls new file mode 100644 index 00000000..70faa296 --- /dev/null +++ b/internal/api/graphql/graph/schema/scanner_run.graphqls @@ -0,0 +1,28 @@ +# SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +# SPDX-License-Identifier: Apache-2.0 + +type ScannerRun implements Node { + id: ID! + uuid: String! + tag: String! + start_run: DateTime! + end_run: DateTime! + completed: Boolean! + metadata: Metadata +} + +input ScannerRunInput { + uuid: String + tag: String +} + +type ScannerRunConnection implements Connection { + totalCount: Int! + edges: [ScannerRun] + pageInfo: PageInfo +} + +type ScannerRunEdge implements Edge { + node: ScannerRun! + cursor: String +} \ No newline at end of file diff --git a/internal/app/heureka.go b/internal/app/heureka.go index 9c404fcb..90786458 100644 --- a/internal/app/heureka.go +++ b/internal/app/heureka.go @@ -17,6 +17,7 @@ import ( "github.com/cloudoperators/heureka/internal/app/issue_match_change" "github.com/cloudoperators/heureka/internal/app/issue_repository" "github.com/cloudoperators/heureka/internal/app/issue_variant" + "github.com/cloudoperators/heureka/internal/app/scanner_run" "github.com/cloudoperators/heureka/internal/app/service" "github.com/cloudoperators/heureka/internal/app/severity" "github.com/cloudoperators/heureka/internal/app/support_group" @@ -26,15 +27,16 @@ import ( type HeurekaApp struct { activity.ActivityHandler - component.ComponentHandler component_instance.ComponentInstanceHandler component_version.ComponentVersionHandler + component.ComponentHandler evidence.EvidenceHandler - issue.IssueHandler - issue_match.IssueMatchHandler issue_match_change.IssueMatchChangeHandler + issue_match.IssueMatchHandler issue_repository.IssueRepositoryHandler issue_variant.IssueVariantHandler + issue.IssueHandler + scanner_run.ScannerRunHandler service.ServiceHandler severity.SeverityHandler support_group.SupportGroupHandler @@ -57,10 +59,11 @@ func NewHeurekaApp(db database.Database) *HeurekaApp { ComponentVersionHandler: component_version.NewComponentVersionHandler(db, er), EvidenceHandler: evidence.NewEvidenceHandler(db, er), IssueHandler: issue.NewIssueHandler(db, er), - IssueMatchHandler: issue_match.NewIssueMatchHandler(db, er, sh), IssueMatchChangeHandler: issue_match_change.NewIssueMatchChangeHandler(db, er), + IssueMatchHandler: issue_match.NewIssueMatchHandler(db, er, sh), IssueRepositoryHandler: rh, IssueVariantHandler: ivh, + ScannerRunHandler: scanner_run.NewScannerRunHandler(db, er), ServiceHandler: service.NewServiceHandler(db, er), SeverityHandler: sh, SupportGroupHandler: support_group.NewSupportGroupHandler(db, er), diff --git a/internal/app/interface.go b/internal/app/interface.go index 41f7e430..408f5bfe 100644 --- a/internal/app/interface.go +++ b/internal/app/interface.go @@ -14,6 +14,7 @@ import ( "github.com/cloudoperators/heureka/internal/app/issue_match_change" "github.com/cloudoperators/heureka/internal/app/issue_repository" "github.com/cloudoperators/heureka/internal/app/issue_variant" + "github.com/cloudoperators/heureka/internal/app/scanner_run" "github.com/cloudoperators/heureka/internal/app/service" "github.com/cloudoperators/heureka/internal/app/severity" "github.com/cloudoperators/heureka/internal/app/support_group" @@ -21,21 +22,22 @@ import ( ) type Heureka interface { - issue.IssueHandler activity.ActivityHandler - service.ServiceHandler - user.UserHandler - component.ComponentHandler component_instance.ComponentInstanceHandler component_version.ComponentVersionHandler - support_group.SupportGroupHandler - issue_variant.IssueVariantHandler - issue_repository.IssueRepositoryHandler - issue_match.IssueMatchHandler - issue_match_change.IssueMatchChangeHandler - severity.SeverityHandler + component.ComponentHandler evidence.EvidenceHandler + issue_match_change.IssueMatchChangeHandler + issue_match.IssueMatchHandler issue_match.IssueMatchHandler + issue_repository.IssueRepositoryHandler + issue_variant.IssueVariantHandler + issue.IssueHandler + scanner_run.ScannerRunHandler + service.ServiceHandler + severity.SeverityHandler + support_group.SupportGroupHandler + user.UserHandler Shutdown() error } diff --git a/internal/app/scanner_run/scanner_run_events.go b/internal/app/scanner_run/scanner_run_events.go new file mode 100644 index 00000000..3bfcf221 --- /dev/null +++ b/internal/app/scanner_run/scanner_run_events.go @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + +package scanner_run + +import ( + "github.com/cloudoperators/heureka/internal/app/event" + "github.com/cloudoperators/heureka/internal/entity" +) + +const ( + CreateScannerRunEventName event.EventName = "CreateScannerRun" +) + +type CreateScannerRunEvent struct { + ScannerRun *entity.ScannerRun +} + +func (csr *CreateScannerRunEvent) Name() event.EventName { + return CreateScannerRunEventName +} diff --git a/internal/app/scanner_run/scanner_run_handler.go b/internal/app/scanner_run/scanner_run_handler.go new file mode 100644 index 00000000..8ae753a0 --- /dev/null +++ b/internal/app/scanner_run/scanner_run_handler.go @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + +package scanner_run + +import ( + "github.com/cloudoperators/heureka/internal/app/event" + "github.com/cloudoperators/heureka/internal/database" + "github.com/cloudoperators/heureka/internal/entity" +) + +type scannerRunHandler struct { + database database.Database + eventRegistry event.EventRegistry +} + +func NewScannerRunHandler(db database.Database, er event.EventRegistry) ScannerRunHandler { + return &scannerRunHandler{ + database: db, + eventRegistry: er, + } +} + +type ScannerRunHandlerError struct { + msg string +} + +func (srh *scannerRunHandler) CreateScannerRun(sr *entity.ScannerRun) (*entity.ScannerRun, error) { + _, err := srh.database.CreateScannerRun(sr) + + if err != nil { + return nil, &ScannerRunHandlerError{msg: "Error creating scanner run"} + } + + srh.eventRegistry.PushEvent(&CreateScannerRunEvent{sr}) + return sr, nil +} + +func (srhe *ScannerRunHandlerError) Error() string { + return srhe.msg +} diff --git a/internal/app/scanner_run/scanner_run_interface.go b/internal/app/scanner_run/scanner_run_interface.go new file mode 100644 index 00000000..224f18b6 --- /dev/null +++ b/internal/app/scanner_run/scanner_run_interface.go @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + +package scanner_run + +import "github.com/cloudoperators/heureka/internal/entity" + +type ScannerRunHandler interface { + CreateScannerRun(*entity.ScannerRun) (*entity.ScannerRun, error) +} diff --git a/internal/app/scanner_run/scanner_run_test.go b/internal/app/scanner_run/scanner_run_test.go new file mode 100644 index 00000000..97ba27a5 --- /dev/null +++ b/internal/app/scanner_run/scanner_run_test.go @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + +package scanner_run + +import ( + "testing" + + "github.com/cloudoperators/heureka/internal/app/event" + "github.com/cloudoperators/heureka/internal/entity/test" + + "github.com/cloudoperators/heureka/internal/entity" + "github.com/cloudoperators/heureka/internal/mocks" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestServiceHandler(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Service Service Test Suite") +} + +var er event.EventRegistry + +var _ = BeforeSuite(func() { + db := mocks.NewMockDatabase(GinkgoT()) + er = event.NewEventRegistry(db) +}) + +var sre *entity.ScannerRun + +var _ = Describe("When creating ScannerRun", Label("app", "CreateScannerRun"), func() { + var ( + db *mocks.MockDatabase + scannerRunHandler ScannerRunHandler + ) + + BeforeEach(func() { + db = mocks.NewMockDatabase(GinkgoT()) + sre = test.NewFakeScannerRunEntity() + }) + + It("creates a scannerrun", func() { + db.On("CreateScannerRun", sre).Return(sre, nil) + + scannerRunHandler = NewScannerRunHandler(db, er) + scannerRunHandler.CreateScannerRun(sre) + }) +}) diff --git a/internal/app/user/user_handler.go b/internal/app/user/user_handler.go index 51cb82b6..96ea96f1 100644 --- a/internal/app/user/user_handler.go +++ b/internal/app/user/user_handler.go @@ -31,7 +31,7 @@ type UserHandlerError struct { } func (e *UserHandlerError) Error() string { - return fmt.Sprintf("ServiceHandlerError: %s", e.msg) + return fmt.Sprintf("UserHandlerError: %s", e.msg) } func NewUserHandlerError(msg string) *UserHandlerError { diff --git a/internal/database/interface.go b/internal/database/interface.go index 6bc94636..8f4cfb85 100644 --- a/internal/database/interface.go +++ b/internal/database/interface.go @@ -126,5 +126,7 @@ type Database interface { UpdateComponentVersion(*entity.ComponentVersion) error DeleteComponentVersion(int64) error + CreateScannerRun(*entity.ScannerRun) (*entity.ScannerRun, error) + CloseConnection() error } diff --git a/internal/database/mariadb/entity.go b/internal/database/mariadb/entity.go index 0d14e775..d0111730 100644 --- a/internal/database/mariadb/entity.go +++ b/internal/database/mariadb/entity.go @@ -19,6 +19,14 @@ func GetInt64Value(v sql.NullInt64) int64 { } } +func GetBoolValue(v sql.NullBool) bool { + if v.Valid { + return v.Bool + } else { + return false + } +} + func GetInt16Value(v sql.NullInt16) int16 { if v.Valid { return v.Int16 @@ -972,3 +980,32 @@ 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 ScannerRunRow struct { + RunID sql.NullInt64 `db:"scannerrun_run_id"` + UUID sql.NullString `db:"scannerrun_uuid"` + Tag sql.NullString `db:"scannerrun_tag"` + StartRun sql.NullTime `db:"scannerrun_start_run"` + EndRun sql.NullTime `db:"scannerrun_end_run"` + IsCompleted sql.NullBool `db:"scannerrun_is_completed"` +} + +func (srr *ScannerRunRow) AsScannerRun() entity.ScannerRun { + return entity.ScannerRun{ + RunID: GetInt64Value(srr.RunID), + UUID: GetStringValue(srr.UUID), + Tag: GetStringValue(srr.Tag), + StartRun: GetTimeValue(srr.StartRun), + EndRun: GetTimeValue(srr.EndRun), + Completed: GetBoolValue(srr.IsCompleted), + } +} + +func (srr *ScannerRunRow) FromScannerRun(sr *entity.ScannerRun) { + srr.RunID = sql.NullInt64{Int64: sr.RunID, Valid: true} + srr.UUID = sql.NullString{String: sr.UUID, Valid: true} + srr.Tag = sql.NullString{String: sr.Tag, Valid: true} + srr.StartRun = sql.NullTime{Time: sr.StartRun, Valid: true} + srr.EndRun = sql.NullTime{Time: sr.EndRun, Valid: true} + srr.IsCompleted = sql.NullBool{Bool: sr.Completed, Valid: true} +} diff --git a/internal/database/mariadb/init/schema.sql b/internal/database/mariadb/init/schema.sql index 995d1fbe..5eda39fb 100644 --- a/internal/database/mariadb/init/schema.sql +++ b/internal/database/mariadb/init/schema.sql @@ -9,7 +9,7 @@ create table if not exists User ( user_id int unsigned auto_increment primary key, - user_name varchar(256) not null, + user_name varchar(255) not null, user_unique_user_id varchar(64) not null, user_type int unsigned, user_created_at timestamp default current_timestamp() not null, @@ -39,8 +39,8 @@ create table if not exists Component ( component_id int unsigned auto_increment primary key, - component_ccrn varchar(256) not null, - component_type varchar(256) not null, + component_ccrn varchar(255) not null, + component_type varchar(255) not null, component_created_at timestamp default current_timestamp() not null, component_created_by int unsigned null, component_deleted_at timestamp null, @@ -60,7 +60,7 @@ create table if not exists ComponentVersion ( componentversion_id int unsigned auto_increment primary key, - componentversion_version varchar(256) not null, + componentversion_version varchar(255) not null, componentversion_component_id int unsigned not null, componentversion_created_at timestamp default current_timestamp() not null, componentversion_created_by int unsigned null, @@ -84,7 +84,7 @@ create table if not exists SupportGroup ( supportgroup_id int unsigned auto_increment primary key, - supportgroup_ccrn varchar(256) not null, + supportgroup_ccrn varchar(255) not null, supportgroup_created_at timestamp default current_timestamp() not null, supportgroup_created_by int unsigned null, supportgroup_deleted_at timestamp null, @@ -104,7 +104,7 @@ create table if not exists Service ( service_id int unsigned auto_increment primary key, - service_ccrn varchar(256) not null, + service_ccrn varchar(255) not null, service_created_at timestamp default current_timestamp() not null, service_created_by int unsigned null, service_deleted_at timestamp null, @@ -274,7 +274,7 @@ create table if not exists Issue issue_id int unsigned auto_increment primary key, issue_type enum ('Vulnerability','PolicyViolation','SecurityEvent') not null, - issue_primary_name varchar(256) not null, + issue_primary_name varchar(255) not null, issue_description longtext not null, issue_created_at timestamp default current_timestamp() not null, issue_created_by int unsigned null, @@ -299,7 +299,7 @@ create table if not exists IssueVariant issuevariant_repository_id int unsigned not null, issuevariant_vector varchar(512) null, issuevariant_rating enum ('None','Low','Medium', 'High', 'Critical') not null, - issuevariant_secondary_name varchar(256) not null, + issuevariant_secondary_name varchar(255) not null, issuevariant_description longtext not null, issuevariant_created_at timestamp default current_timestamp() not null, issuevariant_created_by int unsigned null, @@ -450,3 +450,25 @@ create table if not exists IssueRepositoryService foreign key (issuerepositoryservice_service_id) references Service (service_id) on update cascade ); + + + +create table if not exists ScannerRun +( + scannerrun_run_id int unsigned primary key auto_increment, + scannerrun_uuid UUID not null unique, + scannerrun_tag varchar(255) not null, + scannerrun_start_run timestamp default current_timestamp() not null, + + scannerrun_end_run timestamp default current_timestamp() not null, + + scannerrun_is_completed boolean not null default false +); + +create table if not exists ScannerRunIssueTracker +( + scannerrunissuetracker_scannerrun_run_id int unsigned not null, + scannerrunissuetracker_issuematch_id int unsigned not null, + constraint fk_run_id foreign key (scannerrunissuetracker_scannerrun_run_id) references ScannerRun (scannerrun_run_id) on update cascade, + constraint fk_issuematch_id foreign key (scannerrunissuetracker_issuematch_id) references IssueMatch (issuematch_id) on update cascade +); \ No newline at end of file diff --git a/internal/database/mariadb/scanner_run.go b/internal/database/mariadb/scanner_run.go new file mode 100644 index 00000000..4ecc2b88 --- /dev/null +++ b/internal/database/mariadb/scanner_run.go @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + +package mariadb + +import ( + "github.com/cloudoperators/heureka/internal/entity" + "github.com/sirupsen/logrus" +) + +func (s *SqlDatabase) CreateScannerRun(scannerRun *entity.ScannerRun) (*entity.ScannerRun, error) { + l := logrus.WithFields(logrus.Fields{ + "scannerrun": scannerRun, + "event": "database.CreateScannerRun", + }) + + query := ` + INSERT INTO ScannerRun ( + scannerrun_uuid, + scannerrun_tag, + scannerrun_start_run, + scannerrun_end_run, + scannerrun_is_completed + ) VALUES ( + :scannerrun_uuid, + :scannerrun_tag, + :scannerrun_start_run, + :scannerrun_end_run, + :scannerrun_is_completed + ) + ` + + srr := ScannerRunRow{} + srr.FromScannerRun(scannerRun) + + id, err := performInsert(s, query, srr, l) + + if err != nil { + return nil, err + } + + scannerRun.RunID = id + + return scannerRun, nil +} diff --git a/internal/database/mariadb/scanner_run_test.go b/internal/database/mariadb/scanner_run_test.go new file mode 100644 index 00000000..c6a870c1 --- /dev/null +++ b/internal/database/mariadb/scanner_run_test.go @@ -0,0 +1,118 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + +package mariadb_test + +import ( + "math/rand" + + "github.com/cloudoperators/heureka/internal/database/mariadb" + "github.com/cloudoperators/heureka/internal/database/mariadb/test" + e2e_common "github.com/cloudoperators/heureka/internal/e2e/common" + "github.com/cloudoperators/heureka/internal/entity" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/samber/lo" +) + +var _ = Describe("ScannerRun", Label("database", "ScannerRun"), 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("Creating a new ScannerRun", Label("Create"), func() { + Context("and the database is empty", func() { + It("can perform the query", func() { + res, err := db.GetAllUserIds(nil) + res = e2e_common.SubtractSystemUserId(res) + + By("throwing no error", func() { + Expect(err).To(BeNil()) + }) + By("returning an empty list of non-system users", func() { + Expect(res).To(BeEmpty()) + }) + }) + }) + Context("and we have 20 Users in the database", func() { + var seedCollection *test.SeedCollection + var ids []int64 + BeforeEach(func() { + seedCollection = seeder.SeedDbWithNFakeData(10) + + for _, u := range seedCollection.UserRows { + ids = append(ids, u.Id.Int64) + } + }) + Context("and using no filter", func() { + It("can fetch the items correctly", func() { + res, err := db.GetAllUserIds(nil) + res = e2e_common.SubtractSystemUserId(res) + + By("throwing no error", func() { + Expect(err).Should(BeNil()) + }) + + By("returning the correct number of results", func() { + Expect(len(res)).Should(BeIdenticalTo(len(seedCollection.UserRows))) + }) + + By("returning the correct order", func() { + var prev int64 = 0 + for _, r := range res { + + Expect(r > prev).Should(BeTrue()) + prev = r + + } + }) + + By("returning the correct fields", func() { + for _, r := range res { + Expect(lo.Contains(ids, r)).To(BeTrue()) + } + }) + }) + }) + Context("and using a filter", func() { + It("can filter by a single user id that does exist", func() { + uId := ids[rand.Intn(len(ids))] + filter := &entity.UserFilter{ + Id: []*int64{&uId}, + } + + entries, err := db.GetAllUserIds(filter) + + By("throwing no error", func() { + Expect(err).To(BeNil()) + }) + + By("returning expected number of results", func() { + Expect(len(entries)).To(BeEquivalentTo(1)) + }) + + By("returning expected elements", func() { + Expect(entries[0]).To(BeEquivalentTo(uId)) + }) + }) + }) + }) + }) + Context("and the database is empty", func() { + It("should be initialized correctly", func() { + sr := &entity.ScannerRun{ + UUID: "6809de35-9716-4914-b090-15273f82e8ab", + Tag: "tag", + } + _, err := db.CreateScannerRun(sr) + Expect(err).To(BeNil()) + Expect(sr.RunID).To(BeNumerically(">=", 0)) + Expect(sr.IsCompleted()).To(BeFalse()) + }) + }) +}) diff --git a/internal/e2e/scanner_run_query_test.go b/internal/e2e/scanner_run_query_test.go new file mode 100644 index 00000000..4b9f3739 --- /dev/null +++ b/internal/e2e/scanner_run_query_test.go @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + +package e2e_test + +import ( + "context" + "fmt" + "os" + + "github.com/brianvoe/gofakeit/v7" + "github.com/cloudoperators/heureka/internal/util" + util2 "github.com/cloudoperators/heureka/pkg/util" + + "github.com/cloudoperators/heureka/internal/server" + + "github.com/cloudoperators/heureka/internal/api/graphql/graph/model" + "github.com/machinebox/graphql" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/sirupsen/logrus" +) + +var _ = Describe("Creating ScannerRun via API", Label("e2e", "ScannerRun"), func() { + + var s *server.Server + var cfg util.Config + + BeforeEach(func() { + _ = dbm.NewTestSchema() + + cfg = dbm.DbConfig() + cfg.Port = util2.GetRandomFreePort() + s = server.NewServer(cfg) + + s.NonBlockingStart() + }) + + AfterEach(func() { + s.BlockingStop() + }) + + When("the database is empty", func() { + + Context("and a mutation query is performed", Label("create.graphql"), func() { + It("creates new ScannerRun", func() { + sampleTag := gofakeit.Word() + sampleUUID := gofakeit.UUID() + + // create a queryCollection (safe to share across requests) + client := graphql.NewClient(fmt.Sprintf("http://localhost:%s/query", cfg.Port)) + + //@todo may need to make this more fault proof?! What if the test is executed from the root dir? does it still work? + b, err := os.ReadFile("../api/graphql/graph/queryCollection/scannerRun/create.graphql") + + Expect(err).To(BeNil()) + str := string(b) + req := graphql.NewRequest(str) + + req.Var("input", map[string]string{ + "tag": sampleTag, + "uuid": sampleUUID, + }) + + req.Header.Set("Cache-Control", "no-cache") + ctx := context.Background() + + var respData struct { + ScannerRun model.ScannerRun `json:"createScannerRun"` + } + if err := util2.RequestWithBackoff(func() error { return client.Run(ctx, req, &respData) }); err != nil { + logrus.WithError(err).WithField("request", req).Fatalln("Error while unmarshaling") + } + + Expect(*&respData.ScannerRun.ID).To(Equal("1")) + Expect(*&respData.ScannerRun.Tag).To(Equal(sampleTag)) + Expect(*&respData.ScannerRun.UUID).To(Equal(sampleUUID)) + }) + }) + }) +}) diff --git a/internal/entity/scanner_run.go b/internal/entity/scanner_run.go new file mode 100644 index 00000000..05dc365e --- /dev/null +++ b/internal/entity/scanner_run.go @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + +package entity + +import "time" + +type ScannerRun struct { + RunID int64 `json:"run_id"` + UUID string `json:"uuid"` + Tag string `json:"tag"` + StartRun time.Time `json:"start_run"` + EndRun time.Time `json:"end_run"` + Completed bool `json:"is_completed"` +} + +func (sc ScannerRun) IsCompleted() bool { + return sc.Completed +} diff --git a/internal/entity/test/scanner_run.go b/internal/entity/test/scanner_run.go new file mode 100644 index 00000000..779f6bc8 --- /dev/null +++ b/internal/entity/test/scanner_run.go @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + +package test + +import ( + "time" + + "github.com/brianvoe/gofakeit/v7" + "github.com/cloudoperators/heureka/internal/entity" +) + +func NewFakeScannerRunEntity() *entity.ScannerRun { + startRun := gofakeit.Date() + endRun := startRun.Add(time.Hour) + + return &entity.ScannerRun{ + RunID: int64(gofakeit.Number(1, 10000000)), + + UUID: gofakeit.UUID(), + Tag: gofakeit.Word(), + StartRun: startRun, + EndRun: endRun, + Completed: false, + } +}