From b14ca243c3d91c76c6933cebac451c3227f01fa1 Mon Sep 17 00:00:00 2001 From: Julian Kepka Date: Tue, 23 Jun 2026 16:13:10 +0200 Subject: [PATCH 1/6] 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/6] 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/6] 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/6] 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, From 444c71fcce545d7e134aceae66c6c8ffd93d8c28 Mon Sep 17 00:00:00 2001 From: Julian Kepka Date: Mon, 29 Jun 2026 11:41:50 +0200 Subject: [PATCH 5/6] fix: ID from context and better db find --- controllers/advisory_controller.go | 19 +++++++++++++++++-- database/repositories/advisory_repository.go | 2 +- router/advisory_router.go | 2 -- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/controllers/advisory_controller.go b/controllers/advisory_controller.go index b1f23d958..a94637248 100644 --- a/controllers/advisory_controller.go +++ b/controllers/advisory_controller.go @@ -16,11 +16,14 @@ package controllers import ( + "errors" + "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" + "gorm.io/gorm" ) type AdvisoryController struct { @@ -39,7 +42,12 @@ func (controller *AdvisoryController) Create(ctx shared.Context) error { return echo.NewHTTPError(400, "unable to process request").WithInternal(err) } + if err := shared.V.Struct(req); err != nil { + return echo.NewHTTPError(400, "invalid request").WithInternal(err) + } + newAdvisory := transformer.AdvisoryCreateRequestToModel(req) + newAdvisory.AssetID = shared.GetAsset(ctx).ID err := controller.advisoryService.Create(ctx.Request().Context(), &newAdvisory) @@ -69,7 +77,10 @@ func (controller *AdvisoryController) ReadAdvisory(ctx shared.Context) error { advisory, err := controller.advisoryService.ReadAdvisory(ctx.Request().Context(), parsedID) if err != nil { - return echo.NewHTTPError(409, "could not get any data").WithInternal(err) + if errors.Is(err, gorm.ErrRecordNotFound) { + return echo.NewHTTPError(404, "advisory not found").WithInternal(err) + } + return echo.NewHTTPError(500, "could not get any data").WithInternal(err) } return ctx.JSON(200, advisory) @@ -89,10 +100,14 @@ func (controller *AdvisoryController) Update(ctx shared.Context) error { advisory, err := controller.advisoryService.ReadAdvisory(ctx.Request().Context(), parsedID) if err != nil { - return echo.NewHTTPError(404, "advisory not found").WithInternal(err) + if errors.Is(err, gorm.ErrRecordNotFound) { + return echo.NewHTTPError(404, "advisory not found").WithInternal(err) + } + return echo.NewHTTPError(500, "could not get any data").WithInternal(err) } advisory = transformer.AdvisoryUpdateRequestToModel(req, advisory) + advisory.AssetID = shared.GetAsset(ctx).ID err = controller.advisoryService.Update(ctx.Request().Context(), parsedID, &advisory) diff --git a/database/repositories/advisory_repository.go b/database/repositories/advisory_repository.go index 1e94ce3f4..c5687ed4f 100644 --- a/database/repositories/advisory_repository.go +++ b/database/repositories/advisory_repository.go @@ -48,7 +48,7 @@ func (advisoryRepository *AdvisoryRepository) ReadAdvisory(ctx context.Context, if tx != nil { db = tx } - err := db.Preload("AffectedPackages").Where("id = ?", id).Find(&advisory).Error + err := db.Preload("AffectedPackages").Where("id = ?", id).First(&advisory).Error return advisory, err } diff --git a/router/advisory_router.go b/router/advisory_router.go index 53bf6cce7..34166dec4 100644 --- a/router/advisory_router.go +++ b/router/advisory_router.go @@ -17,7 +17,6 @@ package router import ( "github.com/l3montree-dev/devguard/controllers" - "github.com/l3montree-dev/devguard/shared" "github.com/labstack/echo/v4" ) @@ -26,7 +25,6 @@ type AdvisoryRouter struct { } func NewAdvisoryRouter( - assetRepository shared.AssetRepository, assetVersionGroup AssetVersionRouter, advisoryController *controllers.AdvisoryController, ) AdvisoryRouter { From 1b79c8ed5ae212e77b20d0b4f4955d108d8f04fd Mon Sep 17 00:00:00 2001 From: Julian Kepka Date: Mon, 29 Jun 2026 17:36:37 +0200 Subject: [PATCH 6/6] fix: reworked comments --- controllers/advisory_controller.go | 39 +++++++++-------- .../20260623101120_add_advisory_table.up.sql | 7 +-- database/models/advisory_model.go | 11 ++++- database/repositories/advisory_repository.go | 43 ++++++++++--------- dtos/advisory_dto.go | 4 +- router/advisory_router.go | 12 ++++-- services/advisory_service.go | 24 +++++------ shared/common_interfaces.go | 18 ++++---- transformer/advisory_transformer.go | 4 ++ 9 files changed, 91 insertions(+), 71 deletions(-) diff --git a/controllers/advisory_controller.go b/controllers/advisory_controller.go index a94637248..13fc00bc7 100644 --- a/controllers/advisory_controller.go +++ b/controllers/advisory_controller.go @@ -17,8 +17,8 @@ package controllers import ( "errors" + "strconv" - "github.com/google/uuid" "github.com/l3montree-dev/devguard/dtos" "github.com/l3montree-dev/devguard/shared" "github.com/l3montree-dev/devguard/transformer" @@ -39,20 +39,20 @@ func NewAdvisoryController(advisoryService shared.AdvisoryService) *AdvisoryCont 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) + return echo.NewHTTPError(400, "unable to process request") } if err := shared.V.Struct(req); err != nil { - return echo.NewHTTPError(400, "invalid request").WithInternal(err) + return echo.NewHTTPError(400, err.Error()) } newAdvisory := transformer.AdvisoryCreateRequestToModel(req) newAdvisory.AssetID = shared.GetAsset(ctx).ID - err := controller.advisoryService.Create(ctx.Request().Context(), &newAdvisory) + err := controller.advisoryService.Create(ctx.Request().Context(), nil, &newAdvisory) if err != nil { - return echo.NewHTTPError(409, "could not set advisory").WithInternal(err) + return echo.NewHTTPError(500, "could not create advisory").WithInternal(err) } return ctx.NoContent(200) @@ -60,7 +60,8 @@ func (controller *AdvisoryController) Create(ctx shared.Context) error { func (controller *AdvisoryController) ReadAll(ctx shared.Context) error { asset := shared.GetAsset(ctx) - advisories, err := controller.advisoryService.ReadAll(ctx.Request().Context(), asset.ID) + visibility := ctx.QueryParam("visibility") + advisories, err := controller.advisoryService.ReadAll(ctx.Request().Context(), nil, asset.ID, visibility, shared.GetPageInfo(ctx)) if err != nil { return echo.NewHTTPError(500, "could not get any data").WithInternal(err) } @@ -69,12 +70,12 @@ func (controller *AdvisoryController) ReadAll(ctx shared.Context) error { func (controller *AdvisoryController) ReadAdvisory(ctx shared.Context) error { advisoryID := ctx.Param("id") - parsedID, err := uuid.Parse(advisoryID) + parsedID, err := strconv.ParseInt(advisoryID, 10, 64) if err != nil { - return echo.NewHTTPError(400, "invalid uuid provided") + return echo.NewHTTPError(400, "invalid id provided") } - advisory, err := controller.advisoryService.ReadAdvisory(ctx.Request().Context(), parsedID) + advisory, err := controller.advisoryService.ReadAdvisory(ctx.Request().Context(), nil, parsedID) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { @@ -89,16 +90,16 @@ func (controller *AdvisoryController) ReadAdvisory(ctx shared.Context) error { 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) + return echo.NewHTTPError(400, "unable to process request") } advisoryID := ctx.Param("id") - parsedID, err := uuid.Parse(advisoryID) + parsedID, err := strconv.ParseInt(advisoryID, 10, 64) if err != nil { - return echo.NewHTTPError(400, "invalid uuid provided") + return echo.NewHTTPError(400, "invalid id provided") } - advisory, err := controller.advisoryService.ReadAdvisory(ctx.Request().Context(), parsedID) + advisory, err := controller.advisoryService.ReadAdvisory(ctx.Request().Context(), nil, parsedID) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return echo.NewHTTPError(404, "advisory not found").WithInternal(err) @@ -109,10 +110,10 @@ func (controller *AdvisoryController) Update(ctx shared.Context) error { advisory = transformer.AdvisoryUpdateRequestToModel(req, advisory) advisory.AssetID = shared.GetAsset(ctx).ID - err = controller.advisoryService.Update(ctx.Request().Context(), parsedID, &advisory) + err = controller.advisoryService.Update(ctx.Request().Context(), nil, parsedID, &advisory) if err != nil { - return echo.NewHTTPError(409, "could not update advisory").WithInternal(err) + return echo.NewHTTPError(500, "could not update advisory").WithInternal(err) } return ctx.NoContent(200) @@ -120,15 +121,15 @@ func (controller *AdvisoryController) Update(ctx shared.Context) error { func (controller *AdvisoryController) Delete(ctx shared.Context) error { advisoryID := ctx.Param("id") - parsedID, err := uuid.Parse(advisoryID) + parsedID, err := strconv.ParseInt(advisoryID, 10, 64) if err != nil { - return echo.NewHTTPError(400, "invalid uuid provided") + return echo.NewHTTPError(400, "invalid id provided") } - err = controller.advisoryService.Delete(ctx.Request().Context(), parsedID) + err = controller.advisoryService.Delete(ctx.Request().Context(), nil, parsedID) if err != nil { - return echo.NewHTTPError(409, "could not remove name").WithInternal(err) + return echo.NewHTTPError(500, "could not remove name").WithInternal(err) } 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 c38a342c5..12aab11e6 100644 --- a/database/migrations/20260623101120_add_advisory_table.up.sql +++ b/database/migrations/20260623101120_add_advisory_table.up.sql @@ -1,12 +1,13 @@ CREATE TABLE public.advisories ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, created_at TIMESTAMP NOT NULL DEFAULT NOW(), updated_at TIMESTAMP NOT NULL DEFAULT NOW(), title TEXT NOT NULL, description TEXT NOT NULL, severity TEXT, vector_string TEXT, - asset_id UUID + asset_id UUID, + visibility TEXT NOT NULL DEFAULT 'draft' ); CREATE TABLE public.affected_packages ( @@ -20,7 +21,7 @@ CREATE TABLE public.affected_packages ( ); CREATE TABLE public.advisories_affected_packages ( - advisory_id UUID NOT NULL REFERENCES public.advisories(id) ON DELETE CASCADE, + advisory_id BIGINT 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 9530fa877..78489de10 100644 --- a/database/models/advisory_model.go +++ b/database/models/advisory_model.go @@ -1,15 +1,22 @@ package models -import "github.com/google/uuid" +import ( + "time" + + "github.com/google/uuid" +) type Advisory struct { - Model + ID int64 `json:"id" gorm:"primaryKey;column:id"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` 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;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"` + Visibility string `json:"visibility" gorm:"type:text;column:visibility;default:draft"` } type AffectedPackage struct { Model diff --git a/database/repositories/advisory_repository.go b/database/repositories/advisory_repository.go index c5687ed4f..cab50bbae 100644 --- a/database/repositories/advisory_repository.go +++ b/database/repositories/advisory_repository.go @@ -12,13 +12,13 @@ import ( type AdvisoryRepository struct { db *gorm.DB - utils.Repository[uuid.UUID, models.Advisory, *gorm.DB] + utils.Repository[int64, models.Advisory, *gorm.DB] } func NewAdvisoryRepository(db *gorm.DB) *AdvisoryRepository { return &AdvisoryRepository{ db: db, - Repository: newGormRepository[uuid.UUID, models.Advisory](db), + Repository: newGormRepository[int64, models.Advisory](db), } } @@ -32,36 +32,39 @@ func (advisoryRepository *AdvisoryRepository) Create(ctx context.Context, tx *go return nil } -func (advisoryRepository *AdvisoryRepository) ReadAll(ctx context.Context, tx *gorm.DB, assetID uuid.UUID) ([]models.Advisory, error) { +func (advisoryRepository *AdvisoryRepository) ReadAll(ctx context.Context, tx *gorm.DB, assetID uuid.UUID, visibility string, pagnation shared.PageInfo) (shared.Paged[models.Advisory], error) { advisories := []models.Advisory{} - db := advisoryRepository.db.WithContext(ctx) - if tx != nil { - db = tx + db := advisoryRepository.GetDB(ctx, tx) + query := db.Model(&models.Advisory{}).Preload("AffectedPackages").Where("asset_id = ?", assetID) + if visibility != "" { + query = query.Where("visibility = ?", visibility) } - err := db.Preload("AffectedPackages").Where("asset_id = ?", assetID).Find(&advisories).Error - return advisories, err + + var count int64 + if err := query.Count(&count).Error; err != nil { + return shared.Paged[models.Advisory]{}, err + } + + if err := pagnation.ApplyOnDB(query).Find(&advisories).Error; err != nil { + return shared.Paged[models.Advisory]{}, err + } + + return shared.NewPaged(pagnation, count, advisories), nil } -func (advisoryRepository *AdvisoryRepository) ReadAdvisory(ctx context.Context, tx *gorm.DB, id uuid.UUID) (models.Advisory, error) { +func (advisoryRepository *AdvisoryRepository) ReadAdvisory(ctx context.Context, tx *gorm.DB, id int64) (models.Advisory, error) { advisory := models.Advisory{} - db := advisoryRepository.db.WithContext(ctx) - if tx != nil { - db = tx - } + db := advisoryRepository.GetDB(ctx, tx) err := db.Preload("AffectedPackages").Where("id = ?", id).First(&advisory).Error return advisory, err } -func (advisoryRepository *AdvisoryRepository) Update(ctx context.Context, tx *gorm.DB, id uuid.UUID, advisory *models.Advisory) error { +func (advisoryRepository *AdvisoryRepository) Update(ctx context.Context, tx *gorm.DB, id int64, 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 +func (advisoryRepository *AdvisoryRepository) Delete(ctx context.Context, tx *gorm.DB, id int64) error { + err := advisoryRepository.GetDB(ctx, tx).Delete(&models.Advisory{ID: id}).Error if err != nil { return err } diff --git a/dtos/advisory_dto.go b/dtos/advisory_dto.go index d8dfdb797..a71c1f7bf 100644 --- a/dtos/advisory_dto.go +++ b/dtos/advisory_dto.go @@ -32,16 +32,18 @@ type AdvisoryUpdate struct { Severity *string `json:"severity"` VectorString *string `json:"vectorString"` AssetID *uuid.UUID `json:"assetID"` + Visibility *string `json:"visibility"` } type AdvisoryDTO struct { - ID uuid.UUID `json:"id"` + ID int64 `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"` AssetID uuid.UUID `json:"assetID"` + Visibility string `json:"visibility"` } type AffectedPackage struct { diff --git a/router/advisory_router.go b/router/advisory_router.go index 34166dec4..868889a18 100644 --- a/router/advisory_router.go +++ b/router/advisory_router.go @@ -17,6 +17,8 @@ package router import ( "github.com/l3montree-dev/devguard/controllers" + "github.com/l3montree-dev/devguard/middlewares" + "github.com/l3montree-dev/devguard/shared" "github.com/labstack/echo/v4" ) @@ -27,13 +29,17 @@ type AdvisoryRouter struct { func NewAdvisoryRouter( assetVersionGroup AssetVersionRouter, advisoryController *controllers.AdvisoryController, + assetRepository shared.AssetRepository, ) AdvisoryRouter { + assetScopedRBAC := middlewares.AssetAccessControlFactory(assetRepository) advisoryRouter := assetVersionGroup.Group.Group("/advisory") - advisoryRouter.POST("/", advisoryController.Create) + advisoryRouter.GET("/", advisoryController.ReadAll) advisoryRouter.GET("/:id/", advisoryController.ReadAdvisory) - advisoryRouter.PATCH("/:id/", advisoryController.Update) - advisoryRouter.DELETE("/:id/", advisoryController.Delete) + + advisoryRouter.POST("/", advisoryController.Create, middlewares.NeededScope([]string{"manage"}), assetScopedRBAC(shared.ObjectAsset, shared.ActionUpdate)) + advisoryRouter.PATCH("/:id/", advisoryController.Update, middlewares.NeededScope([]string{"manage"}), assetScopedRBAC(shared.ObjectAsset, shared.ActionUpdate)) + advisoryRouter.DELETE("/:id/", advisoryController.Delete, middlewares.NeededScope([]string{"manage"}), assetScopedRBAC(shared.ObjectAsset, shared.ActionUpdate)) return AdvisoryRouter{Group: advisoryRouter} } diff --git a/services/advisory_service.go b/services/advisory_service.go index 0e12b6f5e..4b330d6ae 100644 --- a/services/advisory_service.go +++ b/services/advisory_service.go @@ -20,26 +20,22 @@ func NewAdvisoryService(advisoryRepository shared.AdvisoryRepository) *AdvisoryS var _ shared.AdvisoryService = (*AdvisoryService)(nil) -func (s *AdvisoryService) Create(ctx context.Context, advisory *models.Advisory) error { - return s.advisoryRepository.Create(ctx, nil, advisory) +func (s *AdvisoryService) Create(ctx context.Context, tx shared.DB, advisory *models.Advisory) error { + return s.advisoryRepository.Create(ctx, tx, advisory) } -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 advisories, nil +func (s *AdvisoryService) ReadAll(ctx context.Context, tx shared.DB, assetID uuid.UUID, visibility string, pagnation shared.PageInfo) (shared.Paged[models.Advisory], error) { + return s.advisoryRepository.ReadAll(ctx, tx, assetID, visibility, pagnation) } -func (s *AdvisoryService) ReadAdvisory(ctx context.Context, id uuid.UUID) (models.Advisory, error) { - return s.advisoryRepository.ReadAdvisory(ctx, nil, id) +func (s *AdvisoryService) ReadAdvisory(ctx context.Context, tx shared.DB, id int64) (models.Advisory, error) { + return s.advisoryRepository.ReadAdvisory(ctx, tx, 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) Update(ctx context.Context, tx shared.DB, id int64, advisory *models.Advisory) error { + return s.advisoryRepository.Update(ctx, tx, id, advisory) } -func (s *AdvisoryService) Delete(ctx context.Context, id uuid.UUID) error { - return s.advisoryRepository.Delete(ctx, nil, id) +func (s *AdvisoryService) Delete(ctx context.Context, tx shared.DB, id int64) error { + return s.advisoryRepository.Delete(ctx, tx, id) } diff --git a/shared/common_interfaces.go b/shared/common_interfaces.go index 26625aaae..36906963e 100644 --- a/shared/common_interfaces.go +++ b/shared/common_interfaces.go @@ -762,19 +762,19 @@ type RBACProvider interface { } type AdvisoryService interface { - Create(ctx context.Context, advisory *models.Advisory) 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 + Create(ctx context.Context, tx DB, advisory *models.Advisory) error + ReadAll(ctx context.Context, tx DB, assetID uuid.UUID, visibility string, pagnation PageInfo) (Paged[models.Advisory], error) + ReadAdvisory(ctx context.Context, tx DB, id int64) (models.Advisory, error) + Update(ctx context.Context, tx DB, id int64, advisory *models.Advisory) error + Delete(ctx context.Context, tx DB, id int64) error } type AdvisoryRepository interface { Create(ctx context.Context, tx DB, advisory *models.Advisory) 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 + ReadAll(ctx context.Context, tx DB, assetID uuid.UUID, visibility string, pagnation PageInfo) (Paged[models.Advisory], error) + ReadAdvisory(ctx context.Context, tx DB, id int64) (models.Advisory, error) + Update(ctx context.Context, tx DB, id int64, advisory *models.Advisory) error + Delete(ctx context.Context, tx DB, id int64) error } type RBACMiddleware = func(obj Object, act Action) echo.MiddlewareFunc diff --git a/transformer/advisory_transformer.go b/transformer/advisory_transformer.go index e2d6cf867..5650a2602 100644 --- a/transformer/advisory_transformer.go +++ b/transformer/advisory_transformer.go @@ -45,6 +45,10 @@ func AdvisoryUpdateRequestToModel(c dtos.AdvisoryUpdate, advisory models.Advisor if c.AssetID != nil { advisory.AssetID = *c.AssetID } + if c.Visibility != nil { + advisory.Visibility = *c.Visibility + } + return advisory }