Skip to content

Commit

Permalink
Merge pull request #433 from green-ecolution/feature/archive-vehicles
Browse files Browse the repository at this point in the history
feat: archive vehicles
  • Loading branch information
choffmann authored Feb 24, 2025
2 parents 3487e13 + 7f2bf19 commit e6794e8
Show file tree
Hide file tree
Showing 18 changed files with 300 additions and 65 deletions.
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ linters-settings:
- pkg: "github.com/sirupsen/logrus"
desc: logging is allowed only by logutils.Log
dupl:
threshold: 100
threshold: 150
funlen:
lines: -1 # the number of lines (code + empty lines) is not a right metric and leads to code without empty line or one-liner.
statements: 50
Expand Down
1 change: 1 addition & 0 deletions internal/entities/vehicle.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Vehicle struct {
ID int32
CreatedAt time.Time
UpdatedAt time.Time
ArchivedAt time.Time
NumberPlate string
Description string
WaterCapacity float64
Expand Down
1 change: 1 addition & 0 deletions internal/server/http/entities/mapper/vehicle.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
// goverter:converter
// goverter:extend github.com/green-ecolution/green-ecolution-backend/internal/utils:TimeToTime
// goverter:extend github.com/green-ecolution/green-ecolution-backend/internal/utils:TimeToTimePtr
// goverter:extend github.com/green-ecolution/green-ecolution-backend/internal/utils:TimeToPtrTime
// goverter:extend github.com/green-ecolution/green-ecolution-backend/internal/utils:MapKeyValueInterface
// goverter:extend MapVehicleStatus MapVehicleType MapVehicleStatusReq MapVehicleTypeReq MapDrivingLicense MapDrivingLicenseReq
type VehicleHTTPMapper interface {
Expand Down
1 change: 1 addition & 0 deletions internal/server/http/entities/vehicle.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type VehicleResponse struct {
ID int32 `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
ArchivedAt *time.Time `json:"archived_at,omitempty"`
NumberPlate string `json:"number_plate"`
Description string `json:"description"`
WaterCapacity float64 `json:"water_capacity"`
Expand Down
2 changes: 2 additions & 0 deletions internal/server/http/handler/v1/errorhandler/error_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ func HandleError(err error) error {
code = fiber.StatusUnauthorized
case service.InternalError:
code = fiber.StatusInternalServerError
case service.Conflict:
code = fiber.StatusConflict
default:
slog.Debug("missing service error code", "code", svcErr.Code)
}
Expand Down
62 changes: 60 additions & 2 deletions internal/server/http/handler/v1/vehicle/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ var (
// @Param limit query int false "Limit"
// @Param type query string false "Vehicle Type"
// @Param provider query string false "Provider"
// @Param archived query bool false "With archived vehicles"
// @Security Keycloak
func GetAllVehicles(svc service.VehicleService) fiber.Handler {
return func(c *fiber.Ctx) error {
Expand All @@ -42,8 +43,7 @@ func GetAllVehicles(svc service.VehicleService) fiber.Handler {
var totalCount int64
var err error

domainData, totalCount, err = svc.GetAll(ctx, strings.Clone(c.Query("provider")), strings.Clone(c.Query("type")))

domainData, totalCount, err = svc.GetAll(ctx, strings.Clone(c.Query("provider")), strings.Clone(c.Query("type")), c.QueryBool("archived", false))
if err != nil {
return errorhandler.HandleError(err)
}
Expand Down Expand Up @@ -199,6 +199,64 @@ func UpdateVehicle(svc service.VehicleService) fiber.Handler {
}
}

// @Summary Get archived vehicle
// @Description Get archived vehicle
// @Id get-archive-vehicle
// @Tags Vehicle
// @Produce json
// @Success 200 {object} []entities.VehicleResponse
// @Failure 400 {object} HTTPError
// @Failure 401 {object} HTTPError
// @Failure 403 {object} HTTPError
// @Failure 404 {object} HTTPError
// @Failure 500 {object} HTTPError
// @Router /v1/vehicle/archive [get]
// @Security Keycloak
func GetArchiveVehicles(svc service.VehicleService) fiber.Handler {
return func(c *fiber.Ctx) error {
ctx := c.Context()
v, err := svc.GetAllArchived(ctx)
if err != nil {
return errorhandler.HandleError(err)
}

return c.JSON(vehicleMapper.FromResponseList(v))
}
}

// @Summary Archive vehicle
// @Description Archive vehicle
// @Id archive-vehicle
// @Tags Vehicle
// @Produce json
// @Success 204
// @Failure 400 {object} HTTPError
// @Failure 401 {object} HTTPError
// @Failure 403 {object} HTTPError
// @Failure 404 {object} HTTPError
// @Failure 409 {object} HTTPError
// @Failure 500 {object} HTTPError
// @Router /v1/vehicle/archive/{id} [post]
// @Param id path int true "Vehicle ID"
// @Security Keycloak
func ArchiveVehicle(svc service.VehicleService) fiber.Handler {
return func(c *fiber.Ctx) error {
ctx := c.Context()
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
err := service.NewError(service.BadRequest, "invalid ID format")
return errorhandler.HandleError(err)
}

err = svc.Archive(ctx, int32(id))
if err != nil {
return errorhandler.HandleError(err)
}

return c.SendStatus(fiber.StatusNoContent)
}
}

// @Summary Delete vehicle
// @Description Delete vehicle
// @Id delete-vehicle
Expand Down
8 changes: 8 additions & 0 deletions internal/server/http/handler/v1/vehicle/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func TestGetAllVehicles(t *testing.T) {
mock.Anything,
"",
"",
false,
).Return(TestVehicles, int64(len(TestVehicles)), nil)

// when
Expand Down Expand Up @@ -67,6 +68,7 @@ func TestGetAllVehicles(t *testing.T) {
mock.Anything,
"",
"",
false,
).Return(TestVehicles, int64(len(TestVehicles)), nil)

// when
Expand Down Expand Up @@ -140,6 +142,7 @@ func TestGetAllVehicles(t *testing.T) {
mock.Anything,
"test-provider",
"",
false,
).Return(TestVehicles, int64(0), nil)

// when
Expand Down Expand Up @@ -174,6 +177,7 @@ func TestGetAllVehicles(t *testing.T) {
mock.Anything,
"",
"transporter",
false,
).Return([]*entities.Vehicle{TestVehicles[1]}, int64(1), nil)

// when
Expand Down Expand Up @@ -209,6 +213,7 @@ func TestGetAllVehicles(t *testing.T) {
mock.Anything,
"",
"transporter",
false,
).Return([]*entities.Vehicle{TestVehicles[1]}, int64(1), nil)

// when
Expand Down Expand Up @@ -248,6 +253,7 @@ func TestGetAllVehicles(t *testing.T) {
mock.Anything,
"",
"",
false,
).Return([]*entities.Vehicle{}, int64(0), nil)

// when
Expand Down Expand Up @@ -284,6 +290,7 @@ func TestGetAllVehicles(t *testing.T) {
mock.Anything,
"",
"",
false,
).Return(nil, int64(0), fiber.NewError(fiber.StatusInternalServerError, "service error"))

// when
Expand All @@ -309,6 +316,7 @@ func TestGetAllVehicles(t *testing.T) {
mock.Anything,
"",
"invalid",
false,
).Return(nil, int64(0), fiber.NewError(fiber.ErrBadRequest.Code, "service error"))

// when
Expand Down
4 changes: 3 additions & 1 deletion internal/server/http/handler/v1/vehicle/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import (

func RegisterRoutes(r fiber.Router, svc service.VehicleService) {
r.Get("/", GetAllVehicles(svc))
r.Get("/:id", GetVehicleByID(svc))
r.Get("/archive", GetArchiveVehicles(svc))
r.Get("/plate/:plate", GetVehicleByPlate(svc))
r.Get("/:id", GetVehicleByID(svc))
r.Post("/", CreateVehicle(svc))
r.Post("/archive/:id", ArchiveVehicle(svc))
r.Put("/:id", UpdateVehicle(svc))
r.Delete("/:id", DeleteVehicle(svc))
}
2 changes: 2 additions & 0 deletions internal/server/http/handler/v1/vehicle/routes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func TestRegisterRoutes(t *testing.T) {
mock.Anything,
"",
"",
false,
).Return(TestVehicles, int64(len(TestVehicles)), nil)

// when
Expand All @@ -51,6 +52,7 @@ func TestRegisterRoutes(t *testing.T) {
mock.Anything,
"",
"transporter",
false,
).Return(TestVehicles, int64(len(TestVehicles)), nil)

// when
Expand Down
53 changes: 46 additions & 7 deletions internal/service/domain/vehicle/vehicle.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,31 +24,51 @@ func NewVehicleService(vehicleRepository storage.VehicleRepository) service.Vehi
}
}

func (v *VehicleService) GetAll(ctx context.Context, provider, vehicleType string) ([]*entities.Vehicle, int64, error) {
func (v *VehicleService) GetAll(ctx context.Context, provider, vehicleType string, withArchived bool) ([]*entities.Vehicle, int64, error) {
log := logger.GetLogger(ctx)
var vehicles []*entities.Vehicle
var err error
var totalCount int64
var err error

if vehicleType != "" {
parsedVehicleType := entities.ParseVehicleType(vehicleType)
if parsedVehicleType == entities.VehicleTypeUnknown {
log.Debug("failed to parse correct vehicle type", "error", err, "vehicle_type", vehicleType)
return nil, 0, service.MapError(ctx, errors.Join(err, service.ErrValidation), service.ErrorLogValidation)
}
vehicles, totalCount, err = v.vehicleRepo.GetAllByType(ctx, provider, parsedVehicleType)

if withArchived {
vehicles, totalCount, err = v.vehicleRepo.GetAllByTypeWithArchived(ctx, provider, parsedVehicleType)
} else {
vehicles, totalCount, err = v.vehicleRepo.GetAllByType(ctx, provider, parsedVehicleType)
}
} else {
vehicles, totalCount, err = v.vehicleRepo.GetAll(ctx, provider)
if withArchived {
vehicles, totalCount, err = v.vehicleRepo.GetAllWithArchived(ctx, provider)
} else {
vehicles, totalCount, err = v.vehicleRepo.GetAll(ctx, provider)
}
}

if err != nil {
log.Debug("failed to fetch vehicles", "error", err)
log.Error("failed to fetch vehicles", "error", err)
return nil, 0, service.MapError(ctx, err, service.ErrorLogEntityNotFound)
}

return vehicles, totalCount, nil
}

func (v *VehicleService) GetAllArchived(ctx context.Context) ([]*entities.Vehicle, error) {
log := logger.GetLogger(ctx)
vehicles, err := v.vehicleRepo.GetAllArchived(ctx)
if err != nil {
log.Error("failed to get all archived vehicles", "error", err)
return nil, service.MapError(ctx, err, service.ErrorLogAll)
}

return vehicles, nil
}

func (v *VehicleService) GetByID(ctx context.Context, id int32) (*entities.Vehicle, error) {
log := logger.GetLogger(ctx)
got, err := v.vehicleRepo.GetByID(ctx, id)
Expand Down Expand Up @@ -86,7 +106,6 @@ func (v *VehicleService) Create(ctx context.Context, createData *entities.Vehicl
return nil, service.ErrVehiclePlateTaken
}

//nolint:dupl // this is create specific
created, err := v.vehicleRepo.Create(ctx, func(vh *entities.Vehicle) (bool, error) {
vh.NumberPlate = createData.NumberPlate
vh.Description = createData.Description
Expand Down Expand Up @@ -136,7 +155,6 @@ func (v *VehicleService) Update(ctx context.Context, id int32, updateData *entit
}
}

//nolint:dupl // this is update specific
err = v.vehicleRepo.Update(ctx, id, func(vh *entities.Vehicle) (bool, error) {
vh.NumberPlate = updateData.NumberPlate
vh.Description = updateData.Description
Expand Down Expand Up @@ -180,6 +198,27 @@ func (v *VehicleService) Delete(ctx context.Context, id int32) error {
return nil
}

func (v *VehicleService) Archive(ctx context.Context, id int32) error {
log := logger.GetLogger(ctx)
vehicle, err := v.vehicleRepo.GetByID(ctx, id)
if err != nil {
log.Debug("failed to get vehicle by id in archive request", "error", err, "vehicle_id", id)
return service.MapError(ctx, err, service.ErrorLogEntityNotFound)
}

if !vehicle.ArchivedAt.IsZero() {
log.Debug("vehicle is already archived", "archived_at", vehicle.ArchivedAt, "vehicle_id", id)
return service.NewError(service.Conflict, "vehicle already archived")
}

if err := v.vehicleRepo.Archive(ctx, id); err != nil {
log.Debug("failed to archive vehicle", "error", err, "vehicle_id", id)
}

log.Info("vehicle archived successfully", "vehicle_id", id)
return nil
}

func (v *VehicleService) Ready() bool {
return v.vehicleRepo != nil
}
Expand Down
12 changes: 6 additions & 6 deletions internal/service/domain/vehicle/vehicle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func TestVehicleService_GetAll(t *testing.T) {
vehicleRepo.EXPECT().GetAll(ctx, "").Return(expectedVehicles, int64(len(expectedVehicles)), nil)

// when
vehicles, totalCount, err := svc.GetAll(ctx, "", "")
vehicles, totalCount, err := svc.GetAll(ctx, "", "", false)

// then
assert.NoError(t, err)
Expand All @@ -40,7 +40,7 @@ func TestVehicleService_GetAll(t *testing.T) {
vehicleRepo.EXPECT().GetAll(ctx, "test-provider").Return(expectedVehicles, int64(len(expectedVehicles)), nil)

// when
vehicles, totalCount, err := svc.GetAll(ctx, "test-provider", "")
vehicles, totalCount, err := svc.GetAll(ctx, "test-provider", "", false)

// then
assert.NoError(t, err)
Expand All @@ -56,7 +56,7 @@ func TestVehicleService_GetAll(t *testing.T) {
vehicleRepo.EXPECT().GetAllByType(ctx, "", entities.VehicleTypeTrailer).Return(expectedVehicles, int64(len(expectedVehicles)), nil)

// when
vehicles, totalCount, err := svc.GetAll(ctx, "", "trailer")
vehicles, totalCount, err := svc.GetAll(ctx, "", "trailer", false)

// then
assert.NoError(t, err)
Expand All @@ -72,7 +72,7 @@ func TestVehicleService_GetAll(t *testing.T) {
vehicleRepo.EXPECT().GetAllByType(ctx, "test-provider", entities.VehicleTypeTrailer).Return(expectedVehicles, int64(len(expectedVehicles)), nil)

// when
vehicles, totalCount, err := svc.GetAll(ctx, "test-provider", "trailer")
vehicles, totalCount, err := svc.GetAll(ctx, "test-provider", "trailer", false)

// then
assert.NoError(t, err)
Expand All @@ -87,7 +87,7 @@ func TestVehicleService_GetAll(t *testing.T) {
vehicleRepo.EXPECT().GetAll(ctx, "").Return([]*entities.Vehicle{}, int64(0), nil)

// when
vehicles, totalCount, err := svc.GetAll(ctx, "", "")
vehicles, totalCount, err := svc.GetAll(ctx, "", "", false)

// then
assert.NoError(t, err)
Expand All @@ -103,7 +103,7 @@ func TestVehicleService_GetAll(t *testing.T) {
vehicleRepo.EXPECT().GetAll(ctx, "").Return(nil, int64(0), expectedErr)

// when
vehicles, totalCount, err := svc.GetAll(ctx, "", "")
vehicles, totalCount, err := svc.GetAll(ctx, "", "", false)

// then
assert.Error(t, err)
Expand Down
5 changes: 4 additions & 1 deletion internal/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ const (
Unauthorized ErrorCode = 401
Forbidden ErrorCode = 403
NotFound ErrorCode = 404
Conflict ErrorCode = 409
InternalError ErrorCode = 500
)

Expand Down Expand Up @@ -152,11 +153,13 @@ type CrudService[T any, CreateType any, UpdateType any] interface {

type VehicleService interface {
Service
GetAll(ctx context.Context, provider string, vehicleType string) ([]*domain.Vehicle, int64, error)
GetAll(ctx context.Context, provider, vehicleType string, withArchived bool) ([]*domain.Vehicle, int64, error)
GetAllArchived(ctx context.Context) ([]*domain.Vehicle, error)
GetByID(ctx context.Context, id int32) (*domain.Vehicle, error)
Create(ctx context.Context, createData *domain.VehicleCreate) (*domain.Vehicle, error)
Update(ctx context.Context, id int32, updateData *domain.VehicleUpdate) (*domain.Vehicle, error)
Delete(ctx context.Context, id int32) error
Archive(ctx context.Context, id int32) error
GetByPlate(ctx context.Context, plate string) (*domain.Vehicle, error)
}

Expand Down
Loading

0 comments on commit e6794e8

Please sign in to comment.