From b14ca243c3d91c76c6933cebac451c3227f01fa1 Mon Sep 17 00:00:00 2001 From: Julian Kepka Date: Tue, 23 Jun 2026 16:13:10 +0200 Subject: [PATCH 1/4] feat: first approach advisory-tab --- cmd/devguard/main.go | 1 + controllers/advisory_controller.go | 51 +++++++++++++++++++ controllers/providers.go | 2 + .../20260623101120_add_advisory_table.up.sql | 6 +++ database/models/advisory_model.go | 10 ++++ database/repositories/advisory_repository.go | 33 ++++++++++++ database/repositories/providers.go | 1 + dtos/advisory_dto.go | 24 +++++++++ router/advisory_router.go | 38 ++++++++++++++ router/providers.go | 1 + services/advisory_service.go | 23 +++++++++ services/providers.go | 1 + shared/common_interfaces.go | 8 +++ 13 files changed, 199 insertions(+) create mode 100644 controllers/advisory_controller.go create mode 100644 database/migrations/20260623101120_add_advisory_table.up.sql create mode 100644 database/models/advisory_model.go create mode 100644 database/repositories/advisory_repository.go create mode 100644 dtos/advisory_dto.go create mode 100644 router/advisory_router.go create mode 100644 services/advisory_service.go diff --git a/cmd/devguard/main.go b/cmd/devguard/main.go index 2c091ade5..ef1653942 100644 --- a/cmd/devguard/main.go +++ b/cmd/devguard/main.go @@ -155,6 +155,7 @@ func main() { fx.Invoke(func(FalsePositiveRuleRouter router.VEXRuleRouter) {}), fx.Invoke(func(ExternalReferenceRouter router.ExternalReferenceRouter) {}), fx.Invoke(func(CrowdsourcedVexingRouter router.CrowdsourcedVexingRouter) {}), + fx.Invoke(func(AdvisoryRouter router.AdvisoryRouter) {}), fx.Invoke(func(lc fx.Lifecycle, encryptionService shared.DBEncryptionService) { lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error { diff --git a/controllers/advisory_controller.go b/controllers/advisory_controller.go new file mode 100644 index 000000000..6611147a7 --- /dev/null +++ b/controllers/advisory_controller.go @@ -0,0 +1,51 @@ +// Copyright (C) 2023 Tim Bastin, l3montree GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package controllers + +import ( + "github.com/l3montree-dev/devguard/dtos" + "github.com/l3montree-dev/devguard/shared" + "github.com/labstack/echo/v4" + "golang.org/x/exp/slog" +) + +type AdvisoryController struct { + advisoryService shared.AdvisoryService +} + +func NewAdvisoryController(advisoryService shared.AdvisoryService) *AdvisoryController { + return &AdvisoryController{ + advisoryService: advisoryService, + } +} + +func (controller *AdvisoryController) CreateName(ctx shared.Context) error { + slog.Info("test") + var req dtos.AdvisoryCreateName + if err := ctx.Bind(&req); err != nil { + return echo.NewHTTPError(400, "unable to process request").WithInternal(err) + } + + newName := req.Name + + err := controller.advisoryService.CreateName(ctx.Request().Context(), newName) + + if err != nil { + return echo.NewHTTPError(409, "could not set name").WithInternal(err) + } + + return ctx.JSON(200, newName) +} diff --git a/controllers/providers.go b/controllers/providers.go index b7d5696cf..df0b40cf5 100644 --- a/controllers/providers.go +++ b/controllers/providers.go @@ -122,4 +122,6 @@ var ControllerModule = fx.Options( //Crowdsourced Vexing fx.Provide(NewCrowdsourcedVexingController), + + fx.Provide(NewAdvisoryController), ) diff --git a/database/migrations/20260623101120_add_advisory_table.up.sql b/database/migrations/20260623101120_add_advisory_table.up.sql new file mode 100644 index 000000000..8d361fea0 --- /dev/null +++ b/database/migrations/20260623101120_add_advisory_table.up.sql @@ -0,0 +1,6 @@ +CREATE TABLE public.advisories ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW(), + advisory_name TEXT NOT NULL +) \ No newline at end of file diff --git a/database/models/advisory_model.go b/database/models/advisory_model.go new file mode 100644 index 000000000..340021711 --- /dev/null +++ b/database/models/advisory_model.go @@ -0,0 +1,10 @@ +package models + +type Advisory struct { + Model + AdvisoryName string `json:"name" gorm:"type:text;column:advisory_name"` +} + +func (m Advisory) TableName() string { + return "advisories" +} diff --git a/database/repositories/advisory_repository.go b/database/repositories/advisory_repository.go new file mode 100644 index 000000000..62c448afd --- /dev/null +++ b/database/repositories/advisory_repository.go @@ -0,0 +1,33 @@ +package repositories + +import ( + "context" + + "github.com/google/uuid" + "github.com/l3montree-dev/devguard/database/models" + "github.com/l3montree-dev/devguard/shared" + "github.com/l3montree-dev/devguard/utils" + "gorm.io/gorm" +) + +type AdvisoryRepository struct { + db *gorm.DB + utils.Repository[uuid.UUID, models.Advisory, *gorm.DB] +} + +func NewAdvisoryRepository(db *gorm.DB) *AdvisoryRepository { + return &AdvisoryRepository{ + db: db, + Repository: newGormRepository[uuid.UUID, models.Advisory](db), + } +} + +var _ shared.AdvisoryRepository = (*AdvisoryRepository)(nil) + +func (advisoryRepository *AdvisoryRepository) CreateName(ctx context.Context, tx *gorm.DB, name string) error { + err := advisoryRepository.GetDB(ctx, tx).Create(&models.Advisory{AdvisoryName: name}).Error + if err != nil { + return err + } + return nil +} diff --git a/database/repositories/providers.go b/database/repositories/providers.go index 84d540a31..fd9cc645c 100644 --- a/database/repositories/providers.go +++ b/database/repositories/providers.go @@ -59,4 +59,5 @@ var Module = fx.Options( fx.Provide(fx.Annotate(NewTrustedEntityRepository, fx.As(new(shared.TrustedEntityRepository)))), fx.Provide(fx.Annotate(NewDependencyProxyRepository, fx.As(new(shared.DependencyProxySecretRepository)))), fx.Provide(fx.Annotate(NewAdminRepository, fx.As(new(shared.AdminRepository)))), + fx.Provide(fx.Annotate(NewAdvisoryRepository, fx.As(new(shared.AdvisoryRepository)))), ) diff --git a/dtos/advisory_dto.go b/dtos/advisory_dto.go new file mode 100644 index 000000000..7242f601f --- /dev/null +++ b/dtos/advisory_dto.go @@ -0,0 +1,24 @@ +// Copyright (C) 2023 Tim Bastin, l3montree GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package dtos + +type AdvisoryCreateName struct { + Name string `json:"name" validate:"required"` +} + +type AdvisoryDTO struct { + Name string `json:"name"` +} diff --git a/router/advisory_router.go b/router/advisory_router.go new file mode 100644 index 000000000..e8efd528e --- /dev/null +++ b/router/advisory_router.go @@ -0,0 +1,38 @@ +// Copyright (C) 2024 Tim Bastin, l3montree GmbH +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package router + +import ( + "github.com/l3montree-dev/devguard/controllers" + "github.com/l3montree-dev/devguard/shared" + "github.com/labstack/echo/v4" +) + +type AdvisoryRouter struct { + *echo.Group +} + +func NewAdvisoryRouter( + assetRepository shared.AssetRepository, + assetVersionGroup AssetVersionRouter, + advisoryController *controllers.AdvisoryController, +) AdvisoryRouter { + advisoryRouter := assetVersionGroup.Group.Group("/advisory") + advisoryRouter.POST("/submit/", advisoryController.CreateName) + //advisoryRouter.GET("/", advisoryController.ReadName) + + return AdvisoryRouter{Group: advisoryRouter} +} diff --git a/router/providers.go b/router/providers.go index 7d5056903..d0624d0b7 100644 --- a/router/providers.go +++ b/router/providers.go @@ -22,4 +22,5 @@ var RouterModule = fx.Options( fx.Provide(NewVEXRuleRouter), fx.Provide(NewExternalReferenceRouter), fx.Provide(NewCrowdsourcedVexingRouter), + fx.Provide(NewAdvisoryRouter), ) diff --git a/services/advisory_service.go b/services/advisory_service.go new file mode 100644 index 000000000..d77179218 --- /dev/null +++ b/services/advisory_service.go @@ -0,0 +1,23 @@ +package services + +import ( + "context" + + "github.com/l3montree-dev/devguard/shared" +) + +type AdvisoryService struct { + advisoryRepository shared.AdvisoryRepository +} + +func NewAdvisoryService(advisoryRepository shared.AdvisoryRepository) *AdvisoryService { + return &AdvisoryService{ + advisoryRepository: advisoryRepository, + } +} + +var _ shared.AdvisoryService = (*AdvisoryService)(nil) + +func (s *AdvisoryService) CreateName(ctx context.Context, name string) error { + return s.advisoryRepository.CreateName(ctx, nil, name) +} diff --git a/services/providers.go b/services/providers.go index 54683f373..6ea5b6792 100644 --- a/services/providers.go +++ b/services/providers.go @@ -39,4 +39,5 @@ var ServiceModule = fx.Options( fx.Provide(fx.Annotate(NewAdminService, fx.As(new(shared.AdminService)))), fx.Provide(fx.Annotate(NewCrowdsourcedVexingService, fx.As(new(shared.CrowdSourcedVexingService)))), fx.Provide(fx.Annotate(NewDBEncryptionService, fx.As(new(shared.DBEncryptionService)))), + fx.Provide(fx.Annotate(NewAdvisoryService, fx.As(new(shared.AdvisoryService)))), ) diff --git a/shared/common_interfaces.go b/shared/common_interfaces.go index c6ff223a3..ed2ade03b 100644 --- a/shared/common_interfaces.go +++ b/shared/common_interfaces.go @@ -761,6 +761,14 @@ type RBACProvider interface { GetAllUsers() ([]string, error) } +type AdvisoryService interface { + CreateName(ctx context.Context, name string) error +} + +type AdvisoryRepository interface { + CreateName(ctx context.Context, tx DB, name string) error +} + type RBACMiddleware = func(obj Object, act Action) echo.MiddlewareFunc type Role string From 834c626f56cd5f1a6e36739aec2701f8bf0eacc2 Mon Sep 17 00:00:00 2001 From: Julian Kepka Date: Wed, 24 Jun 2026 11:24:34 +0200 Subject: [PATCH 2/4] feat: all four crud methods are implemented on first approach --- controllers/advisory_controller.go | 50 +++++++++++++++++++- database/repositories/advisory_repository.go | 28 +++++++++++ dtos/advisory_dto.go | 7 ++- router/advisory_router.go | 6 ++- services/advisory_service.go | 18 +++++++ shared/common_interfaces.go | 6 +++ 6 files changed, 109 insertions(+), 6 deletions(-) diff --git a/controllers/advisory_controller.go b/controllers/advisory_controller.go index 6611147a7..fdb77d54d 100644 --- a/controllers/advisory_controller.go +++ b/controllers/advisory_controller.go @@ -16,10 +16,10 @@ package controllers import ( + "github.com/google/uuid" "github.com/l3montree-dev/devguard/dtos" "github.com/l3montree-dev/devguard/shared" "github.com/labstack/echo/v4" - "golang.org/x/exp/slog" ) type AdvisoryController struct { @@ -33,7 +33,6 @@ func NewAdvisoryController(advisoryService shared.AdvisoryService) *AdvisoryCont } func (controller *AdvisoryController) CreateName(ctx shared.Context) error { - slog.Info("test") var req dtos.AdvisoryCreateName if err := ctx.Bind(&req); err != nil { return echo.NewHTTPError(400, "unable to process request").WithInternal(err) @@ -49,3 +48,50 @@ func (controller *AdvisoryController) CreateName(ctx shared.Context) error { return ctx.JSON(200, newName) } + +func (controller *AdvisoryController) ReadName(ctx shared.Context) error { + advisory_names, err := controller.advisoryService.ReadName(ctx.Request().Context()) + if err != nil { + return echo.NewHTTPError(500, "could not get any name").WithInternal(err) + } + return ctx.JSON(200, advisory_names) +} + +func (controller *AdvisoryController) UpdateName(ctx shared.Context) error { + var req dtos.AdvisoryCreateName // Change to own struct + if err := ctx.Bind(&req); err != nil { + return echo.NewHTTPError(400, "unable to process request").WithInternal(err) + } + + updateName := req.Name + + advisoryID := ctx.Param("id") + parsedID, err := uuid.Parse(advisoryID) + if err != nil { + return echo.NewHTTPError(400, "invalid uuid provided") + } + + err = controller.advisoryService.UpdateName(ctx.Request().Context(), parsedID, updateName) + + if err != nil { + return echo.NewHTTPError(409, "could not update name").WithInternal(err) + } + + return ctx.JSON(200, updateName) +} + +func (controller *AdvisoryController) DeleteName(ctx shared.Context) error { + advisoryID := ctx.Param("id") + parsedID, err := uuid.Parse(advisoryID) + if err != nil { + return echo.NewHTTPError(400, "invalid uuid provided") + } + + err = controller.advisoryService.DeleteName(ctx.Request().Context(), parsedID) + + if err != nil { + return echo.NewHTTPError(409, "could not remove name").WithInternal(err) + } + + return ctx.NoContent(200) +} diff --git a/database/repositories/advisory_repository.go b/database/repositories/advisory_repository.go index 62c448afd..2a36c9dbb 100644 --- a/database/repositories/advisory_repository.go +++ b/database/repositories/advisory_repository.go @@ -31,3 +31,31 @@ func (advisoryRepository *AdvisoryRepository) CreateName(ctx context.Context, tx } return nil } + +func (advisoryRepository *AdvisoryRepository) ReadName(ctx context.Context, tx *gorm.DB) ([]models.Advisory, error) { + advisory_names := []models.Advisory{} + db := advisoryRepository.db.WithContext(ctx) + if tx != nil { + db = tx + } + err := db.Raw(`SELECT * FROM advisories;`).Find(&advisory_names).Error + return advisory_names, err +} + +func (advisoryRepository *AdvisoryRepository) UpdateName(ctx context.Context, tx *gorm.DB, id uuid.UUID, name string) error { + err := advisoryRepository.GetDB(ctx, tx). + Model(&models.Advisory{Model: models.Model{ID: id}}). + Update("advisory_name", name).Error + if err != nil { + return err + } + return nil +} + +func (advisoryRepository *AdvisoryRepository) DeleteName(ctx context.Context, tx *gorm.DB, id uuid.UUID) error { + err := advisoryRepository.GetDB(ctx, tx).Delete(&models.Advisory{Model: models.Model{ID: id}}).Error + if err != nil { + return err + } + return nil +} diff --git a/dtos/advisory_dto.go b/dtos/advisory_dto.go index 7242f601f..e12a21fdd 100644 --- a/dtos/advisory_dto.go +++ b/dtos/advisory_dto.go @@ -15,10 +15,13 @@ package dtos +import "github.com/google/uuid" + type AdvisoryCreateName struct { - Name string `json:"name" validate:"required"` + Name string `json:"name" validate:"required"` } type AdvisoryDTO struct { - Name string `json:"name"` + ID uuid.UUID `json:"id"` + Name string `json:"name"` } diff --git a/router/advisory_router.go b/router/advisory_router.go index e8efd528e..84fbf57af 100644 --- a/router/advisory_router.go +++ b/router/advisory_router.go @@ -31,8 +31,10 @@ func NewAdvisoryRouter( advisoryController *controllers.AdvisoryController, ) AdvisoryRouter { advisoryRouter := assetVersionGroup.Group.Group("/advisory") - advisoryRouter.POST("/submit/", advisoryController.CreateName) - //advisoryRouter.GET("/", advisoryController.ReadName) + advisoryRouter.POST("/", advisoryController.CreateName) + advisoryRouter.GET("/", advisoryController.ReadName) + advisoryRouter.PATCH("/:id/", advisoryController.UpdateName) + advisoryRouter.DELETE("/:id/", advisoryController.DeleteName) return AdvisoryRouter{Group: advisoryRouter} } diff --git a/services/advisory_service.go b/services/advisory_service.go index d77179218..1f27cda0f 100644 --- a/services/advisory_service.go +++ b/services/advisory_service.go @@ -3,6 +3,8 @@ package services import ( "context" + "github.com/google/uuid" + "github.com/l3montree-dev/devguard/database/models" "github.com/l3montree-dev/devguard/shared" ) @@ -21,3 +23,19 @@ var _ shared.AdvisoryService = (*AdvisoryService)(nil) func (s *AdvisoryService) CreateName(ctx context.Context, name string) error { return s.advisoryRepository.CreateName(ctx, nil, name) } + +func (s *AdvisoryService) ReadName(ctx context.Context) ([]models.Advisory, error) { + advisory_names, err := s.advisoryRepository.ReadName(ctx, nil) + if err != nil { + return nil, err + } + return advisory_names, nil +} + +func (s *AdvisoryService) UpdateName(ctx context.Context, id uuid.UUID, name string) error { + return s.advisoryRepository.UpdateName(ctx, nil, id, name) +} + +func (s *AdvisoryService) DeleteName(ctx context.Context, id uuid.UUID) error { + return s.advisoryRepository.DeleteName(ctx, nil, id) +} diff --git a/shared/common_interfaces.go b/shared/common_interfaces.go index ed2ade03b..02332c7b0 100644 --- a/shared/common_interfaces.go +++ b/shared/common_interfaces.go @@ -763,10 +763,16 @@ type RBACProvider interface { type AdvisoryService interface { CreateName(ctx context.Context, name string) error + ReadName(ctx context.Context) ([]models.Advisory, error) + UpdateName(ctx context.Context, id uuid.UUID, name string) error + DeleteName(ctx context.Context, id uuid.UUID) error } type AdvisoryRepository interface { CreateName(ctx context.Context, tx DB, name string) error + ReadName(ctx context.Context, tx DB) ([]models.Advisory, error) + UpdateName(ctx context.Context, tx DB, id uuid.UUID, name string) error + DeleteName(ctx context.Context, tx DB, id uuid.UUID) error } type RBACMiddleware = func(obj Object, act Action) echo.MiddlewareFunc From eaa2c120cc9aadf51608371c1d78167b6de8d3ff Mon Sep 17 00:00:00 2001 From: Julian Kepka Date: Thu, 25 Jun 2026 09:50:23 +0200 Subject: [PATCH 3/4] feat: create method working for real inputs --- controllers/advisory_controller.go | 84 +++++++++---------- .../20260623101120_add_advisory_table.up.sql | 24 +++++- database/models/advisory_model.go | 19 ++++- database/repositories/advisory_repository.go | 10 +-- dtos/advisory_dto.go | 31 ++++++- router/advisory_router.go | 8 +- services/advisory_service.go | 8 +- shared/common_interfaces.go | 4 +- transformer/advisory_transformer.go | 31 +++++++ 9 files changed, 155 insertions(+), 64 deletions(-) create mode 100644 transformer/advisory_transformer.go diff --git a/controllers/advisory_controller.go b/controllers/advisory_controller.go index fdb77d54d..8ae56bd52 100644 --- a/controllers/advisory_controller.go +++ b/controllers/advisory_controller.go @@ -16,9 +16,9 @@ package controllers import ( - "github.com/google/uuid" "github.com/l3montree-dev/devguard/dtos" "github.com/l3montree-dev/devguard/shared" + "github.com/l3montree-dev/devguard/transformer" "github.com/labstack/echo/v4" ) @@ -32,66 +32,66 @@ func NewAdvisoryController(advisoryService shared.AdvisoryService) *AdvisoryCont } } -func (controller *AdvisoryController) CreateName(ctx shared.Context) error { - var req dtos.AdvisoryCreateName +func (controller *AdvisoryController) Create(ctx shared.Context) error { + var req dtos.AdvisoryCreate if err := ctx.Bind(&req); err != nil { return echo.NewHTTPError(400, "unable to process request").WithInternal(err) } - newName := req.Name + newAdvisory := transformer.AdvisoryCreateRequestToModel(req) - err := controller.advisoryService.CreateName(ctx.Request().Context(), newName) + err := controller.advisoryService.Create(ctx.Request().Context(), &newAdvisory) if err != nil { return echo.NewHTTPError(409, "could not set name").WithInternal(err) } - return ctx.JSON(200, newName) + return ctx.NoContent(200) } -func (controller *AdvisoryController) ReadName(ctx shared.Context) error { - advisory_names, err := controller.advisoryService.ReadName(ctx.Request().Context()) - if err != nil { - return echo.NewHTTPError(500, "could not get any name").WithInternal(err) - } - return ctx.JSON(200, advisory_names) -} +// func (controller *advisoryController) ReadName(ctx shared.Context) error { +// advisoryNames, err := controller.advisoryService.ReadName(ctx.Request().Context()) +// if err != nil { +// return echo.NewHTTPError(500, "could not get any name").WithInternal(err) +// } +// return ctx.JSON(200, advisoryNames) +// } -func (controller *AdvisoryController) UpdateName(ctx shared.Context) error { - var req dtos.AdvisoryCreateName // Change to own struct - if err := ctx.Bind(&req); err != nil { - return echo.NewHTTPError(400, "unable to process request").WithInternal(err) - } +// func (controller *advisoryController) UpdateName(ctx shared.Context) error { +// var req dtos.AdvisoryUpdate // Change to own struct +// if err := ctx.Bind(&req); err != nil { +// return echo.NewHTTPError(400, "unable to process request").WithInternal(err) +// } - updateName := req.Name +// updateName := req.Name - advisoryID := ctx.Param("id") - parsedID, err := uuid.Parse(advisoryID) - if err != nil { - return echo.NewHTTPError(400, "invalid uuid provided") - } +// advisoryID := ctx.Param("id") +// parsedID, err := uuid.Parse(advisoryID) +// if err != nil { +// return echo.NewHTTPError(400, "invalid uuid provided") +// } - err = controller.advisoryService.UpdateName(ctx.Request().Context(), parsedID, updateName) +// err = controller.advisoryService.UpdateName(ctx.Request().Context(), parsedID, updateName) - if err != nil { - return echo.NewHTTPError(409, "could not update name").WithInternal(err) - } +// if err != nil { +// return echo.NewHTTPError(409, "could not update name").WithInternal(err) +// } - return ctx.JSON(200, updateName) -} +// return ctx.JSON(200, updateName) +// } -func (controller *AdvisoryController) DeleteName(ctx shared.Context) error { - advisoryID := ctx.Param("id") - parsedID, err := uuid.Parse(advisoryID) - if err != nil { - return echo.NewHTTPError(400, "invalid uuid provided") - } +// func (controller *advisoryController) DeleteName(ctx shared.Context) error { +// advisoryID := ctx.Param("id") +// parsedID, err := uuid.Parse(advisoryID) +// if err != nil { +// return echo.NewHTTPError(400, "invalid uuid provided") +// } - err = controller.advisoryService.DeleteName(ctx.Request().Context(), parsedID) +// err = controller.advisoryService.DeleteName(ctx.Request().Context(), parsedID) - if err != nil { - return echo.NewHTTPError(409, "could not remove name").WithInternal(err) - } +// if err != nil { +// return echo.NewHTTPError(409, "could not remove name").WithInternal(err) +// } - return ctx.NoContent(200) -} +// return ctx.NoContent(200) +// } diff --git a/database/migrations/20260623101120_add_advisory_table.up.sql b/database/migrations/20260623101120_add_advisory_table.up.sql index 8d361fea0..2c2af7827 100644 --- a/database/migrations/20260623101120_add_advisory_table.up.sql +++ b/database/migrations/20260623101120_add_advisory_table.up.sql @@ -2,5 +2,25 @@ CREATE TABLE public.advisories ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), created_at TIMESTAMP NOT NULL DEFAULT NOW(), updated_at TIMESTAMP NOT NULL DEFAULT NOW(), - advisory_name TEXT NOT NULL -) \ No newline at end of file + title TEXT NOT NULL, + description TEXT NOT NULL, + severity TEXT, + vector_string TEXT +); + +CREATE TABLE public.affected_packages ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW(), + ecosystem TEXT NOT NULL, + package_name TEXT, + semver_introduced public.semver, + semver_fixed public.semver +); + +CREATE TABLE public.advisories_affected_packages ( + advisory_id UUID NOT NULL REFERENCES public.advisories(id) ON DELETE CASCADE, + affected_package_id UUID NOT NULL REFERENCES public.affected_packages(id) ON DELETE CASCADE, + CONSTRAINT advisories_affected_packages_pkey PRIMARY KEY (advisory_id, affected_package_id) +); + diff --git a/database/models/advisory_model.go b/database/models/advisory_model.go index 340021711..dca165d6e 100644 --- a/database/models/advisory_model.go +++ b/database/models/advisory_model.go @@ -2,9 +2,26 @@ package models type Advisory struct { Model - AdvisoryName string `json:"name" gorm:"type:text;column:advisory_name"` + Title string `json:"title" gorm:"type:text;column:title"` + Description string `json:"description" gorm:"type:text;column:description"` + AffectedPackages []AffectedPackage `json:"affectedPackages" gorm:"many2many:advisories_affected_packages;constraint:OnDelete:CASCADE"` + Severity string `json:"severity" gorm:"type:text;column:severity"` + VectorString string `json:"vectorstring" gorm:"type:text;column:vector_string"` +} + +type AffectedPackage struct { + Model + Ecosystem string `json:"ecosystem" gorm:"type:text;column:ecosystem"` + PackageName string `json:"packagename" gorm:"type:text;column:package_name"` + SemverIntroduced *string `json:"semverStart" gorm:"type:semver;index"` + SemverFixed *string `json:"semverEnd" gorm:"type:semver;index"` + Advisory []Advisory `json:"advisory" gorm:"many2many:advisories_affected_packages;constraint:OnDelete:CASCADE"` } func (m Advisory) TableName() string { return "advisories" } + +func (m AffectedPackage) TableName() string { + return "affected_packages" +} diff --git a/database/repositories/advisory_repository.go b/database/repositories/advisory_repository.go index 2a36c9dbb..d3a437001 100644 --- a/database/repositories/advisory_repository.go +++ b/database/repositories/advisory_repository.go @@ -24,8 +24,8 @@ func NewAdvisoryRepository(db *gorm.DB) *AdvisoryRepository { var _ shared.AdvisoryRepository = (*AdvisoryRepository)(nil) -func (advisoryRepository *AdvisoryRepository) CreateName(ctx context.Context, tx *gorm.DB, name string) error { - err := advisoryRepository.GetDB(ctx, tx).Create(&models.Advisory{AdvisoryName: name}).Error +func (advisoryRepository *AdvisoryRepository) Create(ctx context.Context, tx *gorm.DB, advisory *models.Advisory) error { + err := advisoryRepository.GetDB(ctx, tx).Create(advisory).Error if err != nil { return err } @@ -33,13 +33,13 @@ func (advisoryRepository *AdvisoryRepository) CreateName(ctx context.Context, tx } func (advisoryRepository *AdvisoryRepository) ReadName(ctx context.Context, tx *gorm.DB) ([]models.Advisory, error) { - advisory_names := []models.Advisory{} + advisoryNames := []models.Advisory{} db := advisoryRepository.db.WithContext(ctx) if tx != nil { db = tx } - err := db.Raw(`SELECT * FROM advisories;`).Find(&advisory_names).Error - return advisory_names, err + err := db.Raw(`SELECT * FROM advisories;`).Find(&advisoryNames).Error + return advisoryNames, err } func (advisoryRepository *AdvisoryRepository) UpdateName(ctx context.Context, tx *gorm.DB, id uuid.UUID, name string) error { diff --git a/dtos/advisory_dto.go b/dtos/advisory_dto.go index e12a21fdd..6fb325bde 100644 --- a/dtos/advisory_dto.go +++ b/dtos/advisory_dto.go @@ -17,11 +17,34 @@ package dtos import "github.com/google/uuid" -type AdvisoryCreateName struct { - Name string `json:"name" validate:"required"` +type AdvisoryCreate struct { + Title string `json:"title" validate:"required"` + Description string `json:"description" validate:"required"` + AffectedPackages []AffectedPackage `json:"affectedPackages"` + Severity string `json:"severity"` + VectorString string `json:"vectorString"` +} +type AdvisoryUpdate struct { + Title *string `json:"title"` + Description *string `json:"description"` + AffectedPackages []AffectedPackage `json:"affectedPackages"` + Severity *string `json:"severity"` + VectorString *string `json:"vectorString"` } type AdvisoryDTO struct { - ID uuid.UUID `json:"id"` - Name string `json:"name"` + ID uuid.UUID `json:"id"` + Title string `json:"title" validate:"required"` + Description string `json:"description" validate:"required"` + AffectedPackages []AffectedPackage `json:"affectedPackages"` + Severity string `json:"severity"` + VectorString string `json:"vectorString"` +} + +type AffectedPackage struct { + ID uuid.UUID `json:"id,omitempty"` + Ecosystem string `json:"ecosystem"` + PackageName string `json:"packageName"` + SemverIntroduced *string `json:"semverStart"` + SemverFixed *string `json:"semverEnd"` } diff --git a/router/advisory_router.go b/router/advisory_router.go index 84fbf57af..f190853c0 100644 --- a/router/advisory_router.go +++ b/router/advisory_router.go @@ -31,10 +31,10 @@ func NewAdvisoryRouter( advisoryController *controllers.AdvisoryController, ) AdvisoryRouter { advisoryRouter := assetVersionGroup.Group.Group("/advisory") - advisoryRouter.POST("/", advisoryController.CreateName) - advisoryRouter.GET("/", advisoryController.ReadName) - advisoryRouter.PATCH("/:id/", advisoryController.UpdateName) - advisoryRouter.DELETE("/:id/", advisoryController.DeleteName) + advisoryRouter.POST("/", advisoryController.Create) + // advisoryRouter.GET("/", advisoryController.Read) + // advisoryRouter.PATCH("/:id/", advisoryController.Update) + // advisoryRouter.DELETE("/:id/", advisoryController.Delete) return AdvisoryRouter{Group: advisoryRouter} } diff --git a/services/advisory_service.go b/services/advisory_service.go index 1f27cda0f..561cc58fe 100644 --- a/services/advisory_service.go +++ b/services/advisory_service.go @@ -20,16 +20,16 @@ func NewAdvisoryService(advisoryRepository shared.AdvisoryRepository) *AdvisoryS var _ shared.AdvisoryService = (*AdvisoryService)(nil) -func (s *AdvisoryService) CreateName(ctx context.Context, name string) error { - return s.advisoryRepository.CreateName(ctx, nil, name) +func (s *AdvisoryService) Create(ctx context.Context, advisory *models.Advisory) error { + return s.advisoryRepository.Create(ctx, nil, advisory) } func (s *AdvisoryService) ReadName(ctx context.Context) ([]models.Advisory, error) { - advisory_names, err := s.advisoryRepository.ReadName(ctx, nil) + advisoryNames, err := s.advisoryRepository.ReadName(ctx, nil) if err != nil { return nil, err } - return advisory_names, nil + return advisoryNames, nil } func (s *AdvisoryService) UpdateName(ctx context.Context, id uuid.UUID, name string) error { diff --git a/shared/common_interfaces.go b/shared/common_interfaces.go index 02332c7b0..06d00073a 100644 --- a/shared/common_interfaces.go +++ b/shared/common_interfaces.go @@ -762,14 +762,14 @@ type RBACProvider interface { } type AdvisoryService interface { - CreateName(ctx context.Context, name string) error + Create(ctx context.Context, advisory *models.Advisory) error ReadName(ctx context.Context) ([]models.Advisory, error) UpdateName(ctx context.Context, id uuid.UUID, name string) error DeleteName(ctx context.Context, id uuid.UUID) error } type AdvisoryRepository interface { - CreateName(ctx context.Context, tx DB, name string) error + Create(ctx context.Context, tx DB, advisory *models.Advisory) error ReadName(ctx context.Context, tx DB) ([]models.Advisory, error) UpdateName(ctx context.Context, tx DB, id uuid.UUID, name string) error DeleteName(ctx context.Context, tx DB, id uuid.UUID) error diff --git a/transformer/advisory_transformer.go b/transformer/advisory_transformer.go new file mode 100644 index 000000000..762088451 --- /dev/null +++ b/transformer/advisory_transformer.go @@ -0,0 +1,31 @@ +package transformer + +import ( + "github.com/l3montree-dev/devguard/database/models" + "github.com/l3montree-dev/devguard/dtos" +) + +func AdvisoryCreateRequestToModel(c dtos.AdvisoryCreate) models.Advisory { + + components := make([]models.AffectedPackage, len(c.AffectedPackages)) + for i, asset := range c.AffectedPackages { + components[i] = AffectedPackageToModel(asset) + } + + return models.Advisory{ + Title: c.Title, + Description: c.Description, + AffectedPackages: components, + Severity: c.Severity, + VectorString: c.VectorString, + } +} + +func AffectedPackageToModel(c dtos.AffectedPackage) models.AffectedPackage { + return models.AffectedPackage{ + Ecosystem: c.Ecosystem, + PackageName: c.PackageName, + SemverIntroduced: c.SemverIntroduced, + SemverFixed: c.SemverFixed, + } +} From dd7b87c68e047ff215e803b67c81503a880bed54 Mon Sep 17 00:00:00 2001 From: Julian Kepka Date: Fri, 26 Jun 2026 15:36:42 +0200 Subject: [PATCH 4/4] feat: added crud methods --- controllers/advisory_controller.go | 97 ++++++++++++------- .../20260623101120_add_advisory_table.up.sql | 3 +- database/models/advisory_model.go | 6 +- database/repositories/advisory_repository.go | 34 ++++--- dtos/advisory_dto.go | 3 + router/advisory_router.go | 7 +- services/advisory_service.go | 18 ++-- shared/common_interfaces.go | 14 +-- transformer/advisory_transformer.go | 28 ++++++ 9 files changed, 141 insertions(+), 69 deletions(-) diff --git a/controllers/advisory_controller.go b/controllers/advisory_controller.go index 8ae56bd52..b1f23d958 100644 --- a/controllers/advisory_controller.go +++ b/controllers/advisory_controller.go @@ -16,6 +16,7 @@ package controllers import ( + "github.com/google/uuid" "github.com/l3montree-dev/devguard/dtos" "github.com/l3montree-dev/devguard/shared" "github.com/l3montree-dev/devguard/transformer" @@ -43,55 +44,77 @@ func (controller *AdvisoryController) Create(ctx shared.Context) error { err := controller.advisoryService.Create(ctx.Request().Context(), &newAdvisory) if err != nil { - return echo.NewHTTPError(409, "could not set name").WithInternal(err) + return echo.NewHTTPError(409, "could not set advisory").WithInternal(err) } return ctx.NoContent(200) } -// func (controller *advisoryController) ReadName(ctx shared.Context) error { -// advisoryNames, err := controller.advisoryService.ReadName(ctx.Request().Context()) -// if err != nil { -// return echo.NewHTTPError(500, "could not get any name").WithInternal(err) -// } -// return ctx.JSON(200, advisoryNames) -// } +func (controller *AdvisoryController) ReadAll(ctx shared.Context) error { + asset := shared.GetAsset(ctx) + advisories, err := controller.advisoryService.ReadAll(ctx.Request().Context(), asset.ID) + if err != nil { + return echo.NewHTTPError(500, "could not get any data").WithInternal(err) + } + return ctx.JSON(200, advisories) +} + +func (controller *AdvisoryController) ReadAdvisory(ctx shared.Context) error { + advisoryID := ctx.Param("id") + parsedID, err := uuid.Parse(advisoryID) + if err != nil { + return echo.NewHTTPError(400, "invalid uuid provided") + } + + advisory, err := controller.advisoryService.ReadAdvisory(ctx.Request().Context(), parsedID) + + if err != nil { + return echo.NewHTTPError(409, "could not get any data").WithInternal(err) + } + + return ctx.JSON(200, advisory) +} + +func (controller *AdvisoryController) Update(ctx shared.Context) error { + var req dtos.AdvisoryUpdate + if err := ctx.Bind(&req); err != nil { + return echo.NewHTTPError(400, "unable to process request").WithInternal(err) + } -// func (controller *advisoryController) UpdateName(ctx shared.Context) error { -// var req dtos.AdvisoryUpdate // Change to own struct -// if err := ctx.Bind(&req); err != nil { -// return echo.NewHTTPError(400, "unable to process request").WithInternal(err) -// } + advisoryID := ctx.Param("id") + parsedID, err := uuid.Parse(advisoryID) + if err != nil { + return echo.NewHTTPError(400, "invalid uuid provided") + } -// updateName := req.Name + advisory, err := controller.advisoryService.ReadAdvisory(ctx.Request().Context(), parsedID) + if err != nil { + return echo.NewHTTPError(404, "advisory not found").WithInternal(err) + } -// advisoryID := ctx.Param("id") -// parsedID, err := uuid.Parse(advisoryID) -// if err != nil { -// return echo.NewHTTPError(400, "invalid uuid provided") -// } + advisory = transformer.AdvisoryUpdateRequestToModel(req, advisory) -// err = controller.advisoryService.UpdateName(ctx.Request().Context(), parsedID, updateName) + err = controller.advisoryService.Update(ctx.Request().Context(), parsedID, &advisory) -// if err != nil { -// return echo.NewHTTPError(409, "could not update name").WithInternal(err) -// } + if err != nil { + return echo.NewHTTPError(409, "could not update advisory").WithInternal(err) + } -// return ctx.JSON(200, updateName) -// } + return ctx.NoContent(200) +} -// func (controller *advisoryController) DeleteName(ctx shared.Context) error { -// advisoryID := ctx.Param("id") -// parsedID, err := uuid.Parse(advisoryID) -// if err != nil { -// return echo.NewHTTPError(400, "invalid uuid provided") -// } +func (controller *AdvisoryController) Delete(ctx shared.Context) error { + advisoryID := ctx.Param("id") + parsedID, err := uuid.Parse(advisoryID) + if err != nil { + return echo.NewHTTPError(400, "invalid uuid provided") + } -// err = controller.advisoryService.DeleteName(ctx.Request().Context(), parsedID) + err = controller.advisoryService.Delete(ctx.Request().Context(), parsedID) -// if err != nil { -// return echo.NewHTTPError(409, "could not remove name").WithInternal(err) -// } + if err != nil { + return echo.NewHTTPError(409, "could not remove name").WithInternal(err) + } -// return ctx.NoContent(200) -// } + return ctx.NoContent(200) +} diff --git a/database/migrations/20260623101120_add_advisory_table.up.sql b/database/migrations/20260623101120_add_advisory_table.up.sql index 2c2af7827..c38a342c5 100644 --- a/database/migrations/20260623101120_add_advisory_table.up.sql +++ b/database/migrations/20260623101120_add_advisory_table.up.sql @@ -5,7 +5,8 @@ CREATE TABLE public.advisories ( title TEXT NOT NULL, description TEXT NOT NULL, severity TEXT, - vector_string TEXT + vector_string TEXT, + asset_id UUID ); CREATE TABLE public.affected_packages ( diff --git a/database/models/advisory_model.go b/database/models/advisory_model.go index dca165d6e..9530fa877 100644 --- a/database/models/advisory_model.go +++ b/database/models/advisory_model.go @@ -1,14 +1,16 @@ package models +import "github.com/google/uuid" + type Advisory struct { Model Title string `json:"title" gorm:"type:text;column:title"` Description string `json:"description" gorm:"type:text;column:description"` - AffectedPackages []AffectedPackage `json:"affectedPackages" gorm:"many2many:advisories_affected_packages;constraint:OnDelete:CASCADE"` + AffectedPackages []AffectedPackage `json:"affectedPackages" gorm:"many2many:advisories_affected_packages;foreignKey:ID;joinForeignKey:advisory_id;References:ID;joinReferences:affected_package_id;constraint:OnDelete:CASCADE"` Severity string `json:"severity" gorm:"type:text;column:severity"` VectorString string `json:"vectorstring" gorm:"type:text;column:vector_string"` + AssetID uuid.UUID `json:"assetID" gorm:"type:uuid;column:asset_id"` } - type AffectedPackage struct { Model Ecosystem string `json:"ecosystem" gorm:"type:text;column:ecosystem"` diff --git a/database/repositories/advisory_repository.go b/database/repositories/advisory_repository.go index d3a437001..1e94ce3f4 100644 --- a/database/repositories/advisory_repository.go +++ b/database/repositories/advisory_repository.go @@ -32,28 +32,36 @@ func (advisoryRepository *AdvisoryRepository) Create(ctx context.Context, tx *go return nil } -func (advisoryRepository *AdvisoryRepository) ReadName(ctx context.Context, tx *gorm.DB) ([]models.Advisory, error) { - advisoryNames := []models.Advisory{} +func (advisoryRepository *AdvisoryRepository) ReadAll(ctx context.Context, tx *gorm.DB, assetID uuid.UUID) ([]models.Advisory, error) { + advisories := []models.Advisory{} db := advisoryRepository.db.WithContext(ctx) if tx != nil { db = tx } - err := db.Raw(`SELECT * FROM advisories;`).Find(&advisoryNames).Error - return advisoryNames, err + err := db.Preload("AffectedPackages").Where("asset_id = ?", assetID).Find(&advisories).Error + return advisories, err } -func (advisoryRepository *AdvisoryRepository) UpdateName(ctx context.Context, tx *gorm.DB, id uuid.UUID, name string) error { - err := advisoryRepository.GetDB(ctx, tx). - Model(&models.Advisory{Model: models.Model{ID: id}}). - Update("advisory_name", name).Error - if err != nil { - return err +func (advisoryRepository *AdvisoryRepository) ReadAdvisory(ctx context.Context, tx *gorm.DB, id uuid.UUID) (models.Advisory, error) { + advisory := models.Advisory{} + db := advisoryRepository.db.WithContext(ctx) + if tx != nil { + db = tx } - return nil + err := db.Preload("AffectedPackages").Where("id = ?", id).Find(&advisory).Error + return advisory, err } -func (advisoryRepository *AdvisoryRepository) DeleteName(ctx context.Context, tx *gorm.DB, id uuid.UUID) error { - err := advisoryRepository.GetDB(ctx, tx).Delete(&models.Advisory{Model: models.Model{ID: id}}).Error +func (advisoryRepository *AdvisoryRepository) Update(ctx context.Context, tx *gorm.DB, id uuid.UUID, advisory *models.Advisory) error { + return advisoryRepository.GetDB(ctx, tx).Session(&gorm.Session{FullSaveAssociations: true}).Save(advisory).Error +} + +func (advisoryRepository *AdvisoryRepository) Delete(ctx context.Context, tx *gorm.DB, id uuid.UUID) error { + db := advisoryRepository.db.WithContext(ctx) + if tx != nil { + db = tx + } + err := db.Preload("AffectedPackages").Delete(&models.Advisory{Model: models.Model{ID: id}}).Error if err != nil { return err } diff --git a/dtos/advisory_dto.go b/dtos/advisory_dto.go index 6fb325bde..d8dfdb797 100644 --- a/dtos/advisory_dto.go +++ b/dtos/advisory_dto.go @@ -23,6 +23,7 @@ type AdvisoryCreate struct { AffectedPackages []AffectedPackage `json:"affectedPackages"` Severity string `json:"severity"` VectorString string `json:"vectorString"` + AssetID uuid.UUID `json:"assetID"` } type AdvisoryUpdate struct { Title *string `json:"title"` @@ -30,6 +31,7 @@ type AdvisoryUpdate struct { AffectedPackages []AffectedPackage `json:"affectedPackages"` Severity *string `json:"severity"` VectorString *string `json:"vectorString"` + AssetID *uuid.UUID `json:"assetID"` } type AdvisoryDTO struct { @@ -39,6 +41,7 @@ type AdvisoryDTO struct { AffectedPackages []AffectedPackage `json:"affectedPackages"` Severity string `json:"severity"` VectorString string `json:"vectorString"` + AssetID uuid.UUID `json:"assetID"` } type AffectedPackage struct { diff --git a/router/advisory_router.go b/router/advisory_router.go index f190853c0..53bf6cce7 100644 --- a/router/advisory_router.go +++ b/router/advisory_router.go @@ -32,9 +32,10 @@ func NewAdvisoryRouter( ) AdvisoryRouter { advisoryRouter := assetVersionGroup.Group.Group("/advisory") advisoryRouter.POST("/", advisoryController.Create) - // advisoryRouter.GET("/", advisoryController.Read) - // advisoryRouter.PATCH("/:id/", advisoryController.Update) - // advisoryRouter.DELETE("/:id/", advisoryController.Delete) + advisoryRouter.GET("/", advisoryController.ReadAll) + advisoryRouter.GET("/:id/", advisoryController.ReadAdvisory) + advisoryRouter.PATCH("/:id/", advisoryController.Update) + advisoryRouter.DELETE("/:id/", advisoryController.Delete) return AdvisoryRouter{Group: advisoryRouter} } diff --git a/services/advisory_service.go b/services/advisory_service.go index 561cc58fe..0e12b6f5e 100644 --- a/services/advisory_service.go +++ b/services/advisory_service.go @@ -24,18 +24,22 @@ func (s *AdvisoryService) Create(ctx context.Context, advisory *models.Advisory) return s.advisoryRepository.Create(ctx, nil, advisory) } -func (s *AdvisoryService) ReadName(ctx context.Context) ([]models.Advisory, error) { - advisoryNames, err := s.advisoryRepository.ReadName(ctx, nil) +func (s *AdvisoryService) ReadAll(ctx context.Context, assetID uuid.UUID) ([]models.Advisory, error) { + advisories, err := s.advisoryRepository.ReadAll(ctx, nil, assetID) if err != nil { return nil, err } - return advisoryNames, nil + return advisories, nil } -func (s *AdvisoryService) UpdateName(ctx context.Context, id uuid.UUID, name string) error { - return s.advisoryRepository.UpdateName(ctx, nil, id, name) +func (s *AdvisoryService) ReadAdvisory(ctx context.Context, id uuid.UUID) (models.Advisory, error) { + return s.advisoryRepository.ReadAdvisory(ctx, nil, id) } -func (s *AdvisoryService) DeleteName(ctx context.Context, id uuid.UUID) error { - return s.advisoryRepository.DeleteName(ctx, nil, id) +func (s *AdvisoryService) Update(ctx context.Context, id uuid.UUID, advisory *models.Advisory) error { + return s.advisoryRepository.Update(ctx, nil, id, advisory) +} + +func (s *AdvisoryService) Delete(ctx context.Context, id uuid.UUID) error { + return s.advisoryRepository.Delete(ctx, nil, id) } diff --git a/shared/common_interfaces.go b/shared/common_interfaces.go index 06d00073a..26625aaae 100644 --- a/shared/common_interfaces.go +++ b/shared/common_interfaces.go @@ -763,16 +763,18 @@ type RBACProvider interface { type AdvisoryService interface { Create(ctx context.Context, advisory *models.Advisory) error - ReadName(ctx context.Context) ([]models.Advisory, error) - UpdateName(ctx context.Context, id uuid.UUID, name string) error - DeleteName(ctx context.Context, id uuid.UUID) error + ReadAll(ctx context.Context, assetID uuid.UUID) ([]models.Advisory, error) + ReadAdvisory(ctx context.Context, id uuid.UUID) (models.Advisory, error) + Update(ctx context.Context, id uuid.UUID, advisory *models.Advisory) error + Delete(ctx context.Context, id uuid.UUID) error } type AdvisoryRepository interface { Create(ctx context.Context, tx DB, advisory *models.Advisory) error - ReadName(ctx context.Context, tx DB) ([]models.Advisory, error) - UpdateName(ctx context.Context, tx DB, id uuid.UUID, name string) error - DeleteName(ctx context.Context, tx DB, id uuid.UUID) error + ReadAll(ctx context.Context, tx DB, assetID uuid.UUID) ([]models.Advisory, error) + ReadAdvisory(ctx context.Context, tx DB, id uuid.UUID) (models.Advisory, error) + Update(ctx context.Context, tx DB, id uuid.UUID, advisory *models.Advisory) error + Delete(ctx context.Context, tx DB, id uuid.UUID) error } type RBACMiddleware = func(obj Object, act Action) echo.MiddlewareFunc diff --git a/transformer/advisory_transformer.go b/transformer/advisory_transformer.go index 762088451..e2d6cf867 100644 --- a/transformer/advisory_transformer.go +++ b/transformer/advisory_transformer.go @@ -18,11 +18,39 @@ func AdvisoryCreateRequestToModel(c dtos.AdvisoryCreate) models.Advisory { AffectedPackages: components, Severity: c.Severity, VectorString: c.VectorString, + AssetID: c.AssetID, } } +func AdvisoryUpdateRequestToModel(c dtos.AdvisoryUpdate, advisory models.Advisory) models.Advisory { + if c.Title != nil { + advisory.Title = *c.Title + } + if c.Description != nil { + advisory.Description = *c.Description + } + if c.Severity != nil { + advisory.Severity = *c.Severity + } + if c.VectorString != nil { + advisory.VectorString = *c.VectorString + } + if c.AffectedPackages != nil { + components := make([]models.AffectedPackage, len(c.AffectedPackages)) + for i, asset := range c.AffectedPackages { + components[i] = AffectedPackageToModel(asset) + } + advisory.AffectedPackages = components + } + if c.AssetID != nil { + advisory.AssetID = *c.AssetID + } + return advisory +} + func AffectedPackageToModel(c dtos.AffectedPackage) models.AffectedPackage { return models.AffectedPackage{ + Model: models.Model{ID: c.ID}, Ecosystem: c.Ecosystem, PackageName: c.PackageName, SemverIntroduced: c.SemverIntroduced,