Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: archive vehicles #433

Merged
merged 5 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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