2 Commits 97b1d67a1c ... e5b773430f

Author SHA1 Message Date
  Dmitriy Gnatenko e5b773430f Refactoring 4 weeks ago
  Dmitriy Gnatenko 97b1d67a1c Refactoring 4 weeks ago
41 changed files with 1354 additions and 493 deletions
  1. 5 3
      internal/api/v1/image/delete_place_image.go
  2. 5 3
      internal/api/v1/image/delete_thing_image.go
  3. 5 3
      internal/api/v1/image/get_place_images.go
  4. 4 2
      internal/api/v1/image/get_thing_images.go
  5. 4 3
      internal/api/v1/notification/delete_thing_notification.go
  6. 3 2
      internal/api/v1/notification/get_thing_notification.go
  7. 5 4
      internal/api/v1/notification/update_thing_notification.go
  8. 3 2
      internal/api/v1/place/get_place.go
  9. 3 2
      internal/api/v1/place/get_place_nested.go
  10. 4 3
      internal/api/v1/place/update_place.go
  11. 13 4
      internal/api/v1/tag/add_tag.go
  12. 11 14
      internal/api/v1/tag/add_tag_test.go
  13. 18 6
      internal/api/v1/tag/add_thing_tag.go
  14. 23 24
      internal/api/v1/tag/add_thing_tag_test.go
  15. 20 13
      internal/api/v1/tag/delete_tag.go
  16. 48 75
      internal/api/v1/tag/delete_tag_test.go
  17. 14 3
      internal/api/v1/tag/delete_thing_tag.go
  18. 23 24
      internal/api/v1/tag/delete_thing_tag_test.go
  19. 8 3
      internal/api/v1/tag/get_tag.go
  20. 12 11
      internal/api/v1/tag/get_tag_test.go
  21. 6 3
      internal/api/v1/tag/get_tags.go
  22. 9 8
      internal/api/v1/tag/get_tags_test.go
  23. 8 3
      internal/api/v1/tag/get_thing_tags.go
  24. 13 12
      internal/api/v1/tag/get_thing_tags_test.go
  25. 393 0
      internal/api/v1/tag/mocks/transaction_manager_minimock.go
  26. 11 4
      internal/api/v1/tag/update_tag.go
  27. 15 18
      internal/api/v1/tag/update_tag_test.go
  28. 28 17
      internal/api/v1/thing/add_thing.go
  29. 1 2
      internal/api/v1/thing/add_thing_test.go
  30. 43 36
      internal/api/v1/thing/delete_thing.go
  31. 1 2
      internal/api/v1/thing/delete_thing_test.go
  32. 9 4
      internal/api/v1/thing/get_place_things.go
  33. 19 18
      internal/api/v1/thing/get_place_things_test.go
  34. 8 3
      internal/api/v1/thing/get_thing.go
  35. 13 12
      internal/api/v1/thing/get_thing_test.go
  36. 393 0
      internal/api/v1/thing/mocks/transaction_manager_minimock.go
  37. 4 2
      internal/api/v1/thing/search_thing.go
  38. 11 10
      internal/api/v1/thing/search_thing_test.go
  39. 36 28
      internal/api/v1/thing/update_thing.go
  40. 82 107
      internal/api/v1/thing/update_thing_test.go
  41. 20 0
      internal/helpers/request/convert.go

+ 5 - 3
internal/api/v1/image/delete_place_image.go

@@ -7,6 +7,7 @@ import (
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2"
 
 
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/request"
 )
 )
 
 
 // @Router 		/api/v1/images/place/{imageId} [delete]
 // @Router 		/api/v1/images/place/{imageId} [delete]
@@ -26,19 +27,20 @@ func DeletePlaceImageHandler(
 ) fiber.Handler {
 ) fiber.Handler {
 	return func(fctx *fiber.Ctx) error {
 	return func(fctx *fiber.Ctx) error {
 		ctx := fctx.Context()
 		ctx := fctx.Context()
-		id, err := fctx.ParamsInt("imageId")
+
+		id, err := request.ConvertToUint64(fctx, "imageId")
 		if err != nil {
 		if err != nil {
 			logger.Info(ctx, err.Error())
 			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
 		err = tm.ReadCommitted(ctx, func(ctx context.Context) error {
 		err = tm.ReadCommitted(ctx, func(ctx context.Context) error {
-			image, txErr := placeImageRepository.Get(ctx, uint64(id))
+			image, txErr := placeImageRepository.Get(ctx, id)
 			if txErr != nil {
 			if txErr != nil {
 				return txErr
 				return txErr
 			}
 			}
 
 
-			if txErr = placeImageRepository.Delete(ctx, uint64(id)); txErr != nil {
+			if txErr = placeImageRepository.Delete(ctx, id); txErr != nil {
 				return txErr
 				return txErr
 			}
 			}
 
 

+ 5 - 3
internal/api/v1/image/delete_thing_image.go

@@ -7,6 +7,7 @@ import (
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2"
 
 
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/request"
 )
 )
 
 
 // @Router 		/api/v1/images/thing/{imageId} [delete]
 // @Router 		/api/v1/images/thing/{imageId} [delete]
@@ -26,19 +27,20 @@ func DeleteThingImageHandler(
 ) fiber.Handler {
 ) fiber.Handler {
 	return func(fctx *fiber.Ctx) error {
 	return func(fctx *fiber.Ctx) error {
 		ctx := fctx.Context()
 		ctx := fctx.Context()
-		id, err := fctx.ParamsInt("imageId")
+
+		id, err := request.ConvertToUint64(fctx, "imageId")
 		if err != nil {
 		if err != nil {
 			logger.Info(ctx, err.Error())
 			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
 		err = tm.ReadCommitted(ctx, func(ctx context.Context) error {
 		err = tm.ReadCommitted(ctx, func(ctx context.Context) error {
-			image, txErr := thingImageRepository.Get(ctx, uint64(id))
+			image, txErr := thingImageRepository.Get(ctx, id)
 			if txErr != nil {
 			if txErr != nil {
 				return txErr
 				return txErr
 			}
 			}
 
 
-			if txErr = thingImageRepository.Delete(ctx, uint64(id)); txErr != nil {
+			if txErr = thingImageRepository.Delete(ctx, id); txErr != nil {
 				return txErr
 				return txErr
 			}
 			}
 
 

+ 5 - 3
internal/api/v1/image/get_place_images.go

@@ -7,6 +7,7 @@ import (
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2"
 
 
 	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/request"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 )
 )
@@ -28,20 +29,21 @@ func GetPlaceImagesHandler(
 	return func(fctx *fiber.Ctx) error {
 	return func(fctx *fiber.Ctx) error {
 		var res []models.Image
 		var res []models.Image
 		ctx := fctx.Context()
 		ctx := fctx.Context()
-		id, err := fctx.ParamsInt("placeId")
+
+		id, err := request.ConvertToUint64(fctx, "placeId")
 		if err != nil {
 		if err != nil {
 			logger.Info(ctx, err.Error())
 			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
-		placesRes, err := placeImageRepository.GetByPlaceID(ctx, uint64(id))
+		placesRes, err := placeImageRepository.GetByPlaceID(ctx, id)
 		if err != nil {
 		if err != nil {
 			logger.Error(ctx, err.Error())
 			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 		res = append(res, placesRes...)
 		res = append(res, placesRes...)
 
 
-		thingsRes, err := thingImageRepository.GetByPlaceID(ctx, uint64(id))
+		thingsRes, err := thingImageRepository.GetByPlaceID(ctx, id)
 		if err != nil {
 		if err != nil {
 			logger.Error(ctx, err.Error())
 			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())

+ 4 - 2
internal/api/v1/image/get_thing_images.go

@@ -5,6 +5,7 @@ import (
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2"
 
 
 	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/request"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 )
 )
 
 
@@ -23,13 +24,14 @@ func GetThingImagesHandler(
 ) fiber.Handler {
 ) fiber.Handler {
 	return func(fctx *fiber.Ctx) error {
 	return func(fctx *fiber.Ctx) error {
 		ctx := fctx.Context()
 		ctx := fctx.Context()
-		id, err := fctx.ParamsInt("thingId")
+
+		id, err := request.ConvertToUint64(fctx, "thingId")
 		if err != nil {
 		if err != nil {
 			logger.Info(ctx, err.Error())
 			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
-		res, err := thingImageRepository.GetByThingID(ctx, uint64(id))
+		res, err := thingImageRepository.GetByThingID(ctx, id)
 		if err != nil {
 		if err != nil {
 			logger.Error(ctx, err.Error())
 			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())

+ 4 - 3
internal/api/v1/notification/delete_thing_notification.go

@@ -8,6 +8,7 @@ import (
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2"
 
 
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/request"
 )
 )
 
 
 // @Router 		/api/v1/things/notifications/{thingId} [delete]
 // @Router 		/api/v1/things/notifications/{thingId} [delete]
@@ -25,13 +26,13 @@ func DeleteThingNotificationHandler(
 ) fiber.Handler {
 ) fiber.Handler {
 	return func(fctx *fiber.Ctx) error {
 	return func(fctx *fiber.Ctx) error {
 		ctx := fctx.Context()
 		ctx := fctx.Context()
-		id, err := fctx.ParamsInt("thingId")
+		id, err := request.ConvertToUint64(fctx, "thingId")
 		if err != nil {
 		if err != nil {
 			logger.Info(ctx, err.Error())
 			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
-		_, err = thingNotificationRepository.Get(ctx, uint64(id))
+		_, err = thingNotificationRepository.Get(ctx, id)
 		if err != nil {
 		if err != nil {
 			if errors.Is(err, sql.ErrNoRows) {
 			if errors.Is(err, sql.ErrNoRows) {
 				logger.Info(ctx, err.Error())
 				logger.Info(ctx, err.Error())
@@ -42,7 +43,7 @@ func DeleteThingNotificationHandler(
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
-		if err = thingNotificationRepository.Delete(ctx, uint64(id)); err != nil {
+		if err = thingNotificationRepository.Delete(ctx, id); err != nil {
 			logger.Error(ctx, err.Error())
 			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}

+ 3 - 2
internal/api/v1/notification/get_thing_notification.go

@@ -8,6 +8,7 @@ import (
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2"
 
 
 	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/request"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 )
 )
 
 
@@ -27,13 +28,13 @@ func GetThingNotificationHandler(
 ) fiber.Handler {
 ) fiber.Handler {
 	return func(fctx *fiber.Ctx) error {
 	return func(fctx *fiber.Ctx) error {
 		ctx := fctx.Context()
 		ctx := fctx.Context()
-		id, err := fctx.ParamsInt("thingId")
+		id, err := request.ConvertToUint64(fctx, "thingId")
 		if err != nil {
 		if err != nil {
 			logger.Info(ctx, err.Error())
 			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
-		res, err := thingNotificationRepository.Get(ctx, uint64(id))
+		res, err := thingNotificationRepository.Get(ctx, id)
 		if err != nil {
 		if err != nil {
 			if errors.Is(err, sql.ErrNoRows) {
 			if errors.Is(err, sql.ErrNoRows) {
 				logger.Info(ctx, err.Error())
 				logger.Info(ctx, err.Error())

+ 5 - 4
internal/api/v1/notification/update_thing_notification.go

@@ -11,6 +11,7 @@ import (
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/request"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 )
 )
 
 
@@ -30,7 +31,7 @@ func UpdateThingNotificationHandler(
 ) fiber.Handler {
 ) fiber.Handler {
 	return func(fctx *fiber.Ctx) error {
 	return func(fctx *fiber.Ctx) error {
 		ctx := fctx.Context()
 		ctx := fctx.Context()
-		id, err := fctx.ParamsInt("thingId")
+		id, err := request.ConvertToUint64(fctx, "thingId")
 		if err != nil {
 		if err != nil {
 			logger.Info(ctx, err.Error())
 			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
@@ -48,7 +49,7 @@ func UpdateThingNotificationHandler(
 			return fctx.Status(fiber.StatusBadRequest).JSON(factory.CreateValidateErrorResponse(err))
 			return fctx.Status(fiber.StatusBadRequest).JSON(factory.CreateValidateErrorResponse(err))
 		}
 		}
 
 
-		_, err = thingNotificationRepository.Get(ctx, uint64(id))
+		_, err = thingNotificationRepository.Get(ctx, id)
 		if err != nil {
 		if err != nil {
 			if errors.Is(err, sql.ErrNoRows) {
 			if errors.Is(err, sql.ErrNoRows) {
 				logger.Info(ctx, err.Error())
 				logger.Info(ctx, err.Error())
@@ -59,7 +60,7 @@ func UpdateThingNotificationHandler(
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
-		dbReq, err := mappers.ToUpdateThingNotificationRequest(uint64(id), req)
+		dbReq, err := mappers.ToUpdateThingNotificationRequest(id, req)
 		if err != nil {
 		if err != nil {
 			logger.Info(ctx, err.Error())
 			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
@@ -70,7 +71,7 @@ func UpdateThingNotificationHandler(
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
-		res, err := thingNotificationRepository.Get(ctx, uint64(id))
+		res, err := thingNotificationRepository.Get(ctx, id)
 		if err != nil {
 		if err != nil {
 			logger.Error(ctx, err.Error())
 			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())

+ 3 - 2
internal/api/v1/place/get_place.go

@@ -8,6 +8,7 @@ import (
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2"
 
 
 	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/request"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 )
 )
 
 
@@ -25,13 +26,13 @@ import (
 func GetPlaceHandler(placeRepository PlaceRepository) fiber.Handler {
 func GetPlaceHandler(placeRepository PlaceRepository) fiber.Handler {
 	return func(fctx *fiber.Ctx) error {
 	return func(fctx *fiber.Ctx) error {
 		ctx := fctx.Context()
 		ctx := fctx.Context()
-		id, err := fctx.ParamsInt("placeId")
+		id, err := request.ConvertToUint64(fctx, "placeId")
 		if err != nil {
 		if err != nil {
 			logger.Info(ctx, err.Error())
 			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
-		res, err := placeRepository.Get(ctx, uint64(id))
+		res, err := placeRepository.Get(ctx, id)
 		if err != nil {
 		if err != nil {
 			if errors.Is(err, sql.ErrNoRows) {
 			if errors.Is(err, sql.ErrNoRows) {
 				logger.Info(ctx, err.Error())
 				logger.Info(ctx, err.Error())

+ 3 - 2
internal/api/v1/place/get_place_nested.go

@@ -5,6 +5,7 @@ import (
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2"
 
 
 	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/request"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 )
 )
 
 
@@ -21,13 +22,13 @@ import (
 func GetNestedPlacesHandler(placeRepository PlaceRepository) fiber.Handler {
 func GetNestedPlacesHandler(placeRepository PlaceRepository) fiber.Handler {
 	return func(fctx *fiber.Ctx) error {
 	return func(fctx *fiber.Ctx) error {
 		ctx := fctx.Context()
 		ctx := fctx.Context()
-		id, err := fctx.ParamsInt("parentPlaceId")
+		id, err := request.ConvertToUint64(fctx, "parentPlaceId")
 		if err != nil {
 		if err != nil {
 			logger.Info(ctx, err.Error())
 			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
-		res, err := placeRepository.GetNestedPlaces(ctx, uint64(id))
+		res, err := placeRepository.GetNestedPlaces(ctx, id)
 		if err != nil {
 		if err != nil {
 			logger.Error(ctx, err.Error())
 			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())

+ 4 - 3
internal/api/v1/place/update_place.go

@@ -8,6 +8,7 @@ import (
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/request"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 )
 )
 
 
@@ -25,7 +26,7 @@ import (
 func UpdatePlaceHandler(placeRepository PlaceRepository) fiber.Handler {
 func UpdatePlaceHandler(placeRepository PlaceRepository) fiber.Handler {
 	return func(fctx *fiber.Ctx) error {
 	return func(fctx *fiber.Ctx) error {
 		ctx := fctx.Context()
 		ctx := fctx.Context()
-		id, err := fctx.ParamsInt("placeId")
+		id, err := request.ConvertToUint64(fctx, "placeId")
 		if err != nil {
 		if err != nil {
 			logger.Info(ctx, err.Error())
 			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
@@ -43,13 +44,13 @@ func UpdatePlaceHandler(placeRepository PlaceRepository) fiber.Handler {
 			return fctx.Status(fiber.StatusBadRequest).JSON(factory.CreateValidateErrorResponse(err))
 			return fctx.Status(fiber.StatusBadRequest).JSON(factory.CreateValidateErrorResponse(err))
 		}
 		}
 
 
-		err = placeRepository.Update(ctx, mappers.ToUpdatePlaceRequest(uint64(id), req))
+		err = placeRepository.Update(ctx, mappers.ToUpdatePlaceRequest(id, req))
 		if err != nil {
 		if err != nil {
 			logger.Error(ctx, err.Error())
 			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
-		res, err := placeRepository.Get(ctx, uint64(id))
+		res, err := placeRepository.Get(ctx, id)
 		if err != nil {
 		if err != nil {
 			logger.Error(ctx, err.Error())
 			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())

+ 13 - 4
internal/api/v1/tag/add_tag.go

@@ -2,22 +2,27 @@ package tag
 
 
 //go:generate mkdir -p mocks
 //go:generate mkdir -p mocks
 //go:generate rm -rf ./mocks/*_minimock.go
 //go:generate rm -rf ./mocks/*_minimock.go
-//go:generate minimock -i TagRepository,ThingRepository,ThingTagRepository -o ./mocks/ -s "_minimock.go"
+//go:generate minimock -i TagRepository,ThingRepository,ThingTagRepository,TransactionManager -o ./mocks/ -s "_minimock.go"
 
 
 import (
 import (
 	"context"
 	"context"
 
 
+	"git.dmitriygnatenko.ru/dima/go-common/logger"
 	"github.com/go-playground/validator/v10"
 	"github.com/go-playground/validator/v10"
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2"
 
 
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 )
 )
 
 
 type (
 type (
+	TransactionManager interface {
+		ReadCommitted(context.Context, func(ctx context.Context) error) error
+	}
+
 	TagRepository interface {
 	TagRepository interface {
 		GetAll(ctx context.Context) ([]models.Tag, error)
 		GetAll(ctx context.Context) ([]models.Tag, error)
 		Get(ctx context.Context, id uint64) (*models.Tag, error)
 		Get(ctx context.Context, id uint64) (*models.Tag, error)
@@ -53,25 +58,29 @@ func AddTagHandler(tagRepository TagRepository) fiber.Handler {
 		ctx := fctx.Context()
 		ctx := fctx.Context()
 		req := dto.AddTagRequest{}
 		req := dto.AddTagRequest{}
 		if err := fctx.BodyParser(&req); err != nil {
 		if err := fctx.BodyParser(&req); err != nil {
+			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
 		var validate = validator.New()
 		var validate = validator.New()
 		if err := validate.Struct(req); err != nil {
 		if err := validate.Struct(req); err != nil {
+			logger.Info(ctx, err.Error())
 			return fctx.Status(fiber.StatusBadRequest).JSON(factory.CreateValidateErrorResponse(err))
 			return fctx.Status(fiber.StatusBadRequest).JSON(factory.CreateValidateErrorResponse(err))
 		}
 		}
 
 
-		id, err := tagRepository.Add(ctx, mappers.ToAddTagRequest(req), nil)
+		id, err := tagRepository.Add(ctx, mappers.ToAddTagRequest(req))
 		if err != nil {
 		if err != nil {
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
 		res, err := tagRepository.Get(ctx, id)
 		res, err := tagRepository.Get(ctx, id)
 		if err != nil {
 		if err != nil {
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
-		res = helpers.ApplyLocation(fctx, res)
+		res = location.ApplyLocation(fctx, res)
 
 
 		return fctx.JSON(mappers.ToTagResponse(*res))
 		return fctx.JSON(mappers.ToTagResponse(*res))
 	}
 	}

+ 11 - 14
internal/api/v1/tag/add_tag_test.go

@@ -2,8 +2,6 @@ package tag
 
 
 import (
 import (
 	"context"
 	"context"
-	"database/sql"
-	"errors"
 	"net/http/httptest"
 	"net/http/httptest"
 	"testing"
 	"testing"
 
 
@@ -12,10 +10,9 @@ import (
 	"github.com/gojuno/minimock/v3"
 	"github.com/gojuno/minimock/v3"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
 
 
-	API "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/tag/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/tag/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/test"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 )
 )
 
 
@@ -30,10 +27,10 @@ func TestAddTagHandler(t *testing.T) {
 	}
 	}
 
 
 	var (
 	var (
-		tagID     = gofakeit.Number(1, 1000)
+		tagID     = uint64(gofakeit.Number(1, 1000))
 		title     = gofakeit.Phrase()
 		title     = gofakeit.Phrase()
 		style     = gofakeit.Phrase()
 		style     = gofakeit.Phrase()
-		testError = errors.New(gofakeit.Phrase())
+		testError = gofakeit.Error()
 		layout    = "2006-01-02 15:04:05"
 		layout    = "2006-01-02 15:04:05"
 
 
 		correctReq = req{
 		correctReq = req{
@@ -78,12 +75,12 @@ func TestAddTagHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.AddMock.Inspect(func(ctx context.Context, req models.AddTagRequest, tx *sql.Tx) {
+				mock.AddMock.Inspect(func(ctx context.Context, req models.AddTagRequest) {
 					assert.Equal(mc, title, req.Title)
 					assert.Equal(mc, title, req.Title)
 					assert.Equal(mc, style, req.Style)
 					assert.Equal(mc, style, req.Style)
 				}).Return(tagID, nil)
 				}).Return(tagID, nil)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(&repoRes, nil)
 				}).Return(&repoRes, nil)
 
 
@@ -150,7 +147,7 @@ func TestAddTagHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.AddMock.Inspect(func(ctx context.Context, req models.AddTagRequest, tx *sql.Tx) {
+				mock.AddMock.Inspect(func(ctx context.Context, req models.AddTagRequest) {
 					assert.Equal(mc, title, req.Title)
 					assert.Equal(mc, title, req.Title)
 					assert.Equal(mc, style, req.Style)
 					assert.Equal(mc, style, req.Style)
 				}).Return(0, testError)
 				}).Return(0, testError)
@@ -165,12 +162,12 @@ func TestAddTagHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.AddMock.Inspect(func(ctx context.Context, req models.AddTagRequest, tx *sql.Tx) {
+				mock.AddMock.Inspect(func(ctx context.Context, req models.AddTagRequest) {
 					assert.Equal(mc, title, req.Title)
 					assert.Equal(mc, title, req.Title)
 					assert.Equal(mc, style, req.Style)
 					assert.Equal(mc, style, req.Style)
 				}).Return(tagID, nil)
 				}).Return(tagID, nil)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil, testError)
 				}).Return(nil, testError)
 
 
@@ -187,13 +184,13 @@ func TestAddTagHandler(t *testing.T) {
 			fiberApp := fiber.New()
 			fiberApp := fiber.New()
 			fiberApp.Post("/v1/tags", AddTagHandler(tt.tagRepoMock(mc)))
 			fiberApp.Post("/v1/tags", AddTagHandler(tt.tagRepoMock(mc)))
 
 
-			fiberReq := httptest.NewRequest(tt.req.method, tt.req.route, helpers.ConvertDataToIOReader(tt.req.body))
+			fiberReq := httptest.NewRequest(tt.req.method, tt.req.route, test.ConvertDataToIOReader(tt.req.body))
 			fiberReq.Header.Add(fiber.HeaderContentType, tt.req.contentType)
 			fiberReq.Header.Add(fiber.HeaderContentType, tt.req.contentType)
-			fiberRes, _ := fiberApp.Test(fiberReq, API.DefaultTestTimeOut)
+			fiberRes, _ := fiberApp.Test(fiberReq, test.TestTimeout)
 
 
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			if tt.resBody != nil {
 			if tt.resBody != nil {
-				assert.Equal(t, helpers.MarshalResponse(tt.resBody), helpers.ConvertBodyToString(fiberRes.Body))
+				assert.Equal(t, test.MarshalResponse(tt.resBody), test.ConvertBodyToString(fiberRes.Body))
 			}
 			}
 		})
 		})
 	}
 	}

+ 18 - 6
internal/api/v1/tag/add_thing_tag.go

@@ -4,9 +4,11 @@ import (
 	"database/sql"
 	"database/sql"
 	"errors"
 	"errors"
 
 
+	"git.dmitriygnatenko.ru/dima/go-common/logger"
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2"
 
 
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/request"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 )
 )
 
 
@@ -28,36 +30,46 @@ func AddThingTagHandler(
 ) fiber.Handler {
 ) fiber.Handler {
 	return func(fctx *fiber.Ctx) error {
 	return func(fctx *fiber.Ctx) error {
 		ctx := fctx.Context()
 		ctx := fctx.Context()
-		tagID, err := fctx.ParamsInt("tagId")
+
+		tagID, err := request.ConvertToUint64(fctx, "tagId")
 		if err != nil {
 		if err != nil {
+			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
-		thingID, err := fctx.ParamsInt("thingId")
+		thingID, err := request.ConvertToUint64(fctx, "thingId")
 		if err != nil {
 		if err != nil {
+			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
 		tag, err := tagRepository.Get(ctx, tagID)
 		tag, err := tagRepository.Get(ctx, tagID)
 		if err != nil {
 		if err != nil {
 			if errors.Is(err, sql.ErrNoRows) {
 			if errors.Is(err, sql.ErrNoRows) {
+				logger.Info(ctx, err.Error())
 				return fiber.NewError(fiber.StatusBadRequest, "")
 				return fiber.NewError(fiber.StatusBadRequest, "")
 			}
 			}
+
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
 		if _, err = thingRepository.Get(ctx, thingID); err != nil {
 		if _, err = thingRepository.Get(ctx, thingID); err != nil {
-			if err == sql.ErrNoRows {
+			if errors.Is(err, sql.ErrNoRows) {
+				logger.Info(ctx, err.Error())
 				return fiber.NewError(fiber.StatusBadRequest, "")
 				return fiber.NewError(fiber.StatusBadRequest, "")
 			}
 			}
+
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
-		if err = thingTagRepository.Add(ctx, mappers.ToAddThingTagRequest(tagID, thingID), nil); err != nil {
+		if err = thingTagRepository.Add(ctx, mappers.ToAddThingTagRequest(tagID, thingID)); err != nil {
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
-		tag = helpers.ApplyLocation(fctx, tag)
+		tag = location.ApplyLocation(fctx, tag)
 
 
 		return fctx.JSON(mappers.ToTagResponse(*tag))
 		return fctx.JSON(mappers.ToTagResponse(*tag))
 	}
 	}

+ 23 - 24
internal/api/v1/tag/add_thing_tag_test.go

@@ -3,7 +3,6 @@ package tag
 import (
 import (
 	"context"
 	"context"
 	"database/sql"
 	"database/sql"
-	"errors"
 	"net/http/httptest"
 	"net/http/httptest"
 	"strconv"
 	"strconv"
 	"testing"
 	"testing"
@@ -13,10 +12,9 @@ import (
 	"github.com/gojuno/minimock/v3"
 	"github.com/gojuno/minimock/v3"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
 
 
-	API "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/tag/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/tag/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/test"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 )
 )
 
 
@@ -30,14 +28,15 @@ func TestAddThingTagHandler(t *testing.T) {
 	}
 	}
 
 
 	var (
 	var (
-		tagID     = gofakeit.Number(1, 1000)
-		thingID   = gofakeit.Number(1, 1000)
-		testError = errors.New(gofakeit.Phrase())
+		tagID     = uint64(gofakeit.Number(1, 1000))
+		thingID   = uint64(gofakeit.Number(1, 1000))
+		testError = gofakeit.Error()
 		layout    = "2006-01-02 15:04:05"
 		layout    = "2006-01-02 15:04:05"
 
 
 		correctReq = req{
 		correctReq = req{
-			method:      fiber.MethodPost,
-			route:       "/v1/tags/" + strconv.Itoa(tagID) + "/thing/" + strconv.Itoa(thingID),
+			method: fiber.MethodPost,
+			route: "/v1/tags/" + strconv.FormatUint(tagID, 10) +
+				"/thing/" + strconv.FormatUint(thingID, 10),
 			contentType: fiber.MIMEApplicationJSON,
 			contentType: fiber.MIMEApplicationJSON,
 		}
 		}
 
 
@@ -75,7 +74,7 @@ func TestAddThingTagHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(&tagRepoRes, nil)
 				}).Return(&tagRepoRes, nil)
 
 
@@ -84,7 +83,7 @@ func TestAddThingTagHandler(t *testing.T) {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				mock := mocks.NewThingRepositoryMock(mc)
 				mock := mocks.NewThingRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 				}).Return(nil, nil)
 				}).Return(nil, nil)
 
 
@@ -93,7 +92,7 @@ func TestAddThingTagHandler(t *testing.T) {
 			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
 			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
 				mock := mocks.NewThingTagRepositoryMock(mc)
 				mock := mocks.NewThingTagRepositoryMock(mc)
 
 
-				mock.AddMock.Inspect(func(ctx context.Context, req models.AddThingTagRequest, tx *sql.Tx) {
+				mock.AddMock.Inspect(func(ctx context.Context, req models.AddThingTagRequest) {
 					assert.Equal(mc, tagID, req.TagID)
 					assert.Equal(mc, tagID, req.TagID)
 					assert.Equal(mc, thingID, req.ThingID)
 					assert.Equal(mc, thingID, req.ThingID)
 				}).Return(nil)
 				}).Return(nil)
@@ -105,7 +104,7 @@ func TestAddThingTagHandler(t *testing.T) {
 			name: "negative case - request without tagID",
 			name: "negative case - request without tagID",
 			req: req{
 			req: req{
 				method:      fiber.MethodPost,
 				method:      fiber.MethodPost,
-				route:       "/v1/tags/" + gofakeit.Word() + "/thing/" + strconv.Itoa(thingID),
+				route:       "/v1/tags/" + gofakeit.Word() + "/thing/" + strconv.FormatUint(thingID, 10),
 				contentType: fiber.MIMEApplicationJSON,
 				contentType: fiber.MIMEApplicationJSON,
 			},
 			},
 			resCode: fiber.StatusBadRequest,
 			resCode: fiber.StatusBadRequest,
@@ -123,7 +122,7 @@ func TestAddThingTagHandler(t *testing.T) {
 			name: "negative case - request without thingID",
 			name: "negative case - request without thingID",
 			req: req{
 			req: req{
 				method:      fiber.MethodPost,
 				method:      fiber.MethodPost,
-				route:       "/v1/tags/" + strconv.Itoa(tagID) + "/thing/" + gofakeit.Word(),
+				route:       "/v1/tags/" + strconv.FormatUint(tagID, 10) + "/thing/" + gofakeit.Word(),
 				contentType: fiber.MIMEApplicationJSON,
 				contentType: fiber.MIMEApplicationJSON,
 			},
 			},
 			resCode: fiber.StatusBadRequest,
 			resCode: fiber.StatusBadRequest,
@@ -144,7 +143,7 @@ func TestAddThingTagHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil, sql.ErrNoRows)
 				}).Return(nil, sql.ErrNoRows)
 
 
@@ -164,7 +163,7 @@ func TestAddThingTagHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil, testError)
 				}).Return(nil, testError)
 
 
@@ -184,7 +183,7 @@ func TestAddThingTagHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil, nil)
 				}).Return(nil, nil)
 
 
@@ -193,7 +192,7 @@ func TestAddThingTagHandler(t *testing.T) {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				mock := mocks.NewThingRepositoryMock(mc)
 				mock := mocks.NewThingRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 				}).Return(nil, sql.ErrNoRows)
 				}).Return(nil, sql.ErrNoRows)
 
 
@@ -210,7 +209,7 @@ func TestAddThingTagHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil, nil)
 				}).Return(nil, nil)
 
 
@@ -219,7 +218,7 @@ func TestAddThingTagHandler(t *testing.T) {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				mock := mocks.NewThingRepositoryMock(mc)
 				mock := mocks.NewThingRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 				}).Return(nil, testError)
 				}).Return(nil, testError)
 
 
@@ -236,7 +235,7 @@ func TestAddThingTagHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil, nil)
 				}).Return(nil, nil)
 
 
@@ -245,7 +244,7 @@ func TestAddThingTagHandler(t *testing.T) {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				mock := mocks.NewThingRepositoryMock(mc)
 				mock := mocks.NewThingRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 				}).Return(nil, nil)
 				}).Return(nil, nil)
 
 
@@ -254,7 +253,7 @@ func TestAddThingTagHandler(t *testing.T) {
 			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
 			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
 				mock := mocks.NewThingTagRepositoryMock(mc)
 				mock := mocks.NewThingTagRepositoryMock(mc)
 
 
-				mock.AddMock.Inspect(func(ctx context.Context, req models.AddThingTagRequest, tx *sql.Tx) {
+				mock.AddMock.Inspect(func(ctx context.Context, req models.AddThingTagRequest) {
 					assert.Equal(mc, tagID, req.TagID)
 					assert.Equal(mc, tagID, req.TagID)
 					assert.Equal(mc, thingID, req.ThingID)
 					assert.Equal(mc, thingID, req.ThingID)
 				}).Return(testError)
 				}).Return(testError)
@@ -279,11 +278,11 @@ func TestAddThingTagHandler(t *testing.T) {
 
 
 			fiberReq := httptest.NewRequest(tt.req.method, tt.req.route, nil)
 			fiberReq := httptest.NewRequest(tt.req.method, tt.req.route, nil)
 			fiberReq.Header.Add(fiber.HeaderContentType, tt.req.contentType)
 			fiberReq.Header.Add(fiber.HeaderContentType, tt.req.contentType)
-			fiberRes, _ := fiberApp.Test(fiberReq, API.DefaultTestTimeOut)
+			fiberRes, _ := fiberApp.Test(fiberReq, test.TestTimeout)
 
 
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			if tt.resBody != nil {
 			if tt.resBody != nil {
-				assert.Equal(t, helpers.MarshalResponse(tt.resBody), helpers.ConvertBodyToString(fiberRes.Body))
+				assert.Equal(t, test.MarshalResponse(tt.resBody), test.ConvertBodyToString(fiberRes.Body))
 			}
 			}
 		})
 		})
 	}
 	}

+ 20 - 13
internal/api/v1/tag/delete_tag.go

@@ -1,13 +1,15 @@
 package tag
 package tag
 
 
 import (
 import (
+	"context"
 	"database/sql"
 	"database/sql"
 	"errors"
 	"errors"
 
 
+	"git.dmitriygnatenko.ru/dima/go-common/logger"
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2"
 
 
-	API "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/request"
 )
 )
 
 
 // @Router 		/api/v1/tags/{tagId} [delete]
 // @Router 		/api/v1/tags/{tagId} [delete]
@@ -21,37 +23,42 @@ import (
 // @Accept      json
 // @Accept      json
 // @Produce     json
 // @Produce     json
 func DeleteTagHandler(
 func DeleteTagHandler(
+	tm TransactionManager,
 	tagRepository TagRepository,
 	tagRepository TagRepository,
 	thingTagRepository ThingTagRepository,
 	thingTagRepository ThingTagRepository,
 ) fiber.Handler {
 ) fiber.Handler {
 	return func(fctx *fiber.Ctx) error {
 	return func(fctx *fiber.Ctx) error {
 		ctx := fctx.Context()
 		ctx := fctx.Context()
-		id, err := fctx.ParamsInt("tagId")
+		id, err := request.ConvertToUint64(fctx, "tagId")
 		if err != nil {
 		if err != nil {
+			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
 		if _, err = tagRepository.Get(ctx, id); err != nil {
 		if _, err = tagRepository.Get(ctx, id); err != nil {
 			if errors.Is(err, sql.ErrNoRows) {
 			if errors.Is(err, sql.ErrNoRows) {
+				logger.Info(ctx, err.Error())
 				return fiber.NewError(fiber.StatusBadRequest, "")
 				return fiber.NewError(fiber.StatusBadRequest, "")
 			}
 			}
-			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
-		}
 
 
-		tx, err := tagRepository.BeginTx(ctx, API.DefaultTxLevel)
-		if err != nil {
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
-		if err = thingTagRepository.DeleteByTagID(ctx, id, tx); err != nil {
-			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
-		}
+		err = tm.ReadCommitted(ctx, func(ctx context.Context) error {
+			if txErr := thingTagRepository.DeleteByTagID(ctx, id); txErr != nil {
+				return txErr
+			}
 
 
-		if err = tagRepository.Delete(ctx, id, tx); err != nil {
-			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
-		}
+			if txErr := tagRepository.Delete(ctx, id); txErr != nil {
+				return txErr
+			}
 
 
-		if err = tagRepository.CommitTx(tx); err != nil {
+			return nil
+		})
+
+		if err != nil {
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 

+ 48 - 75
internal/api/v1/tag/delete_tag_test.go

@@ -3,7 +3,6 @@ package tag
 import (
 import (
 	"context"
 	"context"
 	"database/sql"
 	"database/sql"
-	"errors"
 	"net/http/httptest"
 	"net/http/httptest"
 	"strconv"
 	"strconv"
 	"testing"
 	"testing"
@@ -13,10 +12,9 @@ import (
 	"github.com/gojuno/minimock/v3"
 	"github.com/gojuno/minimock/v3"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
 
 
-	API "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/tag/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/tag/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/test"
 )
 )
 
 
 func TestDeleteTagHandler(t *testing.T) {
 func TestDeleteTagHandler(t *testing.T) {
@@ -29,12 +27,16 @@ func TestDeleteTagHandler(t *testing.T) {
 	}
 	}
 
 
 	var (
 	var (
-		tagID     = gofakeit.Number(1, 1000)
-		testError = errors.New(gofakeit.Phrase())
+		tagID     = uint64(gofakeit.Number(1, 1000))
+		testError = gofakeit.Error()
+
+		txMockFunc = func(ctx context.Context, f func(ctx context.Context) error) error {
+			return f(ctx)
+		}
 
 
 		correctReq = req{
 		correctReq = req{
 			method:      fiber.MethodDelete,
 			method:      fiber.MethodDelete,
-			route:       "/v1/tags/" + strconv.Itoa(tagID),
+			route:       "/v1/tags/" + strconv.FormatUint(tagID, 10),
 			contentType: fiber.MIMEApplicationJSON,
 			contentType: fiber.MIMEApplicationJSON,
 		}
 		}
 	)
 	)
@@ -44,6 +46,7 @@ func TestDeleteTagHandler(t *testing.T) {
 		req              req
 		req              req
 		resCode          int
 		resCode          int
 		resBody          interface{}
 		resBody          interface{}
+		tmMock           func(mc *minimock.Controller) TransactionManager
 		tagRepoMock      func(mc *minimock.Controller) TagRepository
 		tagRepoMock      func(mc *minimock.Controller) TagRepository
 		thingTagRepoMock func(mc *minimock.Controller) ThingTagRepository
 		thingTagRepoMock func(mc *minimock.Controller) ThingTagRepository
 	}{
 	}{
@@ -55,6 +58,9 @@ func TestDeleteTagHandler(t *testing.T) {
 				contentType: fiber.MIMEApplicationJSON,
 				contentType: fiber.MIMEApplicationJSON,
 			},
 			},
 			resCode: fiber.StatusBadRequest,
 			resCode: fiber.StatusBadRequest,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				return mocks.NewTransactionManagerMock(mc)
+			},
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				return mocks.NewTagRepositoryMock(mc)
 				return mocks.NewTagRepositoryMock(mc)
 			},
 			},
@@ -66,10 +72,13 @@ func TestDeleteTagHandler(t *testing.T) {
 			name:    "negative case - tag is not exist",
 			name:    "negative case - tag is not exist",
 			req:     correctReq,
 			req:     correctReq,
 			resCode: fiber.StatusBadRequest,
 			resCode: fiber.StatusBadRequest,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				return mocks.NewTransactionManagerMock(mc)
+			},
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil, sql.ErrNoRows)
 				}).Return(nil, sql.ErrNoRows)
 
 
@@ -83,10 +92,13 @@ func TestDeleteTagHandler(t *testing.T) {
 			name:    "negative case - tag repository error (get)",
 			name:    "negative case - tag repository error (get)",
 			req:     correctReq,
 			req:     correctReq,
 			resCode: fiber.StatusInternalServerError,
 			resCode: fiber.StatusInternalServerError,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				return mocks.NewTransactionManagerMock(mc)
+			},
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil, testError)
 				}).Return(nil, testError)
 
 
@@ -97,43 +109,27 @@ func TestDeleteTagHandler(t *testing.T) {
 			},
 			},
 		},
 		},
 		{
 		{
-			name:    "negative case - tag repository error (begin tx)",
+			name:    "negative case - thing tag repository error (delete)",
 			req:     correctReq,
 			req:     correctReq,
 			resCode: fiber.StatusInternalServerError,
 			resCode: fiber.StatusInternalServerError,
-			tagRepoMock: func(mc *minimock.Controller) TagRepository {
-				mock := mocks.NewTagRepositoryMock(mc)
-
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
-					assert.Equal(mc, tagID, id)
-				}).Return(nil, nil)
-
-				mock.BeginTxMock.Return(nil, testError)
-
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
 				return mock
 				return mock
 			},
 			},
-			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
-				return mocks.NewThingTagRepositoryMock(mc)
-			},
-		},
-		{
-			name:    "negative case - thing tag repository error (delete)",
-			req:     correctReq,
-			resCode: fiber.StatusInternalServerError,
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil, nil)
 				}).Return(nil, nil)
 
 
-				mock.BeginTxMock.Return(nil, nil)
-
 				return mock
 				return mock
 			},
 			},
 			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
 			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
 				mock := mocks.NewThingTagRepositoryMock(mc)
 				mock := mocks.NewThingTagRepositoryMock(mc)
 
 
-				mock.DeleteByTagIDMock.Inspect(func(ctx context.Context, id int, tx *sql.Tx) {
+				mock.DeleteByTagIDMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(testError)
 				}).Return(testError)
 
 
@@ -144,56 +140,28 @@ func TestDeleteTagHandler(t *testing.T) {
 			name:    "negative case - tag repository error (delete)",
 			name:    "negative case - tag repository error (delete)",
 			req:     correctReq,
 			req:     correctReq,
 			resCode: fiber.StatusInternalServerError,
 			resCode: fiber.StatusInternalServerError,
-			tagRepoMock: func(mc *minimock.Controller) TagRepository {
-				mock := mocks.NewTagRepositoryMock(mc)
-
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
-					assert.Equal(mc, tagID, id)
-				}).Return(nil, nil)
-
-				mock.BeginTxMock.Return(nil, nil)
-
-				mock.DeleteMock.Inspect(func(ctx context.Context, id int, tx *sql.Tx) {
-					assert.Equal(mc, tagID, id)
-				}).Return(testError)
-
-				return mock
-			},
-			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
-				mock := mocks.NewThingTagRepositoryMock(mc)
-
-				mock.DeleteByTagIDMock.Inspect(func(ctx context.Context, id int, tx *sql.Tx) {
-					assert.Equal(mc, tagID, id)
-				}).Return(nil)
-
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
 				return mock
 				return mock
 			},
 			},
-		},
-		{
-			name:    "negative case - tag repository error (commit tx)",
-			req:     correctReq,
-			resCode: fiber.StatusInternalServerError,
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil, nil)
 				}).Return(nil, nil)
 
 
-				mock.BeginTxMock.Return(nil, nil)
-
-				mock.DeleteMock.Inspect(func(ctx context.Context, id int, tx *sql.Tx) {
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
-				}).Return(nil)
-
-				mock.CommitTxMock.Return(testError)
+				}).Return(testError)
 
 
 				return mock
 				return mock
 			},
 			},
 			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
 			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
 				mock := mocks.NewThingTagRepositoryMock(mc)
 				mock := mocks.NewThingTagRepositoryMock(mc)
 
 
-				mock.DeleteByTagIDMock.Inspect(func(ctx context.Context, id int, tx *sql.Tx) {
+				mock.DeleteByTagIDMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil)
 				}).Return(nil)
 
 
@@ -205,27 +173,28 @@ func TestDeleteTagHandler(t *testing.T) {
 			req:     correctReq,
 			req:     correctReq,
 			resCode: fiber.StatusOK,
 			resCode: fiber.StatusOK,
 			resBody: dto.EmptyResponse{},
 			resBody: dto.EmptyResponse{},
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
+				return mock
+			},
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil, nil)
 				}).Return(nil, nil)
 
 
-				mock.BeginTxMock.Return(nil, nil)
-
-				mock.DeleteMock.Inspect(func(ctx context.Context, id int, tx *sql.Tx) {
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil)
 				}).Return(nil)
 
 
-				mock.CommitTxMock.Return(nil)
-
 				return mock
 				return mock
 			},
 			},
 			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
 			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
 				mock := mocks.NewThingTagRepositoryMock(mc)
 				mock := mocks.NewThingTagRepositoryMock(mc)
 
 
-				mock.DeleteByTagIDMock.Inspect(func(ctx context.Context, id int, tx *sql.Tx) {
+				mock.DeleteByTagIDMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil)
 				}).Return(nil)
 
 
@@ -240,15 +209,19 @@ func TestDeleteTagHandler(t *testing.T) {
 
 
 			mc := minimock.NewController(t)
 			mc := minimock.NewController(t)
 			fiberApp := fiber.New()
 			fiberApp := fiber.New()
-			fiberApp.Delete("/v1/tags/:tagId", DeleteTagHandler(tt.tagRepoMock(mc), tt.thingTagRepoMock(mc)))
+			fiberApp.Delete("/v1/tags/:tagId", DeleteTagHandler(
+				tt.tmMock(mc),
+				tt.tagRepoMock(mc),
+				tt.thingTagRepoMock(mc),
+			))
 
 
 			fiberReq := httptest.NewRequest(tt.req.method, tt.req.route, nil)
 			fiberReq := httptest.NewRequest(tt.req.method, tt.req.route, nil)
 			fiberReq.Header.Add(fiber.HeaderContentType, tt.req.contentType)
 			fiberReq.Header.Add(fiber.HeaderContentType, tt.req.contentType)
-			fiberRes, _ := fiberApp.Test(fiberReq, API.DefaultTestTimeOut)
+			fiberRes, _ := fiberApp.Test(fiberReq, test.TestTimeout)
 
 
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			if tt.resBody != nil {
 			if tt.resBody != nil {
-				assert.Equal(t, helpers.MarshalResponse(tt.resBody), helpers.ConvertBodyToString(fiberRes.Body))
+				assert.Equal(t, test.MarshalResponse(tt.resBody), test.ConvertBodyToString(fiberRes.Body))
 			}
 			}
 		})
 		})
 	}
 	}

+ 14 - 3
internal/api/v1/tag/delete_thing_tag.go

@@ -4,9 +4,11 @@ import (
 	"database/sql"
 	"database/sql"
 	"errors"
 	"errors"
 
 
+	"git.dmitriygnatenko.ru/dima/go-common/logger"
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2"
 
 
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/request"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 )
 )
 
 
@@ -28,31 +30,40 @@ func DeleteThingTagHandler(
 ) fiber.Handler {
 ) fiber.Handler {
 	return func(fctx *fiber.Ctx) error {
 	return func(fctx *fiber.Ctx) error {
 		ctx := fctx.Context()
 		ctx := fctx.Context()
-		tagID, err := fctx.ParamsInt("tagId")
+		tagID, err := request.ConvertToUint64(fctx, "tagId")
 		if err != nil {
 		if err != nil {
+			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
-		thingID, err := fctx.ParamsInt("thingId")
+		thingID, err := request.ConvertToUint64(fctx, "thingId")
 		if err != nil {
 		if err != nil {
+			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
 		if _, err = tagRepository.Get(ctx, tagID); err != nil {
 		if _, err = tagRepository.Get(ctx, tagID); err != nil {
 			if errors.Is(err, sql.ErrNoRows) {
 			if errors.Is(err, sql.ErrNoRows) {
+				logger.Info(ctx, err.Error())
 				return fiber.NewError(fiber.StatusBadRequest, "")
 				return fiber.NewError(fiber.StatusBadRequest, "")
 			}
 			}
+
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
 		if _, err = thingRepository.Get(ctx, thingID); err != nil {
 		if _, err = thingRepository.Get(ctx, thingID); err != nil {
 			if errors.Is(err, sql.ErrNoRows) {
 			if errors.Is(err, sql.ErrNoRows) {
+				logger.Info(ctx, err.Error())
 				return fiber.NewError(fiber.StatusBadRequest, "")
 				return fiber.NewError(fiber.StatusBadRequest, "")
 			}
 			}
+
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
-		if err = thingTagRepository.Delete(ctx, mappers.ToDeleteThingTagRequest(tagID, thingID), nil); err != nil {
+		if err = thingTagRepository.Delete(ctx, mappers.ToDeleteThingTagRequest(tagID, thingID)); err != nil {
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 

+ 23 - 24
internal/api/v1/tag/delete_thing_tag_test.go

@@ -3,7 +3,6 @@ package tag
 import (
 import (
 	"context"
 	"context"
 	"database/sql"
 	"database/sql"
-	"errors"
 	"net/http/httptest"
 	"net/http/httptest"
 	"strconv"
 	"strconv"
 	"testing"
 	"testing"
@@ -13,10 +12,9 @@ import (
 	"github.com/gojuno/minimock/v3"
 	"github.com/gojuno/minimock/v3"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
 
 
-	API "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/tag/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/tag/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/test"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 )
 )
 
 
@@ -30,13 +28,14 @@ func TestDeleteThingTagHandler(t *testing.T) {
 	}
 	}
 
 
 	var (
 	var (
-		tagID     = gofakeit.Number(1, 1000)
-		thingID   = gofakeit.Number(1, 1000)
-		testError = errors.New(gofakeit.Phrase())
+		tagID     = uint64(gofakeit.Number(1, 1000))
+		thingID   = uint64(gofakeit.Number(1, 1000))
+		testError = gofakeit.Error()
 
 
 		correctReq = req{
 		correctReq = req{
-			method:      fiber.MethodDelete,
-			route:       "/v1/tags/" + strconv.Itoa(tagID) + "/thing/" + strconv.Itoa(thingID),
+			method: fiber.MethodDelete,
+			route: "/v1/tags/" + strconv.FormatUint(tagID, 10) +
+				"/thing/" + strconv.FormatUint(thingID, 10),
 			contentType: fiber.MIMEApplicationJSON,
 			contentType: fiber.MIMEApplicationJSON,
 		}
 		}
 	)
 	)
@@ -58,7 +57,7 @@ func TestDeleteThingTagHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil, nil)
 				}).Return(nil, nil)
 
 
@@ -67,7 +66,7 @@ func TestDeleteThingTagHandler(t *testing.T) {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				mock := mocks.NewThingRepositoryMock(mc)
 				mock := mocks.NewThingRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 				}).Return(nil, nil)
 				}).Return(nil, nil)
 
 
@@ -76,7 +75,7 @@ func TestDeleteThingTagHandler(t *testing.T) {
 			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
 			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
 				mock := mocks.NewThingTagRepositoryMock(mc)
 				mock := mocks.NewThingTagRepositoryMock(mc)
 
 
-				mock.DeleteMock.Inspect(func(ctx context.Context, req models.DeleteThingTagRequest, tx *sql.Tx) {
+				mock.DeleteMock.Inspect(func(ctx context.Context, req models.DeleteThingTagRequest) {
 					assert.Equal(mc, tagID, req.TagID)
 					assert.Equal(mc, tagID, req.TagID)
 					assert.Equal(mc, thingID, req.ThingID)
 					assert.Equal(mc, thingID, req.ThingID)
 				}).Return(nil)
 				}).Return(nil)
@@ -88,7 +87,7 @@ func TestDeleteThingTagHandler(t *testing.T) {
 			name: "negative case - request without tagID",
 			name: "negative case - request without tagID",
 			req: req{
 			req: req{
 				method:      fiber.MethodDelete,
 				method:      fiber.MethodDelete,
-				route:       "/v1/tags/" + gofakeit.Word() + "/thing/" + strconv.Itoa(thingID),
+				route:       "/v1/tags/" + gofakeit.Word() + "/thing/" + strconv.FormatUint(thingID, 10),
 				contentType: fiber.MIMEApplicationJSON,
 				contentType: fiber.MIMEApplicationJSON,
 			},
 			},
 			resCode: fiber.StatusBadRequest,
 			resCode: fiber.StatusBadRequest,
@@ -106,7 +105,7 @@ func TestDeleteThingTagHandler(t *testing.T) {
 			name: "negative case - request without thingID",
 			name: "negative case - request without thingID",
 			req: req{
 			req: req{
 				method:      fiber.MethodDelete,
 				method:      fiber.MethodDelete,
-				route:       "/v1/tags/" + strconv.Itoa(tagID) + "/thing/" + gofakeit.Word(),
+				route:       "/v1/tags/" + strconv.FormatUint(tagID, 10) + "/thing/" + gofakeit.Word(),
 				contentType: fiber.MIMEApplicationJSON,
 				contentType: fiber.MIMEApplicationJSON,
 			},
 			},
 			resCode: fiber.StatusBadRequest,
 			resCode: fiber.StatusBadRequest,
@@ -127,7 +126,7 @@ func TestDeleteThingTagHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil, sql.ErrNoRows)
 				}).Return(nil, sql.ErrNoRows)
 
 
@@ -147,7 +146,7 @@ func TestDeleteThingTagHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil, testError)
 				}).Return(nil, testError)
 
 
@@ -167,7 +166,7 @@ func TestDeleteThingTagHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil, nil)
 				}).Return(nil, nil)
 
 
@@ -176,7 +175,7 @@ func TestDeleteThingTagHandler(t *testing.T) {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				mock := mocks.NewThingRepositoryMock(mc)
 				mock := mocks.NewThingRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 				}).Return(nil, sql.ErrNoRows)
 				}).Return(nil, sql.ErrNoRows)
 
 
@@ -193,7 +192,7 @@ func TestDeleteThingTagHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil, nil)
 				}).Return(nil, nil)
 
 
@@ -202,7 +201,7 @@ func TestDeleteThingTagHandler(t *testing.T) {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				mock := mocks.NewThingRepositoryMock(mc)
 				mock := mocks.NewThingRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 				}).Return(nil, testError)
 				}).Return(nil, testError)
 
 
@@ -219,7 +218,7 @@ func TestDeleteThingTagHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil, nil)
 				}).Return(nil, nil)
 
 
@@ -228,7 +227,7 @@ func TestDeleteThingTagHandler(t *testing.T) {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				mock := mocks.NewThingRepositoryMock(mc)
 				mock := mocks.NewThingRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 				}).Return(nil, nil)
 				}).Return(nil, nil)
 
 
@@ -237,7 +236,7 @@ func TestDeleteThingTagHandler(t *testing.T) {
 			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
 			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
 				mock := mocks.NewThingTagRepositoryMock(mc)
 				mock := mocks.NewThingTagRepositoryMock(mc)
 
 
-				mock.DeleteMock.Inspect(func(ctx context.Context, req models.DeleteThingTagRequest, tx *sql.Tx) {
+				mock.DeleteMock.Inspect(func(ctx context.Context, req models.DeleteThingTagRequest) {
 					assert.Equal(mc, tagID, req.TagID)
 					assert.Equal(mc, tagID, req.TagID)
 					assert.Equal(mc, thingID, req.ThingID)
 					assert.Equal(mc, thingID, req.ThingID)
 				}).Return(testError)
 				}).Return(testError)
@@ -262,11 +261,11 @@ func TestDeleteThingTagHandler(t *testing.T) {
 
 
 			fiberReq := httptest.NewRequest(tt.req.method, tt.req.route, nil)
 			fiberReq := httptest.NewRequest(tt.req.method, tt.req.route, nil)
 			fiberReq.Header.Add(fiber.HeaderContentType, tt.req.contentType)
 			fiberReq.Header.Add(fiber.HeaderContentType, tt.req.contentType)
-			fiberRes, _ := fiberApp.Test(fiberReq, API.DefaultTestTimeOut)
+			fiberRes, _ := fiberApp.Test(fiberReq, test.TestTimeout)
 
 
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			if tt.resBody != nil {
 			if tt.resBody != nil {
-				assert.Equal(t, helpers.MarshalResponse(tt.resBody), helpers.ConvertBodyToString(fiberRes.Body))
+				assert.Equal(t, test.MarshalResponse(tt.resBody), test.ConvertBodyToString(fiberRes.Body))
 			}
 			}
 		})
 		})
 	}
 	}

+ 8 - 3
internal/api/v1/tag/get_tag.go

@@ -4,9 +4,11 @@ import (
 	"database/sql"
 	"database/sql"
 	"errors"
 	"errors"
 
 
+	"git.dmitriygnatenko.ru/dima/go-common/logger"
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2"
 
 
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/request"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 )
 )
 
 
@@ -24,21 +26,24 @@ import (
 func GetTagHandler(tagRepository TagRepository) fiber.Handler {
 func GetTagHandler(tagRepository TagRepository) fiber.Handler {
 	return func(fctx *fiber.Ctx) error {
 	return func(fctx *fiber.Ctx) error {
 		ctx := fctx.Context()
 		ctx := fctx.Context()
-		id, err := fctx.ParamsInt("tagId")
+		id, err := request.ConvertToUint64(fctx, "tagId")
 		if err != nil {
 		if err != nil {
+			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
 		res, err := tagRepository.Get(ctx, id)
 		res, err := tagRepository.Get(ctx, id)
 		if err != nil {
 		if err != nil {
 			if errors.Is(err, sql.ErrNoRows) {
 			if errors.Is(err, sql.ErrNoRows) {
+				logger.Info(ctx, err.Error())
 				return fiber.NewError(fiber.StatusNotFound, "")
 				return fiber.NewError(fiber.StatusNotFound, "")
 			}
 			}
 
 
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
-		res = helpers.ApplyLocation(fctx, res)
+		res = location.ApplyLocation(fctx, res)
 
 
 		return fctx.JSON(mappers.ToTagResponse(*res))
 		return fctx.JSON(mappers.ToTagResponse(*res))
 	}
 	}

+ 12 - 11
internal/api/v1/tag/get_tag_test.go

@@ -3,7 +3,6 @@ package tag
 import (
 import (
 	"context"
 	"context"
 	"database/sql"
 	"database/sql"
-	"errors"
 	"net/http/httptest"
 	"net/http/httptest"
 	"strconv"
 	"strconv"
 	"testing"
 	"testing"
@@ -13,10 +12,9 @@ import (
 	"github.com/gojuno/minimock/v3"
 	"github.com/gojuno/minimock/v3"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
 
 
-	API "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/tag/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/tag/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/test"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 )
 )
 
 
@@ -29,13 +27,13 @@ func TestGetTagHandler(t *testing.T) {
 	}
 	}
 
 
 	var (
 	var (
-		tagID     = gofakeit.Number(1, 1000)
-		testError = errors.New(gofakeit.Phrase())
+		tagID     = uint64(gofakeit.Number(1, 1000))
+		testError = gofakeit.Error()
 		layout    = "2006-01-02 15:04:05"
 		layout    = "2006-01-02 15:04:05"
 
 
 		correctReq = req{
 		correctReq = req{
 			method: fiber.MethodGet,
 			method: fiber.MethodGet,
-			route:  "/v1/tags/" + strconv.Itoa(tagID),
+			route:  "/v1/tags/" + strconv.FormatUint(tagID, 10),
 		}
 		}
 
 
 		tagRepoRes = models.Tag{
 		tagRepoRes = models.Tag{
@@ -70,7 +68,7 @@ func TestGetTagHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(&tagRepoRes, nil)
 				}).Return(&tagRepoRes, nil)
 
 
@@ -84,7 +82,7 @@ func TestGetTagHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil, sql.ErrNoRows)
 				}).Return(nil, sql.ErrNoRows)
 
 
@@ -98,7 +96,7 @@ func TestGetTagHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil, testError)
 				}).Return(nil, testError)
 
 
@@ -127,10 +125,13 @@ func TestGetTagHandler(t *testing.T) {
 			fiberApp := fiber.New()
 			fiberApp := fiber.New()
 			fiberApp.Get("/v1/tags/:tagId", GetTagHandler(tt.tagRepoMock(mc)))
 			fiberApp.Get("/v1/tags/:tagId", GetTagHandler(tt.tagRepoMock(mc)))
 
 
-			fiberRes, _ := fiberApp.Test(httptest.NewRequest(tt.req.method, tt.req.route, nil), API.DefaultTestTimeOut)
+			fiberRes, _ := fiberApp.Test(
+				httptest.NewRequest(tt.req.method, tt.req.route, nil),
+				test.TestTimeout,
+			)
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			if tt.resBody != nil {
 			if tt.resBody != nil {
-				assert.Equal(t, helpers.MarshalResponse(tt.resBody), helpers.ConvertBodyToString(fiberRes.Body))
+				assert.Equal(t, test.MarshalResponse(tt.resBody), test.ConvertBodyToString(fiberRes.Body))
 			}
 			}
 		})
 		})
 	}
 	}

+ 6 - 3
internal/api/v1/tag/get_tags.go

@@ -1,9 +1,10 @@
 package tag
 package tag
 
 
 import (
 import (
+	"git.dmitriygnatenko.ru/dima/go-common/logger"
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2"
 
 
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 )
 )
 
 
@@ -18,12 +19,14 @@ import (
 // @Produce     json
 // @Produce     json
 func GetTagsHandler(tagRepository TagRepository) fiber.Handler {
 func GetTagsHandler(tagRepository TagRepository) fiber.Handler {
 	return func(fctx *fiber.Ctx) error {
 	return func(fctx *fiber.Ctx) error {
-		res, err := tagRepository.GetAll(fctx.Context())
+		ctx := fctx.Context()
+		res, err := tagRepository.GetAll(ctx)
 		if err != nil {
 		if err != nil {
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
-		res = helpers.ApplyLocation(fctx, res)
+		res = location.ApplyLocation(fctx, res)
 
 
 		return fctx.JSON(mappers.ToTagsResponse(res))
 		return fctx.JSON(mappers.ToTagsResponse(res))
 	}
 	}

+ 9 - 8
internal/api/v1/tag/get_tags_test.go

@@ -1,7 +1,6 @@
 package tag
 package tag
 
 
 import (
 import (
-	"errors"
 	"net/http/httptest"
 	"net/http/httptest"
 	"testing"
 	"testing"
 
 
@@ -10,10 +9,9 @@ import (
 	"github.com/gojuno/minimock/v3"
 	"github.com/gojuno/minimock/v3"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
 
 
-	API "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/tag/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/tag/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/test"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 )
 )
 
 
@@ -26,7 +24,7 @@ func TestGetTagsHandler(t *testing.T) {
 	}
 	}
 
 
 	var (
 	var (
-		testError = errors.New(gofakeit.Phrase())
+		testError = gofakeit.Error()
 		layout    = "2006-01-02 15:04:05"
 		layout    = "2006-01-02 15:04:05"
 
 
 		correctReq = req{
 		correctReq = req{
@@ -36,14 +34,14 @@ func TestGetTagsHandler(t *testing.T) {
 
 
 		tagRepoRes = []models.Tag{
 		tagRepoRes = []models.Tag{
 			{
 			{
-				ID:        gofakeit.Number(1, 1000),
+				ID:        uint64(gofakeit.Number(1, 1000)),
 				Title:     "A" + gofakeit.Phrase(),
 				Title:     "A" + gofakeit.Phrase(),
 				Style:     gofakeit.Phrase(),
 				Style:     gofakeit.Phrase(),
 				CreatedAt: gofakeit.Date(),
 				CreatedAt: gofakeit.Date(),
 				UpdatedAt: gofakeit.Date(),
 				UpdatedAt: gofakeit.Date(),
 			},
 			},
 			{
 			{
-				ID:        gofakeit.Number(1, 1000),
+				ID:        uint64(gofakeit.Number(1, 1000)),
 				Title:     "B" + gofakeit.Phrase(),
 				Title:     "B" + gofakeit.Phrase(),
 				Style:     gofakeit.Phrase(),
 				Style:     gofakeit.Phrase(),
 				CreatedAt: gofakeit.Date(),
 				CreatedAt: gofakeit.Date(),
@@ -109,10 +107,13 @@ func TestGetTagsHandler(t *testing.T) {
 			fiberApp := fiber.New()
 			fiberApp := fiber.New()
 			fiberApp.Get("/v1/tags", GetTagsHandler(tt.tagRepoMock(mc)))
 			fiberApp.Get("/v1/tags", GetTagsHandler(tt.tagRepoMock(mc)))
 
 
-			fiberRes, _ := fiberApp.Test(httptest.NewRequest(tt.req.method, tt.req.route, nil), API.DefaultTestTimeOut)
+			fiberRes, _ := fiberApp.Test(
+				httptest.NewRequest(tt.req.method, tt.req.route, nil),
+				test.TestTimeout,
+			)
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			if tt.resBody != nil {
 			if tt.resBody != nil {
-				assert.Equal(t, helpers.MarshalResponse(tt.resBody), helpers.ConvertBodyToString(fiberRes.Body))
+				assert.Equal(t, test.MarshalResponse(tt.resBody), test.ConvertBodyToString(fiberRes.Body))
 			}
 			}
 		})
 		})
 	}
 	}

+ 8 - 3
internal/api/v1/tag/get_thing_tags.go

@@ -1,9 +1,11 @@
 package tag
 package tag
 
 
 import (
 import (
+	"git.dmitriygnatenko.ru/dima/go-common/logger"
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2"
 
 
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/request"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 )
 )
 
 
@@ -20,17 +22,20 @@ import (
 func GetThingTagsHandler(tagRepository TagRepository) fiber.Handler {
 func GetThingTagsHandler(tagRepository TagRepository) fiber.Handler {
 	return func(fctx *fiber.Ctx) error {
 	return func(fctx *fiber.Ctx) error {
 		ctx := fctx.Context()
 		ctx := fctx.Context()
-		id, err := fctx.ParamsInt("thingId")
+
+		id, err := request.ConvertToUint64(fctx, "thingId")
 		if err != nil {
 		if err != nil {
+			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
 		res, err := tagRepository.GetByThingID(ctx, id)
 		res, err := tagRepository.GetByThingID(ctx, id)
 		if err != nil {
 		if err != nil {
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
-		res = helpers.ApplyLocation(fctx, res)
+		res = location.ApplyLocation(fctx, res)
 
 
 		return fctx.JSON(mappers.ToTagsResponse(res))
 		return fctx.JSON(mappers.ToTagsResponse(res))
 	}
 	}

+ 13 - 12
internal/api/v1/tag/get_thing_tags_test.go

@@ -2,7 +2,6 @@ package tag
 
 
 import (
 import (
 	"context"
 	"context"
-	"errors"
 	"net/http/httptest"
 	"net/http/httptest"
 	"strconv"
 	"strconv"
 	"testing"
 	"testing"
@@ -12,10 +11,9 @@ import (
 	"github.com/gojuno/minimock/v3"
 	"github.com/gojuno/minimock/v3"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
 
 
-	API "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/tag/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/tag/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/test"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 )
 )
 
 
@@ -28,25 +26,25 @@ func TestGetThingTagsHandler(t *testing.T) {
 	}
 	}
 
 
 	var (
 	var (
-		thingID   = gofakeit.Number(1, 1000)
-		testError = errors.New(gofakeit.Phrase())
+		thingID   = uint64(gofakeit.Number(1, 1000))
+		testError = gofakeit.Error()
 		layout    = "2006-01-02 15:04:05"
 		layout    = "2006-01-02 15:04:05"
 
 
 		correctReq = req{
 		correctReq = req{
 			method: fiber.MethodGet,
 			method: fiber.MethodGet,
-			route:  "/v1/tags/thing/" + strconv.Itoa(thingID),
+			route:  "/v1/tags/thing/" + strconv.FormatUint(thingID, 10),
 		}
 		}
 
 
 		tagRepoRes = []models.Tag{
 		tagRepoRes = []models.Tag{
 			{
 			{
-				ID:        gofakeit.Number(1, 1000),
+				ID:        uint64(gofakeit.Number(1, 1000)),
 				Title:     "A" + gofakeit.Phrase(),
 				Title:     "A" + gofakeit.Phrase(),
 				Style:     gofakeit.Phrase(),
 				Style:     gofakeit.Phrase(),
 				CreatedAt: gofakeit.Date(),
 				CreatedAt: gofakeit.Date(),
 				UpdatedAt: gofakeit.Date(),
 				UpdatedAt: gofakeit.Date(),
 			},
 			},
 			{
 			{
-				ID:        gofakeit.Number(1, 1000),
+				ID:        uint64(gofakeit.Number(1, 1000)),
 				Title:     "B" + gofakeit.Phrase(),
 				Title:     "B" + gofakeit.Phrase(),
 				Style:     gofakeit.Phrase(),
 				Style:     gofakeit.Phrase(),
 				CreatedAt: gofakeit.Date(),
 				CreatedAt: gofakeit.Date(),
@@ -89,7 +87,7 @@ func TestGetThingTagsHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 				}).Return(tagRepoRes, nil)
 				}).Return(tagRepoRes, nil)
 
 
@@ -103,7 +101,7 @@ func TestGetThingTagsHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 				}).Return(nil, testError)
 				}).Return(nil, testError)
 
 
@@ -132,10 +130,13 @@ func TestGetThingTagsHandler(t *testing.T) {
 			fiberApp := fiber.New()
 			fiberApp := fiber.New()
 			fiberApp.Get("/v1/tags/thing/:thingId", GetThingTagsHandler(tt.tagRepoMock(mc)))
 			fiberApp.Get("/v1/tags/thing/:thingId", GetThingTagsHandler(tt.tagRepoMock(mc)))
 
 
-			fiberRes, _ := fiberApp.Test(httptest.NewRequest(tt.req.method, tt.req.route, nil), API.DefaultTestTimeOut)
+			fiberRes, _ := fiberApp.Test(
+				httptest.NewRequest(tt.req.method, tt.req.route, nil),
+				test.TestTimeout,
+			)
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			if tt.resBody != nil {
 			if tt.resBody != nil {
-				assert.Equal(t, helpers.MarshalResponse(tt.resBody), helpers.ConvertBodyToString(fiberRes.Body))
+				assert.Equal(t, test.MarshalResponse(tt.resBody), test.ConvertBodyToString(fiberRes.Body))
 			}
 			}
 		})
 		})
 	}
 	}

+ 393 - 0
internal/api/v1/tag/mocks/transaction_manager_minimock.go

@@ -0,0 +1,393 @@
+// Code generated by http://github.com/gojuno/minimock (v3.3.14). DO NOT EDIT.
+
+package mocks
+
+//go:generate minimock -i git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/tag.TransactionManager -o transaction_manager_minimock.go -n TransactionManagerMock -p mocks
+
+import (
+	"context"
+	"sync"
+	mm_atomic "sync/atomic"
+	mm_time "time"
+
+	"github.com/gojuno/minimock/v3"
+)
+
+// TransactionManagerMock implements tag.TransactionManager
+type TransactionManagerMock struct {
+	t          minimock.Tester
+	finishOnce sync.Once
+
+	funcReadCommitted          func(ctx context.Context, f1 func(ctx context.Context) error) (err error)
+	inspectFuncReadCommitted   func(ctx context.Context, f1 func(ctx context.Context) error)
+	afterReadCommittedCounter  uint64
+	beforeReadCommittedCounter uint64
+	ReadCommittedMock          mTransactionManagerMockReadCommitted
+}
+
+// NewTransactionManagerMock returns a mock for tag.TransactionManager
+func NewTransactionManagerMock(t minimock.Tester) *TransactionManagerMock {
+	m := &TransactionManagerMock{t: t}
+
+	if controller, ok := t.(minimock.MockController); ok {
+		controller.RegisterMocker(m)
+	}
+
+	m.ReadCommittedMock = mTransactionManagerMockReadCommitted{mock: m}
+	m.ReadCommittedMock.callArgs = []*TransactionManagerMockReadCommittedParams{}
+
+	t.Cleanup(m.MinimockFinish)
+
+	return m
+}
+
+type mTransactionManagerMockReadCommitted struct {
+	optional           bool
+	mock               *TransactionManagerMock
+	defaultExpectation *TransactionManagerMockReadCommittedExpectation
+	expectations       []*TransactionManagerMockReadCommittedExpectation
+
+	callArgs []*TransactionManagerMockReadCommittedParams
+	mutex    sync.RWMutex
+
+	expectedInvocations uint64
+}
+
+// TransactionManagerMockReadCommittedExpectation specifies expectation struct of the TransactionManager.ReadCommitted
+type TransactionManagerMockReadCommittedExpectation struct {
+	mock      *TransactionManagerMock
+	params    *TransactionManagerMockReadCommittedParams
+	paramPtrs *TransactionManagerMockReadCommittedParamPtrs
+	results   *TransactionManagerMockReadCommittedResults
+	Counter   uint64
+}
+
+// TransactionManagerMockReadCommittedParams contains parameters of the TransactionManager.ReadCommitted
+type TransactionManagerMockReadCommittedParams struct {
+	ctx context.Context
+	f1  func(ctx context.Context) error
+}
+
+// TransactionManagerMockReadCommittedParamPtrs contains pointers to parameters of the TransactionManager.ReadCommitted
+type TransactionManagerMockReadCommittedParamPtrs struct {
+	ctx *context.Context
+	f1  *func(ctx context.Context) error
+}
+
+// TransactionManagerMockReadCommittedResults contains results of the TransactionManager.ReadCommitted
+type TransactionManagerMockReadCommittedResults struct {
+	err error
+}
+
+// Marks this method to be optional. The default behavior of any method with Return() is '1 or more', meaning
+// the test will fail minimock's automatic final call check if the mocked method was not called at least once.
+// Optional() makes method check to work in '0 or more' mode.
+// It is NOT RECOMMENDED to use this option unless you really need it, as default behaviour helps to
+// catch the problems when the expected method call is totally skipped during test run.
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) Optional() *mTransactionManagerMockReadCommitted {
+	mmReadCommitted.optional = true
+	return mmReadCommitted
+}
+
+// Expect sets up expected params for TransactionManager.ReadCommitted
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) Expect(ctx context.Context, f1 func(ctx context.Context) error) *mTransactionManagerMockReadCommitted {
+	if mmReadCommitted.mock.funcReadCommitted != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by Set")
+	}
+
+	if mmReadCommitted.defaultExpectation == nil {
+		mmReadCommitted.defaultExpectation = &TransactionManagerMockReadCommittedExpectation{}
+	}
+
+	if mmReadCommitted.defaultExpectation.paramPtrs != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by ExpectParams functions")
+	}
+
+	mmReadCommitted.defaultExpectation.params = &TransactionManagerMockReadCommittedParams{ctx, f1}
+	for _, e := range mmReadCommitted.expectations {
+		if minimock.Equal(e.params, mmReadCommitted.defaultExpectation.params) {
+			mmReadCommitted.mock.t.Fatalf("Expectation set by When has same params: %#v", *mmReadCommitted.defaultExpectation.params)
+		}
+	}
+
+	return mmReadCommitted
+}
+
+// ExpectCtxParam1 sets up expected param ctx for TransactionManager.ReadCommitted
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) ExpectCtxParam1(ctx context.Context) *mTransactionManagerMockReadCommitted {
+	if mmReadCommitted.mock.funcReadCommitted != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by Set")
+	}
+
+	if mmReadCommitted.defaultExpectation == nil {
+		mmReadCommitted.defaultExpectation = &TransactionManagerMockReadCommittedExpectation{}
+	}
+
+	if mmReadCommitted.defaultExpectation.params != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by Expect")
+	}
+
+	if mmReadCommitted.defaultExpectation.paramPtrs == nil {
+		mmReadCommitted.defaultExpectation.paramPtrs = &TransactionManagerMockReadCommittedParamPtrs{}
+	}
+	mmReadCommitted.defaultExpectation.paramPtrs.ctx = &ctx
+
+	return mmReadCommitted
+}
+
+// ExpectF1Param2 sets up expected param f1 for TransactionManager.ReadCommitted
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) ExpectF1Param2(f1 func(ctx context.Context) error) *mTransactionManagerMockReadCommitted {
+	if mmReadCommitted.mock.funcReadCommitted != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by Set")
+	}
+
+	if mmReadCommitted.defaultExpectation == nil {
+		mmReadCommitted.defaultExpectation = &TransactionManagerMockReadCommittedExpectation{}
+	}
+
+	if mmReadCommitted.defaultExpectation.params != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by Expect")
+	}
+
+	if mmReadCommitted.defaultExpectation.paramPtrs == nil {
+		mmReadCommitted.defaultExpectation.paramPtrs = &TransactionManagerMockReadCommittedParamPtrs{}
+	}
+	mmReadCommitted.defaultExpectation.paramPtrs.f1 = &f1
+
+	return mmReadCommitted
+}
+
+// Inspect accepts an inspector function that has same arguments as the TransactionManager.ReadCommitted
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) Inspect(f func(ctx context.Context, f1 func(ctx context.Context) error)) *mTransactionManagerMockReadCommitted {
+	if mmReadCommitted.mock.inspectFuncReadCommitted != nil {
+		mmReadCommitted.mock.t.Fatalf("Inspect function is already set for TransactionManagerMock.ReadCommitted")
+	}
+
+	mmReadCommitted.mock.inspectFuncReadCommitted = f
+
+	return mmReadCommitted
+}
+
+// Return sets up results that will be returned by TransactionManager.ReadCommitted
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) Return(err error) *TransactionManagerMock {
+	if mmReadCommitted.mock.funcReadCommitted != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by Set")
+	}
+
+	if mmReadCommitted.defaultExpectation == nil {
+		mmReadCommitted.defaultExpectation = &TransactionManagerMockReadCommittedExpectation{mock: mmReadCommitted.mock}
+	}
+	mmReadCommitted.defaultExpectation.results = &TransactionManagerMockReadCommittedResults{err}
+	return mmReadCommitted.mock
+}
+
+// Set uses given function f to mock the TransactionManager.ReadCommitted method
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) Set(f func(ctx context.Context, f1 func(ctx context.Context) error) (err error)) *TransactionManagerMock {
+	if mmReadCommitted.defaultExpectation != nil {
+		mmReadCommitted.mock.t.Fatalf("Default expectation is already set for the TransactionManager.ReadCommitted method")
+	}
+
+	if len(mmReadCommitted.expectations) > 0 {
+		mmReadCommitted.mock.t.Fatalf("Some expectations are already set for the TransactionManager.ReadCommitted method")
+	}
+
+	mmReadCommitted.mock.funcReadCommitted = f
+	return mmReadCommitted.mock
+}
+
+// When sets expectation for the TransactionManager.ReadCommitted which will trigger the result defined by the following
+// Then helper
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) When(ctx context.Context, f1 func(ctx context.Context) error) *TransactionManagerMockReadCommittedExpectation {
+	if mmReadCommitted.mock.funcReadCommitted != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by Set")
+	}
+
+	expectation := &TransactionManagerMockReadCommittedExpectation{
+		mock:   mmReadCommitted.mock,
+		params: &TransactionManagerMockReadCommittedParams{ctx, f1},
+	}
+	mmReadCommitted.expectations = append(mmReadCommitted.expectations, expectation)
+	return expectation
+}
+
+// Then sets up TransactionManager.ReadCommitted return parameters for the expectation previously defined by the When method
+func (e *TransactionManagerMockReadCommittedExpectation) Then(err error) *TransactionManagerMock {
+	e.results = &TransactionManagerMockReadCommittedResults{err}
+	return e.mock
+}
+
+// Times sets number of times TransactionManager.ReadCommitted should be invoked
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) Times(n uint64) *mTransactionManagerMockReadCommitted {
+	if n == 0 {
+		mmReadCommitted.mock.t.Fatalf("Times of TransactionManagerMock.ReadCommitted mock can not be zero")
+	}
+	mm_atomic.StoreUint64(&mmReadCommitted.expectedInvocations, n)
+	return mmReadCommitted
+}
+
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) invocationsDone() bool {
+	if len(mmReadCommitted.expectations) == 0 && mmReadCommitted.defaultExpectation == nil && mmReadCommitted.mock.funcReadCommitted == nil {
+		return true
+	}
+
+	totalInvocations := mm_atomic.LoadUint64(&mmReadCommitted.mock.afterReadCommittedCounter)
+	expectedInvocations := mm_atomic.LoadUint64(&mmReadCommitted.expectedInvocations)
+
+	return totalInvocations > 0 && (expectedInvocations == 0 || expectedInvocations == totalInvocations)
+}
+
+// ReadCommitted implements tag.TransactionManager
+func (mmReadCommitted *TransactionManagerMock) ReadCommitted(ctx context.Context, f1 func(ctx context.Context) error) (err error) {
+	mm_atomic.AddUint64(&mmReadCommitted.beforeReadCommittedCounter, 1)
+	defer mm_atomic.AddUint64(&mmReadCommitted.afterReadCommittedCounter, 1)
+
+	if mmReadCommitted.inspectFuncReadCommitted != nil {
+		mmReadCommitted.inspectFuncReadCommitted(ctx, f1)
+	}
+
+	mm_params := TransactionManagerMockReadCommittedParams{ctx, f1}
+
+	// Record call args
+	mmReadCommitted.ReadCommittedMock.mutex.Lock()
+	mmReadCommitted.ReadCommittedMock.callArgs = append(mmReadCommitted.ReadCommittedMock.callArgs, &mm_params)
+	mmReadCommitted.ReadCommittedMock.mutex.Unlock()
+
+	for _, e := range mmReadCommitted.ReadCommittedMock.expectations {
+		if minimock.Equal(*e.params, mm_params) {
+			mm_atomic.AddUint64(&e.Counter, 1)
+			return e.results.err
+		}
+	}
+
+	if mmReadCommitted.ReadCommittedMock.defaultExpectation != nil {
+		mm_atomic.AddUint64(&mmReadCommitted.ReadCommittedMock.defaultExpectation.Counter, 1)
+		mm_want := mmReadCommitted.ReadCommittedMock.defaultExpectation.params
+		mm_want_ptrs := mmReadCommitted.ReadCommittedMock.defaultExpectation.paramPtrs
+
+		mm_got := TransactionManagerMockReadCommittedParams{ctx, f1}
+
+		if mm_want_ptrs != nil {
+
+			if mm_want_ptrs.ctx != nil && !minimock.Equal(*mm_want_ptrs.ctx, mm_got.ctx) {
+				mmReadCommitted.t.Errorf("TransactionManagerMock.ReadCommitted got unexpected parameter ctx, want: %#v, got: %#v%s\n", *mm_want_ptrs.ctx, mm_got.ctx, minimock.Diff(*mm_want_ptrs.ctx, mm_got.ctx))
+			}
+
+			if mm_want_ptrs.f1 != nil && !minimock.Equal(*mm_want_ptrs.f1, mm_got.f1) {
+				mmReadCommitted.t.Errorf("TransactionManagerMock.ReadCommitted got unexpected parameter f1, want: %#v, got: %#v%s\n", *mm_want_ptrs.f1, mm_got.f1, minimock.Diff(*mm_want_ptrs.f1, mm_got.f1))
+			}
+
+		} else if mm_want != nil && !minimock.Equal(*mm_want, mm_got) {
+			mmReadCommitted.t.Errorf("TransactionManagerMock.ReadCommitted got unexpected parameters, want: %#v, got: %#v%s\n", *mm_want, mm_got, minimock.Diff(*mm_want, mm_got))
+		}
+
+		mm_results := mmReadCommitted.ReadCommittedMock.defaultExpectation.results
+		if mm_results == nil {
+			mmReadCommitted.t.Fatal("No results are set for the TransactionManagerMock.ReadCommitted")
+		}
+		return (*mm_results).err
+	}
+	if mmReadCommitted.funcReadCommitted != nil {
+		return mmReadCommitted.funcReadCommitted(ctx, f1)
+	}
+	mmReadCommitted.t.Fatalf("Unexpected call to TransactionManagerMock.ReadCommitted. %v %v", ctx, f1)
+	return
+}
+
+// ReadCommittedAfterCounter returns a count of finished TransactionManagerMock.ReadCommitted invocations
+func (mmReadCommitted *TransactionManagerMock) ReadCommittedAfterCounter() uint64 {
+	return mm_atomic.LoadUint64(&mmReadCommitted.afterReadCommittedCounter)
+}
+
+// ReadCommittedBeforeCounter returns a count of TransactionManagerMock.ReadCommitted invocations
+func (mmReadCommitted *TransactionManagerMock) ReadCommittedBeforeCounter() uint64 {
+	return mm_atomic.LoadUint64(&mmReadCommitted.beforeReadCommittedCounter)
+}
+
+// Calls returns a list of arguments used in each call to TransactionManagerMock.ReadCommitted.
+// The list is in the same order as the calls were made (i.e. recent calls have a higher index)
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) Calls() []*TransactionManagerMockReadCommittedParams {
+	mmReadCommitted.mutex.RLock()
+
+	argCopy := make([]*TransactionManagerMockReadCommittedParams, len(mmReadCommitted.callArgs))
+	copy(argCopy, mmReadCommitted.callArgs)
+
+	mmReadCommitted.mutex.RUnlock()
+
+	return argCopy
+}
+
+// MinimockReadCommittedDone returns true if the count of the ReadCommitted invocations corresponds
+// the number of defined expectations
+func (m *TransactionManagerMock) MinimockReadCommittedDone() bool {
+	if m.ReadCommittedMock.optional {
+		// Optional methods provide '0 or more' call count restriction.
+		return true
+	}
+
+	for _, e := range m.ReadCommittedMock.expectations {
+		if mm_atomic.LoadUint64(&e.Counter) < 1 {
+			return false
+		}
+	}
+
+	return m.ReadCommittedMock.invocationsDone()
+}
+
+// MinimockReadCommittedInspect logs each unmet expectation
+func (m *TransactionManagerMock) MinimockReadCommittedInspect() {
+	for _, e := range m.ReadCommittedMock.expectations {
+		if mm_atomic.LoadUint64(&e.Counter) < 1 {
+			m.t.Errorf("Expected call to TransactionManagerMock.ReadCommitted with params: %#v", *e.params)
+		}
+	}
+
+	afterReadCommittedCounter := mm_atomic.LoadUint64(&m.afterReadCommittedCounter)
+	// if default expectation was set then invocations count should be greater than zero
+	if m.ReadCommittedMock.defaultExpectation != nil && afterReadCommittedCounter < 1 {
+		if m.ReadCommittedMock.defaultExpectation.params == nil {
+			m.t.Error("Expected call to TransactionManagerMock.ReadCommitted")
+		} else {
+			m.t.Errorf("Expected call to TransactionManagerMock.ReadCommitted with params: %#v", *m.ReadCommittedMock.defaultExpectation.params)
+		}
+	}
+	// if func was set then invocations count should be greater than zero
+	if m.funcReadCommitted != nil && afterReadCommittedCounter < 1 {
+		m.t.Error("Expected call to TransactionManagerMock.ReadCommitted")
+	}
+
+	if !m.ReadCommittedMock.invocationsDone() && afterReadCommittedCounter > 0 {
+		m.t.Errorf("Expected %d calls to TransactionManagerMock.ReadCommitted but found %d calls",
+			mm_atomic.LoadUint64(&m.ReadCommittedMock.expectedInvocations), afterReadCommittedCounter)
+	}
+}
+
+// MinimockFinish checks that all mocked methods have been called the expected number of times
+func (m *TransactionManagerMock) MinimockFinish() {
+	m.finishOnce.Do(func() {
+		if !m.minimockDone() {
+			m.MinimockReadCommittedInspect()
+		}
+	})
+}
+
+// MinimockWait waits for all mocked methods to be called the expected number of times
+func (m *TransactionManagerMock) MinimockWait(timeout mm_time.Duration) {
+	timeoutCh := mm_time.After(timeout)
+	for {
+		if m.minimockDone() {
+			return
+		}
+		select {
+		case <-timeoutCh:
+			m.MinimockFinish()
+			return
+		case <-mm_time.After(10 * mm_time.Millisecond):
+		}
+	}
+}
+
+func (m *TransactionManagerMock) minimockDone() bool {
+	done := true
+	return done &&
+		m.MinimockReadCommittedDone()
+}

+ 11 - 4
internal/api/v1/tag/update_tag.go

@@ -1,12 +1,14 @@
 package tag
 package tag
 
 
 import (
 import (
+	"git.dmitriygnatenko.ru/dima/go-common/logger"
 	"github.com/go-playground/validator/v10"
 	"github.com/go-playground/validator/v10"
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2"
 
 
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/request"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 )
 )
 
 
@@ -24,32 +26,37 @@ import (
 func UpdateTagHandler(tagRepository TagRepository) fiber.Handler {
 func UpdateTagHandler(tagRepository TagRepository) fiber.Handler {
 	return func(fctx *fiber.Ctx) error {
 	return func(fctx *fiber.Ctx) error {
 		ctx := fctx.Context()
 		ctx := fctx.Context()
-		id, err := fctx.ParamsInt("tagId")
+		id, err := request.ConvertToUint64(fctx, "tagId")
 		if err != nil {
 		if err != nil {
+			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
 		req := dto.UpdateTagRequest{}
 		req := dto.UpdateTagRequest{}
 		if err = fctx.BodyParser(&req); err != nil {
 		if err = fctx.BodyParser(&req); err != nil {
+			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
 		var validate = validator.New()
 		var validate = validator.New()
 		if err = validate.Struct(req); err != nil {
 		if err = validate.Struct(req); err != nil {
+			logger.Info(ctx, err.Error())
 			return fctx.Status(fiber.StatusBadRequest).JSON(factory.CreateValidateErrorResponse(err))
 			return fctx.Status(fiber.StatusBadRequest).JSON(factory.CreateValidateErrorResponse(err))
 		}
 		}
 
 
-		err = tagRepository.Update(ctx, mappers.ToUpdateTagRequest(id, req), nil)
+		err = tagRepository.Update(ctx, mappers.ToUpdateTagRequest(id, req))
 		if err != nil {
 		if err != nil {
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
 		res, err := tagRepository.Get(ctx, id)
 		res, err := tagRepository.Get(ctx, id)
 		if err != nil {
 		if err != nil {
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
-		res = helpers.ApplyLocation(fctx, res)
+		res = location.ApplyLocation(fctx, res)
 
 
 		return fctx.JSON(mappers.ToTagResponse(*res))
 		return fctx.JSON(mappers.ToTagResponse(*res))
 	}
 	}

+ 15 - 18
internal/api/v1/tag/update_tag_test.go

@@ -2,8 +2,6 @@ package tag
 
 
 import (
 import (
 	"context"
 	"context"
-	"database/sql"
-	"errors"
 	"net/http/httptest"
 	"net/http/httptest"
 	"strconv"
 	"strconv"
 	"testing"
 	"testing"
@@ -13,10 +11,9 @@ import (
 	"github.com/gojuno/minimock/v3"
 	"github.com/gojuno/minimock/v3"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
 
 
-	API "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/tag/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/tag/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/test"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 )
 )
 
 
@@ -31,15 +28,15 @@ func TestUpdateTagHandler(t *testing.T) {
 	}
 	}
 
 
 	var (
 	var (
-		tagID     = gofakeit.Number(1, 1000)
+		tagID     = uint64(gofakeit.Number(1, 1000))
 		title     = gofakeit.Phrase()
 		title     = gofakeit.Phrase()
 		style     = gofakeit.Phrase()
 		style     = gofakeit.Phrase()
-		testError = errors.New(gofakeit.Phrase())
+		testError = gofakeit.Error()
 		layout    = "2006-01-02 15:04:05"
 		layout    = "2006-01-02 15:04:05"
 
 
 		correctReq = req{
 		correctReq = req{
 			method: fiber.MethodPut,
 			method: fiber.MethodPut,
-			route:  "/v1/tags/" + strconv.Itoa(tagID),
+			route:  "/v1/tags/" + strconv.FormatUint(tagID, 10),
 			body: &dto.UpdateTagRequest{
 			body: &dto.UpdateTagRequest{
 				Title: title,
 				Title: title,
 				Style: style,
 				Style: style,
@@ -79,13 +76,13 @@ func TestUpdateTagHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.UpdateMock.Inspect(func(ctx context.Context, req models.UpdateTagRequest, tx *sql.Tx) {
+				mock.UpdateMock.Inspect(func(ctx context.Context, req models.UpdateTagRequest) {
 					assert.Equal(mc, tagID, req.ID)
 					assert.Equal(mc, tagID, req.ID)
 					assert.Equal(mc, title, req.Title)
 					assert.Equal(mc, title, req.Title)
 					assert.Equal(mc, style, req.Style)
 					assert.Equal(mc, style, req.Style)
 				}).Return(nil)
 				}).Return(nil)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(&repoRes, nil)
 				}).Return(&repoRes, nil)
 
 
@@ -107,7 +104,7 @@ func TestUpdateTagHandler(t *testing.T) {
 			name: "negative case - body parse error",
 			name: "negative case - body parse error",
 			req: req{
 			req: req{
 				method: fiber.MethodPut,
 				method: fiber.MethodPut,
-				route:  "/v1/tags/" + strconv.Itoa(tagID),
+				route:  "/v1/tags/" + strconv.FormatUint(tagID, 10),
 			},
 			},
 			resCode: fiber.StatusBadRequest,
 			resCode: fiber.StatusBadRequest,
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
@@ -118,7 +115,7 @@ func TestUpdateTagHandler(t *testing.T) {
 			name: "negative case - request without title",
 			name: "negative case - request without title",
 			req: req{
 			req: req{
 				method:      fiber.MethodPut,
 				method:      fiber.MethodPut,
-				route:       "/v1/tags/" + strconv.Itoa(tagID),
+				route:       "/v1/tags/" + strconv.FormatUint(tagID, 10),
 				contentType: fiber.MIMEApplicationJSON,
 				contentType: fiber.MIMEApplicationJSON,
 				body: &dto.UpdateTagRequest{
 				body: &dto.UpdateTagRequest{
 					Style: style,
 					Style: style,
@@ -139,7 +136,7 @@ func TestUpdateTagHandler(t *testing.T) {
 			name: "negative case - request without style",
 			name: "negative case - request without style",
 			req: req{
 			req: req{
 				method:      fiber.MethodPut,
 				method:      fiber.MethodPut,
-				route:       "/v1/tags/" + strconv.Itoa(tagID),
+				route:       "/v1/tags/" + strconv.FormatUint(tagID, 10),
 				contentType: fiber.MIMEApplicationJSON,
 				contentType: fiber.MIMEApplicationJSON,
 				body: &dto.UpdateTagRequest{
 				body: &dto.UpdateTagRequest{
 					Title: title,
 					Title: title,
@@ -163,7 +160,7 @@ func TestUpdateTagHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.UpdateMock.Inspect(func(ctx context.Context, req models.UpdateTagRequest, tx *sql.Tx) {
+				mock.UpdateMock.Inspect(func(ctx context.Context, req models.UpdateTagRequest) {
 					assert.Equal(mc, tagID, req.ID)
 					assert.Equal(mc, tagID, req.ID)
 					assert.Equal(mc, title, req.Title)
 					assert.Equal(mc, title, req.Title)
 					assert.Equal(mc, style, req.Style)
 					assert.Equal(mc, style, req.Style)
@@ -179,13 +176,13 @@ func TestUpdateTagHandler(t *testing.T) {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 			tagRepoMock: func(mc *minimock.Controller) TagRepository {
 				mock := mocks.NewTagRepositoryMock(mc)
 				mock := mocks.NewTagRepositoryMock(mc)
 
 
-				mock.UpdateMock.Inspect(func(ctx context.Context, req models.UpdateTagRequest, tx *sql.Tx) {
+				mock.UpdateMock.Inspect(func(ctx context.Context, req models.UpdateTagRequest) {
 					assert.Equal(mc, tagID, req.ID)
 					assert.Equal(mc, tagID, req.ID)
 					assert.Equal(mc, title, req.Title)
 					assert.Equal(mc, title, req.Title)
 					assert.Equal(mc, style, req.Style)
 					assert.Equal(mc, style, req.Style)
 				}).Return(nil)
 				}).Return(nil)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, tagID, id)
 					assert.Equal(mc, tagID, id)
 				}).Return(nil, testError)
 				}).Return(nil, testError)
 
 
@@ -202,13 +199,13 @@ func TestUpdateTagHandler(t *testing.T) {
 			fiberApp := fiber.New()
 			fiberApp := fiber.New()
 			fiberApp.Put("/v1/tags/:tagId", UpdateTagHandler(tt.tagRepoMock(mc)))
 			fiberApp.Put("/v1/tags/:tagId", UpdateTagHandler(tt.tagRepoMock(mc)))
 
 
-			fiberReq := httptest.NewRequest(tt.req.method, tt.req.route, helpers.ConvertDataToIOReader(tt.req.body))
+			fiberReq := httptest.NewRequest(tt.req.method, tt.req.route, test.ConvertDataToIOReader(tt.req.body))
 			fiberReq.Header.Add(fiber.HeaderContentType, tt.req.contentType)
 			fiberReq.Header.Add(fiber.HeaderContentType, tt.req.contentType)
-			fiberRes, _ := fiberApp.Test(fiberReq, API.DefaultTestTimeOut)
+			fiberRes, _ := fiberApp.Test(fiberReq, test.TestTimeout)
 
 
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			if tt.resBody != nil {
 			if tt.resBody != nil {
-				assert.Equal(t, helpers.MarshalResponse(tt.resBody), helpers.ConvertBodyToString(fiberRes.Body))
+				assert.Equal(t, test.MarshalResponse(tt.resBody), test.ConvertBodyToString(fiberRes.Body))
 			}
 			}
 		})
 		})
 	}
 	}

+ 28 - 17
internal/api/v1/thing/add_thing.go

@@ -2,15 +2,16 @@ package thing
 
 
 //go:generate mkdir -p mocks
 //go:generate mkdir -p mocks
 //go:generate rm -rf ./mocks/*_minimock.go
 //go:generate rm -rf ./mocks/*_minimock.go
-//go:generate minimock -i ThingRepository,PlaceThingRepository,ThingTagRepository,ThingImageRepository,ThingNotificationRepository,FileRepository -o ./mocks/ -s "_minimock.go"
+//go:generate minimock -i ThingRepository,PlaceThingRepository,ThingTagRepository,ThingImageRepository,ThingNotificationRepository,FileRepository,TransactionManager -o ./mocks/ -s "_minimock.go"
 
 
 import (
 import (
 	"context"
 	"context"
 
 
-	API "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1"
+	"git.dmitriygnatenko.ru/dima/go-common/logger"
+
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 
 
@@ -19,6 +20,10 @@ import (
 )
 )
 
 
 type (
 type (
+	TransactionManager interface {
+		ReadCommitted(context.Context, func(ctx context.Context) error) error
+	}
+
 	ThingRepository interface {
 	ThingRepository interface {
 		Get(ctx context.Context, id uint64) (*models.Thing, error)
 		Get(ctx context.Context, id uint64) (*models.Thing, error)
 		Search(ctx context.Context, search string) ([]models.Thing, error)
 		Search(ctx context.Context, search string) ([]models.Thing, error)
@@ -66,6 +71,7 @@ type (
 // @Accept      json
 // @Accept      json
 // @Produce     json
 // @Produce     json
 func AddThingHandler(
 func AddThingHandler(
+	tm TransactionManager,
 	thingRepository ThingRepository,
 	thingRepository ThingRepository,
 	placeThingRepository PlaceThingRepository,
 	placeThingRepository PlaceThingRepository,
 ) fiber.Handler {
 ) fiber.Handler {
@@ -73,39 +79,44 @@ func AddThingHandler(
 		ctx := fctx.Context()
 		ctx := fctx.Context()
 		req := dto.AddThingRequest{}
 		req := dto.AddThingRequest{}
 		if err := fctx.BodyParser(&req); err != nil {
 		if err := fctx.BodyParser(&req); err != nil {
+			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
 		var validate = validator.New()
 		var validate = validator.New()
 		if err := validate.Struct(req); err != nil {
 		if err := validate.Struct(req); err != nil {
+			logger.Info(ctx, err.Error())
 			return fctx.Status(fiber.StatusBadRequest).JSON(factory.CreateValidateErrorResponse(err))
 			return fctx.Status(fiber.StatusBadRequest).JSON(factory.CreateValidateErrorResponse(err))
 		}
 		}
 
 
-		tx, err := thingRepository.BeginTx(ctx, API.DefaultTxLevel)
-		if err != nil {
-			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
-		}
+		var id uint64
 
 
-		id, err := thingRepository.Add(ctx, mappers.ToAddThingRequest(req), tx)
-		if err != nil {
-			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
-		}
+		err := tm.ReadCommitted(ctx, func(ctx context.Context) error {
+			id, txErr := thingRepository.Add(ctx, mappers.ToAddThingRequest(req))
+			if txErr != nil {
+				return txErr
+			}
 
 
-		err = placeThingRepository.Add(ctx, mappers.ToAddPlaceThingRequest(id, req.PlaceID), tx)
-		if err != nil {
-			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
-		}
+			txErr = placeThingRepository.Add(ctx, mappers.ToAddPlaceThingRequest(id, req.PlaceID))
+			if txErr != nil {
+				return txErr
+			}
+
+			return nil
+		})
 
 
-		if err = thingRepository.CommitTx(tx); err != nil {
+		if err != nil {
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
 		res, err := thingRepository.Get(ctx, id)
 		res, err := thingRepository.Get(ctx, id)
 		if err != nil {
 		if err != nil {
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
-		res = helpers.ApplyLocation(fctx, res)
+		res = location.ApplyLocation(fctx, res)
 
 
 		return fctx.JSON(mappers.ToThingResponse(*res))
 		return fctx.JSON(mappers.ToThingResponse(*res))
 	}
 	}

+ 1 - 2
internal/api/v1/thing/add_thing_test.go

@@ -3,7 +3,6 @@ package thing
 import (
 import (
 	"context"
 	"context"
 	"database/sql"
 	"database/sql"
-	"errors"
 	"net/http/httptest"
 	"net/http/httptest"
 	"testing"
 	"testing"
 
 
@@ -34,7 +33,7 @@ func TestAddThingHandler(t *testing.T) {
 		thingID     = gofakeit.Number(1, 1000)
 		thingID     = gofakeit.Number(1, 1000)
 		title       = gofakeit.Phrase()
 		title       = gofakeit.Phrase()
 		description = gofakeit.Phrase()
 		description = gofakeit.Phrase()
-		testError   = errors.New(gofakeit.Phrase())
+		testError   = gofakeit.Error()
 		layout      = "2006-01-02 15:04:05"
 		layout      = "2006-01-02 15:04:05"
 
 
 		correctReq = req{
 		correctReq = req{

+ 43 - 36
internal/api/v1/thing/delete_thing.go

@@ -1,13 +1,15 @@
 package thing
 package thing
 
 
 import (
 import (
+	"context"
 	"database/sql"
 	"database/sql"
 	"errors"
 	"errors"
 
 
+	"git.dmitriygnatenko.ru/dima/go-common/logger"
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2"
 
 
-	API "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/request"
 )
 )
 
 
 // @Router 		/api/v1/things/{thingId} [delete]
 // @Router 		/api/v1/things/{thingId} [delete]
@@ -21,6 +23,7 @@ import (
 // @Accept      json
 // @Accept      json
 // @Produce     json
 // @Produce     json
 func DeleteThingHandler(
 func DeleteThingHandler(
+	tm TransactionManager,
 	thingRepository ThingRepository,
 	thingRepository ThingRepository,
 	thingTagRepository ThingTagRepository,
 	thingTagRepository ThingTagRepository,
 	placeThingRepository PlaceThingRepository,
 	placeThingRepository PlaceThingRepository,
@@ -30,63 +33,67 @@ func DeleteThingHandler(
 ) fiber.Handler {
 ) fiber.Handler {
 	return func(fctx *fiber.Ctx) error {
 	return func(fctx *fiber.Ctx) error {
 		ctx := fctx.Context()
 		ctx := fctx.Context()
-		id, err := fctx.ParamsInt("thingId")
+		id, err := request.ConvertToUint64(fctx, "thingId")
 		if err != nil {
 		if err != nil {
+			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
 		if _, err = thingRepository.Get(ctx, id); err != nil {
 		if _, err = thingRepository.Get(ctx, id); err != nil {
 			if errors.Is(err, sql.ErrNoRows) {
 			if errors.Is(err, sql.ErrNoRows) {
+				logger.Info(ctx, err.Error())
 				return fiber.NewError(fiber.StatusBadRequest, "")
 				return fiber.NewError(fiber.StatusBadRequest, "")
 			}
 			}
-			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
-		}
 
 
-		tx, err := thingRepository.BeginTx(ctx, API.DefaultTxLevel)
-		if err != nil {
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
-		if err = placeThingRepository.DeleteThing(ctx, id, tx); err != nil {
-			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
-		}
+		err = tm.ReadCommitted(ctx, func(ctx context.Context) error {
+			if txErr := placeThingRepository.DeleteThing(ctx, id); txErr != nil {
+				return txErr
+			}
 
 
-		images, err := thingImageRepository.GetByThingID(ctx, id)
-		if err != nil {
-			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
-		}
+			images, txErr := thingImageRepository.GetByThingID(ctx, id)
+			if txErr != nil {
+				return txErr
+			}
 
 
-		imageURLs := make([]string, 0, len(images))
-		for i := range images {
-			imageURLs = append(imageURLs, images[i].Image)
+			imageURLs := make([]string, 0, len(images))
+			for i := range images {
+				imageURLs = append(imageURLs, images[i].Image)
 
 
-			if err = thingImageRepository.Delete(ctx, images[i].ID, tx); err != nil {
-				return fiber.NewError(fiber.StatusInternalServerError, err.Error())
+				if txErr = thingImageRepository.Delete(ctx, images[i].ID); txErr != nil {
+					return txErr
+				}
 			}
 			}
-		}
-
-		if err = thingTagRepository.DeleteByThingID(ctx, id, tx); err != nil {
-			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
-		}
 
 
-		if err = thingNotificationRepository.Delete(ctx, id, tx); err != nil {
-			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
-		}
+			if txErr = thingTagRepository.DeleteByThingID(ctx, id); txErr != nil {
+				return txErr
+			}
 
 
-		if err = thingRepository.Delete(ctx, id, tx); err != nil {
-			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
-		}
+			if txErr = thingNotificationRepository.Delete(ctx, id); txErr != nil {
+				return txErr
+			}
 
 
-		if err = thingRepository.CommitTx(tx); err != nil {
-			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
-		}
+			if txErr = thingRepository.Delete(ctx, id); txErr != nil {
+				return txErr
+			}
 
 
-		if len(imageURLs) > 0 {
-			for i := range imageURLs {
-				if err = fileRepository.Delete(imageURLs[i]); err != nil {
-					return fiber.NewError(fiber.StatusInternalServerError, err.Error())
+			if len(imageURLs) > 0 {
+				for i := range imageURLs {
+					if txErr = fileRepository.Delete(imageURLs[i]); txErr != nil {
+						return txErr
+					}
 				}
 				}
 			}
 			}
+
+			return nil
+		})
+
+		if err != nil {
+			logger.Error(ctx, err.Error())
+			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
 		return fctx.JSON(factory.CreateEmptyResponse())
 		return fctx.JSON(factory.CreateEmptyResponse())

+ 1 - 2
internal/api/v1/thing/delete_thing_test.go

@@ -3,7 +3,6 @@ package thing
 import (
 import (
 	"context"
 	"context"
 	"database/sql"
 	"database/sql"
-	"errors"
 	"net/http/httptest"
 	"net/http/httptest"
 	"strconv"
 	"strconv"
 	"testing"
 	"testing"
@@ -32,7 +31,7 @@ func TestDeleteThingHandler(t *testing.T) {
 		thingID   = gofakeit.Number(1, 1000)
 		thingID   = gofakeit.Number(1, 1000)
 		imageID   = gofakeit.Number(1, 1000)
 		imageID   = gofakeit.Number(1, 1000)
 		imageURL  = gofakeit.URL()
 		imageURL  = gofakeit.URL()
-		testError = errors.New(gofakeit.Phrase())
+		testError = gofakeit.Error()
 
 
 		correctReq = req{
 		correctReq = req{
 			method: fiber.MethodDelete,
 			method: fiber.MethodDelete,

+ 9 - 4
internal/api/v1/thing/get_place_things.go

@@ -1,9 +1,11 @@
 package thing
 package thing
 
 
 import (
 import (
+	"git.dmitriygnatenko.ru/dima/go-common/logger"
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2"
 
 
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/request"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 )
 )
 
 
@@ -23,23 +25,26 @@ func GetPlaceThingsHandler(
 ) fiber.Handler {
 ) fiber.Handler {
 	return func(fctx *fiber.Ctx) error {
 	return func(fctx *fiber.Ctx) error {
 		ctx := fctx.Context()
 		ctx := fctx.Context()
-		id, err := fctx.ParamsInt("placeId")
+		id, err := request.ConvertToUint64(fctx, "placeId")
 		if err != nil {
 		if err != nil {
+			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
 		things, err := thingRepository.GetAllByPlaceID(ctx, id)
 		things, err := thingRepository.GetAllByPlaceID(ctx, id)
 		if err != nil {
 		if err != nil {
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
 		tags, err := thingTagRepository.GetByPlaceID(ctx, id)
 		tags, err := thingTagRepository.GetByPlaceID(ctx, id)
 		if err != nil {
 		if err != nil {
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
-		things = helpers.ApplyLocation(fctx, things)
-		tags = helpers.ApplyLocation(fctx, tags)
+		things = location.ApplyLocation(fctx, things)
+		tags = location.ApplyLocation(fctx, tags)
 
 
 		return fctx.JSON(mappers.ToThingsExtResponse(things, tags))
 		return fctx.JSON(mappers.ToThingsExtResponse(things, tags))
 	}
 	}

+ 19 - 18
internal/api/v1/thing/get_place_things_test.go

@@ -2,7 +2,6 @@ package thing
 
 
 import (
 import (
 	"context"
 	"context"
-	"errors"
 	"net/http/httptest"
 	"net/http/httptest"
 	"strconv"
 	"strconv"
 	"testing"
 	"testing"
@@ -12,10 +11,9 @@ import (
 	"github.com/gojuno/minimock/v3"
 	"github.com/gojuno/minimock/v3"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
 
 
-	API "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/thing/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/thing/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/test"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 )
 )
 
 
@@ -28,18 +26,18 @@ func TestGetPlaceThingsHandler(t *testing.T) {
 	}
 	}
 
 
 	var (
 	var (
-		placeID   = gofakeit.Number(1, 1000)
-		testError = errors.New(gofakeit.Phrase())
+		placeID   = uint64(gofakeit.Number(1, 1000))
+		testError = gofakeit.Error()
 		layout    = "2006-01-02 15:04:05"
 		layout    = "2006-01-02 15:04:05"
 
 
 		correctReq = req{
 		correctReq = req{
 			method: fiber.MethodGet,
 			method: fiber.MethodGet,
-			route:  "/v1/things/place/" + strconv.Itoa(placeID),
+			route:  "/v1/things/place/" + strconv.FormatUint(placeID, 10),
 		}
 		}
 
 
 		thingRepoRes = []models.Thing{
 		thingRepoRes = []models.Thing{
 			{
 			{
-				ID:          gofakeit.Number(1, 1000),
+				ID:          uint64(gofakeit.Number(1, 1000)),
 				PlaceID:     placeID,
 				PlaceID:     placeID,
 				Title:       gofakeit.Phrase(),
 				Title:       gofakeit.Phrase(),
 				Description: gofakeit.Phrase(),
 				Description: gofakeit.Phrase(),
@@ -47,7 +45,7 @@ func TestGetPlaceThingsHandler(t *testing.T) {
 				UpdatedAt:   gofakeit.Date(),
 				UpdatedAt:   gofakeit.Date(),
 			},
 			},
 			{
 			{
-				ID:          gofakeit.Number(1, 1000),
+				ID:          uint64(gofakeit.Number(1, 1000)),
 				PlaceID:     placeID,
 				PlaceID:     placeID,
 				Title:       gofakeit.Phrase(),
 				Title:       gofakeit.Phrase(),
 				Description: gofakeit.Phrase(),
 				Description: gofakeit.Phrase(),
@@ -60,7 +58,7 @@ func TestGetPlaceThingsHandler(t *testing.T) {
 			{
 			{
 				ThingID: thingRepoRes[0].ID,
 				ThingID: thingRepoRes[0].ID,
 				Tag: models.Tag{
 				Tag: models.Tag{
-					ID:        gofakeit.Number(1, 1000),
+					ID:        uint64(gofakeit.Number(1, 1000)),
 					Title:     gofakeit.Phrase(),
 					Title:     gofakeit.Phrase(),
 					Style:     gofakeit.Phrase(),
 					Style:     gofakeit.Phrase(),
 					CreatedAt: gofakeit.Date(),
 					CreatedAt: gofakeit.Date(),
@@ -70,7 +68,7 @@ func TestGetPlaceThingsHandler(t *testing.T) {
 			{
 			{
 				ThingID: thingRepoRes[1].ID,
 				ThingID: thingRepoRes[1].ID,
 				Tag: models.Tag{
 				Tag: models.Tag{
-					ID:        gofakeit.Number(1, 1000),
+					ID:        uint64(gofakeit.Number(1, 1000)),
 					Title:     gofakeit.Phrase(),
 					Title:     gofakeit.Phrase(),
 					Style:     gofakeit.Phrase(),
 					Style:     gofakeit.Phrase(),
 					CreatedAt: gofakeit.Date(),
 					CreatedAt: gofakeit.Date(),
@@ -139,7 +137,7 @@ func TestGetPlaceThingsHandler(t *testing.T) {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				mock := mocks.NewThingRepositoryMock(mc)
 				mock := mocks.NewThingRepositoryMock(mc)
 
 
-				mock.GetAllByPlaceIDMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetAllByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, placeID, id)
 					assert.Equal(mc, placeID, id)
 				}).Return(thingRepoRes, nil)
 				}).Return(thingRepoRes, nil)
 
 
@@ -148,7 +146,7 @@ func TestGetPlaceThingsHandler(t *testing.T) {
 			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
 			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
 				mock := mocks.NewThingTagRepositoryMock(mc)
 				mock := mocks.NewThingTagRepositoryMock(mc)
 
 
-				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, placeID, id)
 					assert.Equal(mc, placeID, id)
 				}).Return(thingTagRepoRes, nil)
 				}).Return(thingTagRepoRes, nil)
 
 
@@ -162,7 +160,7 @@ func TestGetPlaceThingsHandler(t *testing.T) {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				mock := mocks.NewThingRepositoryMock(mc)
 				mock := mocks.NewThingRepositoryMock(mc)
 
 
-				mock.GetAllByPlaceIDMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetAllByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, placeID, id)
 					assert.Equal(mc, placeID, id)
 				}).Return(nil, testError)
 				}).Return(nil, testError)
 
 
@@ -179,7 +177,7 @@ func TestGetPlaceThingsHandler(t *testing.T) {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				mock := mocks.NewThingRepositoryMock(mc)
 				mock := mocks.NewThingRepositoryMock(mc)
 
 
-				mock.GetAllByPlaceIDMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetAllByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, placeID, id)
 					assert.Equal(mc, placeID, id)
 				}).Return(thingRepoRes, nil)
 				}).Return(thingRepoRes, nil)
 
 
@@ -188,7 +186,7 @@ func TestGetPlaceThingsHandler(t *testing.T) {
 			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
 			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
 				mock := mocks.NewThingTagRepositoryMock(mc)
 				mock := mocks.NewThingTagRepositoryMock(mc)
 
 
-				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, placeID, id)
 					assert.Equal(mc, placeID, id)
 				}).Return(nil, testError)
 				}).Return(nil, testError)
 
 
@@ -218,12 +216,15 @@ func TestGetPlaceThingsHandler(t *testing.T) {
 
 
 			mc := minimock.NewController(t)
 			mc := minimock.NewController(t)
 			fiberApp := fiber.New()
 			fiberApp := fiber.New()
-			fiberApp.Get("/v1/things/place/:placeId", GetPlaceThingsHandler(tt.thingRepoMock(mc), tt.thingTagRepoMock(mc)))
+			fiberApp.Get("/v1/things/place/:placeId", GetPlaceThingsHandler(
+				tt.thingRepoMock(mc),
+				tt.thingTagRepoMock(mc),
+			))
 
 
-			fiberRes, _ := fiberApp.Test(httptest.NewRequest(tt.req.method, tt.req.route, nil), API.DefaultTestTimeOut)
+			fiberRes, _ := fiberApp.Test(httptest.NewRequest(tt.req.method, tt.req.route, nil), test.TestTimeout)
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			if tt.resBody != nil {
 			if tt.resBody != nil {
-				assert.Equal(t, helpers.MarshalResponse(tt.resBody), helpers.ConvertBodyToString(fiberRes.Body))
+				assert.Equal(t, test.MarshalResponse(tt.resBody), test.ConvertBodyToString(fiberRes.Body))
 			}
 			}
 		})
 		})
 	}
 	}

+ 8 - 3
internal/api/v1/thing/get_thing.go

@@ -4,9 +4,11 @@ import (
 	"database/sql"
 	"database/sql"
 	"errors"
 	"errors"
 
 
+	"git.dmitriygnatenko.ru/dima/go-common/logger"
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2"
 
 
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/request"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 )
 )
 
 
@@ -24,21 +26,24 @@ import (
 func GetThingHandler(thingRepository ThingRepository) fiber.Handler {
 func GetThingHandler(thingRepository ThingRepository) fiber.Handler {
 	return func(fctx *fiber.Ctx) error {
 	return func(fctx *fiber.Ctx) error {
 		ctx := fctx.Context()
 		ctx := fctx.Context()
-		id, err := fctx.ParamsInt("thingId")
+		id, err := request.ConvertToUint64(fctx, "thingId")
 		if err != nil {
 		if err != nil {
+			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
 		res, err := thingRepository.Get(ctx, id)
 		res, err := thingRepository.Get(ctx, id)
 		if err != nil {
 		if err != nil {
 			if errors.Is(err, sql.ErrNoRows) {
 			if errors.Is(err, sql.ErrNoRows) {
+				logger.Info(ctx, err.Error())
 				return fiber.NewError(fiber.StatusNotFound, "")
 				return fiber.NewError(fiber.StatusNotFound, "")
 			}
 			}
 
 
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
-		res = helpers.ApplyLocation(fctx, res)
+		res = location.ApplyLocation(fctx, res)
 
 
 		return fctx.JSON(mappers.ToThingResponse(*res))
 		return fctx.JSON(mappers.ToThingResponse(*res))
 	}
 	}

+ 13 - 12
internal/api/v1/thing/get_thing_test.go

@@ -3,7 +3,6 @@ package thing
 import (
 import (
 	"context"
 	"context"
 	"database/sql"
 	"database/sql"
-	"errors"
 	"net/http/httptest"
 	"net/http/httptest"
 	"strconv"
 	"strconv"
 	"testing"
 	"testing"
@@ -13,10 +12,9 @@ import (
 	"github.com/gojuno/minimock/v3"
 	"github.com/gojuno/minimock/v3"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
 
 
-	API "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/thing/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/thing/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/test"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 )
 )
 
 
@@ -29,14 +27,14 @@ func TestGetThingHandler(t *testing.T) {
 	}
 	}
 
 
 	var (
 	var (
-		thingID   = gofakeit.Number(1, 1000)
-		placeID   = gofakeit.Number(1, 1000)
-		testError = errors.New(gofakeit.Phrase())
+		thingID   = uint64(gofakeit.Number(1, 1000))
+		placeID   = uint64(gofakeit.Number(1, 1000))
+		testError = gofakeit.Error()
 		layout    = "2006-01-02 15:04:05"
 		layout    = "2006-01-02 15:04:05"
 
 
 		correctReq = req{
 		correctReq = req{
 			method: fiber.MethodGet,
 			method: fiber.MethodGet,
-			route:  "/v1/things/" + strconv.Itoa(thingID),
+			route:  "/v1/things/" + strconv.FormatUint(thingID, 10),
 		}
 		}
 
 
 		thingRepoRes = models.Thing{
 		thingRepoRes = models.Thing{
@@ -73,7 +71,7 @@ func TestGetThingHandler(t *testing.T) {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				mock := mocks.NewThingRepositoryMock(mc)
 				mock := mocks.NewThingRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 				}).Return(&thingRepoRes, nil)
 				}).Return(&thingRepoRes, nil)
 
 
@@ -87,7 +85,7 @@ func TestGetThingHandler(t *testing.T) {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				mock := mocks.NewThingRepositoryMock(mc)
 				mock := mocks.NewThingRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 				}).Return(nil, sql.ErrNoRows)
 				}).Return(nil, sql.ErrNoRows)
 
 
@@ -101,7 +99,7 @@ func TestGetThingHandler(t *testing.T) {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				mock := mocks.NewThingRepositoryMock(mc)
 				mock := mocks.NewThingRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 				}).Return(nil, testError)
 				}).Return(nil, testError)
 
 
@@ -130,10 +128,13 @@ func TestGetThingHandler(t *testing.T) {
 			fiberApp := fiber.New()
 			fiberApp := fiber.New()
 			fiberApp.Get("/v1/things/:thingId", GetThingHandler(tt.thingRepoMock(mc)))
 			fiberApp.Get("/v1/things/:thingId", GetThingHandler(tt.thingRepoMock(mc)))
 
 
-			fiberRes, _ := fiberApp.Test(httptest.NewRequest(tt.req.method, tt.req.route, nil), API.DefaultTestTimeOut)
+			fiberRes, _ := fiberApp.Test(
+				httptest.NewRequest(tt.req.method, tt.req.route, nil),
+				test.TestTimeout,
+			)
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			if tt.resBody != nil {
 			if tt.resBody != nil {
-				assert.Equal(t, helpers.MarshalResponse(tt.resBody), helpers.ConvertBodyToString(fiberRes.Body))
+				assert.Equal(t, test.MarshalResponse(tt.resBody), test.ConvertBodyToString(fiberRes.Body))
 			}
 			}
 		})
 		})
 	}
 	}

+ 393 - 0
internal/api/v1/thing/mocks/transaction_manager_minimock.go

@@ -0,0 +1,393 @@
+// Code generated by http://github.com/gojuno/minimock (v3.3.14). DO NOT EDIT.
+
+package mocks
+
+//go:generate minimock -i git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/thing.TransactionManager -o transaction_manager_minimock.go -n TransactionManagerMock -p mocks
+
+import (
+	"context"
+	"sync"
+	mm_atomic "sync/atomic"
+	mm_time "time"
+
+	"github.com/gojuno/minimock/v3"
+)
+
+// TransactionManagerMock implements thing.TransactionManager
+type TransactionManagerMock struct {
+	t          minimock.Tester
+	finishOnce sync.Once
+
+	funcReadCommitted          func(ctx context.Context, f1 func(ctx context.Context) error) (err error)
+	inspectFuncReadCommitted   func(ctx context.Context, f1 func(ctx context.Context) error)
+	afterReadCommittedCounter  uint64
+	beforeReadCommittedCounter uint64
+	ReadCommittedMock          mTransactionManagerMockReadCommitted
+}
+
+// NewTransactionManagerMock returns a mock for thing.TransactionManager
+func NewTransactionManagerMock(t minimock.Tester) *TransactionManagerMock {
+	m := &TransactionManagerMock{t: t}
+
+	if controller, ok := t.(minimock.MockController); ok {
+		controller.RegisterMocker(m)
+	}
+
+	m.ReadCommittedMock = mTransactionManagerMockReadCommitted{mock: m}
+	m.ReadCommittedMock.callArgs = []*TransactionManagerMockReadCommittedParams{}
+
+	t.Cleanup(m.MinimockFinish)
+
+	return m
+}
+
+type mTransactionManagerMockReadCommitted struct {
+	optional           bool
+	mock               *TransactionManagerMock
+	defaultExpectation *TransactionManagerMockReadCommittedExpectation
+	expectations       []*TransactionManagerMockReadCommittedExpectation
+
+	callArgs []*TransactionManagerMockReadCommittedParams
+	mutex    sync.RWMutex
+
+	expectedInvocations uint64
+}
+
+// TransactionManagerMockReadCommittedExpectation specifies expectation struct of the TransactionManager.ReadCommitted
+type TransactionManagerMockReadCommittedExpectation struct {
+	mock      *TransactionManagerMock
+	params    *TransactionManagerMockReadCommittedParams
+	paramPtrs *TransactionManagerMockReadCommittedParamPtrs
+	results   *TransactionManagerMockReadCommittedResults
+	Counter   uint64
+}
+
+// TransactionManagerMockReadCommittedParams contains parameters of the TransactionManager.ReadCommitted
+type TransactionManagerMockReadCommittedParams struct {
+	ctx context.Context
+	f1  func(ctx context.Context) error
+}
+
+// TransactionManagerMockReadCommittedParamPtrs contains pointers to parameters of the TransactionManager.ReadCommitted
+type TransactionManagerMockReadCommittedParamPtrs struct {
+	ctx *context.Context
+	f1  *func(ctx context.Context) error
+}
+
+// TransactionManagerMockReadCommittedResults contains results of the TransactionManager.ReadCommitted
+type TransactionManagerMockReadCommittedResults struct {
+	err error
+}
+
+// Marks this method to be optional. The default behavior of any method with Return() is '1 or more', meaning
+// the test will fail minimock's automatic final call check if the mocked method was not called at least once.
+// Optional() makes method check to work in '0 or more' mode.
+// It is NOT RECOMMENDED to use this option unless you really need it, as default behaviour helps to
+// catch the problems when the expected method call is totally skipped during test run.
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) Optional() *mTransactionManagerMockReadCommitted {
+	mmReadCommitted.optional = true
+	return mmReadCommitted
+}
+
+// Expect sets up expected params for TransactionManager.ReadCommitted
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) Expect(ctx context.Context, f1 func(ctx context.Context) error) *mTransactionManagerMockReadCommitted {
+	if mmReadCommitted.mock.funcReadCommitted != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by Set")
+	}
+
+	if mmReadCommitted.defaultExpectation == nil {
+		mmReadCommitted.defaultExpectation = &TransactionManagerMockReadCommittedExpectation{}
+	}
+
+	if mmReadCommitted.defaultExpectation.paramPtrs != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by ExpectParams functions")
+	}
+
+	mmReadCommitted.defaultExpectation.params = &TransactionManagerMockReadCommittedParams{ctx, f1}
+	for _, e := range mmReadCommitted.expectations {
+		if minimock.Equal(e.params, mmReadCommitted.defaultExpectation.params) {
+			mmReadCommitted.mock.t.Fatalf("Expectation set by When has same params: %#v", *mmReadCommitted.defaultExpectation.params)
+		}
+	}
+
+	return mmReadCommitted
+}
+
+// ExpectCtxParam1 sets up expected param ctx for TransactionManager.ReadCommitted
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) ExpectCtxParam1(ctx context.Context) *mTransactionManagerMockReadCommitted {
+	if mmReadCommitted.mock.funcReadCommitted != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by Set")
+	}
+
+	if mmReadCommitted.defaultExpectation == nil {
+		mmReadCommitted.defaultExpectation = &TransactionManagerMockReadCommittedExpectation{}
+	}
+
+	if mmReadCommitted.defaultExpectation.params != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by Expect")
+	}
+
+	if mmReadCommitted.defaultExpectation.paramPtrs == nil {
+		mmReadCommitted.defaultExpectation.paramPtrs = &TransactionManagerMockReadCommittedParamPtrs{}
+	}
+	mmReadCommitted.defaultExpectation.paramPtrs.ctx = &ctx
+
+	return mmReadCommitted
+}
+
+// ExpectF1Param2 sets up expected param f1 for TransactionManager.ReadCommitted
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) ExpectF1Param2(f1 func(ctx context.Context) error) *mTransactionManagerMockReadCommitted {
+	if mmReadCommitted.mock.funcReadCommitted != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by Set")
+	}
+
+	if mmReadCommitted.defaultExpectation == nil {
+		mmReadCommitted.defaultExpectation = &TransactionManagerMockReadCommittedExpectation{}
+	}
+
+	if mmReadCommitted.defaultExpectation.params != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by Expect")
+	}
+
+	if mmReadCommitted.defaultExpectation.paramPtrs == nil {
+		mmReadCommitted.defaultExpectation.paramPtrs = &TransactionManagerMockReadCommittedParamPtrs{}
+	}
+	mmReadCommitted.defaultExpectation.paramPtrs.f1 = &f1
+
+	return mmReadCommitted
+}
+
+// Inspect accepts an inspector function that has same arguments as the TransactionManager.ReadCommitted
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) Inspect(f func(ctx context.Context, f1 func(ctx context.Context) error)) *mTransactionManagerMockReadCommitted {
+	if mmReadCommitted.mock.inspectFuncReadCommitted != nil {
+		mmReadCommitted.mock.t.Fatalf("Inspect function is already set for TransactionManagerMock.ReadCommitted")
+	}
+
+	mmReadCommitted.mock.inspectFuncReadCommitted = f
+
+	return mmReadCommitted
+}
+
+// Return sets up results that will be returned by TransactionManager.ReadCommitted
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) Return(err error) *TransactionManagerMock {
+	if mmReadCommitted.mock.funcReadCommitted != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by Set")
+	}
+
+	if mmReadCommitted.defaultExpectation == nil {
+		mmReadCommitted.defaultExpectation = &TransactionManagerMockReadCommittedExpectation{mock: mmReadCommitted.mock}
+	}
+	mmReadCommitted.defaultExpectation.results = &TransactionManagerMockReadCommittedResults{err}
+	return mmReadCommitted.mock
+}
+
+// Set uses given function f to mock the TransactionManager.ReadCommitted method
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) Set(f func(ctx context.Context, f1 func(ctx context.Context) error) (err error)) *TransactionManagerMock {
+	if mmReadCommitted.defaultExpectation != nil {
+		mmReadCommitted.mock.t.Fatalf("Default expectation is already set for the TransactionManager.ReadCommitted method")
+	}
+
+	if len(mmReadCommitted.expectations) > 0 {
+		mmReadCommitted.mock.t.Fatalf("Some expectations are already set for the TransactionManager.ReadCommitted method")
+	}
+
+	mmReadCommitted.mock.funcReadCommitted = f
+	return mmReadCommitted.mock
+}
+
+// When sets expectation for the TransactionManager.ReadCommitted which will trigger the result defined by the following
+// Then helper
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) When(ctx context.Context, f1 func(ctx context.Context) error) *TransactionManagerMockReadCommittedExpectation {
+	if mmReadCommitted.mock.funcReadCommitted != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by Set")
+	}
+
+	expectation := &TransactionManagerMockReadCommittedExpectation{
+		mock:   mmReadCommitted.mock,
+		params: &TransactionManagerMockReadCommittedParams{ctx, f1},
+	}
+	mmReadCommitted.expectations = append(mmReadCommitted.expectations, expectation)
+	return expectation
+}
+
+// Then sets up TransactionManager.ReadCommitted return parameters for the expectation previously defined by the When method
+func (e *TransactionManagerMockReadCommittedExpectation) Then(err error) *TransactionManagerMock {
+	e.results = &TransactionManagerMockReadCommittedResults{err}
+	return e.mock
+}
+
+// Times sets number of times TransactionManager.ReadCommitted should be invoked
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) Times(n uint64) *mTransactionManagerMockReadCommitted {
+	if n == 0 {
+		mmReadCommitted.mock.t.Fatalf("Times of TransactionManagerMock.ReadCommitted mock can not be zero")
+	}
+	mm_atomic.StoreUint64(&mmReadCommitted.expectedInvocations, n)
+	return mmReadCommitted
+}
+
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) invocationsDone() bool {
+	if len(mmReadCommitted.expectations) == 0 && mmReadCommitted.defaultExpectation == nil && mmReadCommitted.mock.funcReadCommitted == nil {
+		return true
+	}
+
+	totalInvocations := mm_atomic.LoadUint64(&mmReadCommitted.mock.afterReadCommittedCounter)
+	expectedInvocations := mm_atomic.LoadUint64(&mmReadCommitted.expectedInvocations)
+
+	return totalInvocations > 0 && (expectedInvocations == 0 || expectedInvocations == totalInvocations)
+}
+
+// ReadCommitted implements thing.TransactionManager
+func (mmReadCommitted *TransactionManagerMock) ReadCommitted(ctx context.Context, f1 func(ctx context.Context) error) (err error) {
+	mm_atomic.AddUint64(&mmReadCommitted.beforeReadCommittedCounter, 1)
+	defer mm_atomic.AddUint64(&mmReadCommitted.afterReadCommittedCounter, 1)
+
+	if mmReadCommitted.inspectFuncReadCommitted != nil {
+		mmReadCommitted.inspectFuncReadCommitted(ctx, f1)
+	}
+
+	mm_params := TransactionManagerMockReadCommittedParams{ctx, f1}
+
+	// Record call args
+	mmReadCommitted.ReadCommittedMock.mutex.Lock()
+	mmReadCommitted.ReadCommittedMock.callArgs = append(mmReadCommitted.ReadCommittedMock.callArgs, &mm_params)
+	mmReadCommitted.ReadCommittedMock.mutex.Unlock()
+
+	for _, e := range mmReadCommitted.ReadCommittedMock.expectations {
+		if minimock.Equal(*e.params, mm_params) {
+			mm_atomic.AddUint64(&e.Counter, 1)
+			return e.results.err
+		}
+	}
+
+	if mmReadCommitted.ReadCommittedMock.defaultExpectation != nil {
+		mm_atomic.AddUint64(&mmReadCommitted.ReadCommittedMock.defaultExpectation.Counter, 1)
+		mm_want := mmReadCommitted.ReadCommittedMock.defaultExpectation.params
+		mm_want_ptrs := mmReadCommitted.ReadCommittedMock.defaultExpectation.paramPtrs
+
+		mm_got := TransactionManagerMockReadCommittedParams{ctx, f1}
+
+		if mm_want_ptrs != nil {
+
+			if mm_want_ptrs.ctx != nil && !minimock.Equal(*mm_want_ptrs.ctx, mm_got.ctx) {
+				mmReadCommitted.t.Errorf("TransactionManagerMock.ReadCommitted got unexpected parameter ctx, want: %#v, got: %#v%s\n", *mm_want_ptrs.ctx, mm_got.ctx, minimock.Diff(*mm_want_ptrs.ctx, mm_got.ctx))
+			}
+
+			if mm_want_ptrs.f1 != nil && !minimock.Equal(*mm_want_ptrs.f1, mm_got.f1) {
+				mmReadCommitted.t.Errorf("TransactionManagerMock.ReadCommitted got unexpected parameter f1, want: %#v, got: %#v%s\n", *mm_want_ptrs.f1, mm_got.f1, minimock.Diff(*mm_want_ptrs.f1, mm_got.f1))
+			}
+
+		} else if mm_want != nil && !minimock.Equal(*mm_want, mm_got) {
+			mmReadCommitted.t.Errorf("TransactionManagerMock.ReadCommitted got unexpected parameters, want: %#v, got: %#v%s\n", *mm_want, mm_got, minimock.Diff(*mm_want, mm_got))
+		}
+
+		mm_results := mmReadCommitted.ReadCommittedMock.defaultExpectation.results
+		if mm_results == nil {
+			mmReadCommitted.t.Fatal("No results are set for the TransactionManagerMock.ReadCommitted")
+		}
+		return (*mm_results).err
+	}
+	if mmReadCommitted.funcReadCommitted != nil {
+		return mmReadCommitted.funcReadCommitted(ctx, f1)
+	}
+	mmReadCommitted.t.Fatalf("Unexpected call to TransactionManagerMock.ReadCommitted. %v %v", ctx, f1)
+	return
+}
+
+// ReadCommittedAfterCounter returns a count of finished TransactionManagerMock.ReadCommitted invocations
+func (mmReadCommitted *TransactionManagerMock) ReadCommittedAfterCounter() uint64 {
+	return mm_atomic.LoadUint64(&mmReadCommitted.afterReadCommittedCounter)
+}
+
+// ReadCommittedBeforeCounter returns a count of TransactionManagerMock.ReadCommitted invocations
+func (mmReadCommitted *TransactionManagerMock) ReadCommittedBeforeCounter() uint64 {
+	return mm_atomic.LoadUint64(&mmReadCommitted.beforeReadCommittedCounter)
+}
+
+// Calls returns a list of arguments used in each call to TransactionManagerMock.ReadCommitted.
+// The list is in the same order as the calls were made (i.e. recent calls have a higher index)
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) Calls() []*TransactionManagerMockReadCommittedParams {
+	mmReadCommitted.mutex.RLock()
+
+	argCopy := make([]*TransactionManagerMockReadCommittedParams, len(mmReadCommitted.callArgs))
+	copy(argCopy, mmReadCommitted.callArgs)
+
+	mmReadCommitted.mutex.RUnlock()
+
+	return argCopy
+}
+
+// MinimockReadCommittedDone returns true if the count of the ReadCommitted invocations corresponds
+// the number of defined expectations
+func (m *TransactionManagerMock) MinimockReadCommittedDone() bool {
+	if m.ReadCommittedMock.optional {
+		// Optional methods provide '0 or more' call count restriction.
+		return true
+	}
+
+	for _, e := range m.ReadCommittedMock.expectations {
+		if mm_atomic.LoadUint64(&e.Counter) < 1 {
+			return false
+		}
+	}
+
+	return m.ReadCommittedMock.invocationsDone()
+}
+
+// MinimockReadCommittedInspect logs each unmet expectation
+func (m *TransactionManagerMock) MinimockReadCommittedInspect() {
+	for _, e := range m.ReadCommittedMock.expectations {
+		if mm_atomic.LoadUint64(&e.Counter) < 1 {
+			m.t.Errorf("Expected call to TransactionManagerMock.ReadCommitted with params: %#v", *e.params)
+		}
+	}
+
+	afterReadCommittedCounter := mm_atomic.LoadUint64(&m.afterReadCommittedCounter)
+	// if default expectation was set then invocations count should be greater than zero
+	if m.ReadCommittedMock.defaultExpectation != nil && afterReadCommittedCounter < 1 {
+		if m.ReadCommittedMock.defaultExpectation.params == nil {
+			m.t.Error("Expected call to TransactionManagerMock.ReadCommitted")
+		} else {
+			m.t.Errorf("Expected call to TransactionManagerMock.ReadCommitted with params: %#v", *m.ReadCommittedMock.defaultExpectation.params)
+		}
+	}
+	// if func was set then invocations count should be greater than zero
+	if m.funcReadCommitted != nil && afterReadCommittedCounter < 1 {
+		m.t.Error("Expected call to TransactionManagerMock.ReadCommitted")
+	}
+
+	if !m.ReadCommittedMock.invocationsDone() && afterReadCommittedCounter > 0 {
+		m.t.Errorf("Expected %d calls to TransactionManagerMock.ReadCommitted but found %d calls",
+			mm_atomic.LoadUint64(&m.ReadCommittedMock.expectedInvocations), afterReadCommittedCounter)
+	}
+}
+
+// MinimockFinish checks that all mocked methods have been called the expected number of times
+func (m *TransactionManagerMock) MinimockFinish() {
+	m.finishOnce.Do(func() {
+		if !m.minimockDone() {
+			m.MinimockReadCommittedInspect()
+		}
+	})
+}
+
+// MinimockWait waits for all mocked methods to be called the expected number of times
+func (m *TransactionManagerMock) MinimockWait(timeout mm_time.Duration) {
+	timeoutCh := mm_time.After(timeout)
+	for {
+		if m.minimockDone() {
+			return
+		}
+		select {
+		case <-timeoutCh:
+			m.MinimockFinish()
+			return
+		case <-mm_time.After(10 * mm_time.Millisecond):
+		}
+	}
+}
+
+func (m *TransactionManagerMock) minimockDone() bool {
+	done := true
+	return done &&
+		m.MinimockReadCommittedDone()
+}

+ 4 - 2
internal/api/v1/thing/search_thing.go

@@ -5,9 +5,10 @@ import (
 	"regexp"
 	"regexp"
 	"strings"
 	"strings"
 
 
+	"git.dmitriygnatenko.ru/dima/go-common/logger"
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2"
 
 
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 )
 )
 
 
@@ -36,10 +37,11 @@ func SearchThingHandler(thingRepository ThingRepository) fiber.Handler {
 
 
 		res, err := thingRepository.Search(ctx, search)
 		res, err := thingRepository.Search(ctx, search)
 		if err != nil {
 		if err != nil {
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
-		res = helpers.ApplyLocation(fctx, res)
+		res = location.ApplyLocation(fctx, res)
 
 
 		return fctx.JSON(mappers.ToThingsResponse(res))
 		return fctx.JSON(mappers.ToThingsResponse(res))
 	}
 	}

+ 11 - 10
internal/api/v1/thing/search_thing_test.go

@@ -2,7 +2,6 @@ package thing
 
 
 import (
 import (
 	"context"
 	"context"
-	"errors"
 	"net/http/httptest"
 	"net/http/httptest"
 	"net/url"
 	"net/url"
 	"testing"
 	"testing"
@@ -12,10 +11,9 @@ import (
 	"github.com/gojuno/minimock/v3"
 	"github.com/gojuno/minimock/v3"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
 
 
-	API "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/thing/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/thing/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/test"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 )
 )
 
 
@@ -29,7 +27,7 @@ func TestSearchThingHandler(t *testing.T) {
 
 
 	var (
 	var (
 		search    = gofakeit.LetterN(10)
 		search    = gofakeit.LetterN(10)
-		testError = errors.New(gofakeit.Phrase())
+		testError = gofakeit.Error()
 		layout    = "2006-01-02 15:04:05"
 		layout    = "2006-01-02 15:04:05"
 
 
 		correctReq = req{
 		correctReq = req{
@@ -39,16 +37,16 @@ func TestSearchThingHandler(t *testing.T) {
 
 
 		thingRepoRes = []models.Thing{
 		thingRepoRes = []models.Thing{
 			{
 			{
-				ID:          gofakeit.Number(1, 1000),
-				PlaceID:     gofakeit.Number(1, 1000),
+				ID:          uint64(gofakeit.Number(1, 1000)),
+				PlaceID:     uint64(gofakeit.Number(1, 1000)),
 				Title:       gofakeit.Phrase(),
 				Title:       gofakeit.Phrase(),
 				Description: gofakeit.Phrase(),
 				Description: gofakeit.Phrase(),
 				CreatedAt:   gofakeit.Date(),
 				CreatedAt:   gofakeit.Date(),
 				UpdatedAt:   gofakeit.Date(),
 				UpdatedAt:   gofakeit.Date(),
 			},
 			},
 			{
 			{
-				ID:          gofakeit.Number(1, 1000),
-				PlaceID:     gofakeit.Number(1, 1000),
+				ID:          uint64(gofakeit.Number(1, 1000)),
+				PlaceID:     uint64(gofakeit.Number(1, 1000)),
 				Title:       gofakeit.Phrase(),
 				Title:       gofakeit.Phrase(),
 				Description: gofakeit.Phrase(),
 				Description: gofakeit.Phrase(),
 				CreatedAt:   gofakeit.Date(),
 				CreatedAt:   gofakeit.Date(),
@@ -146,10 +144,13 @@ func TestSearchThingHandler(t *testing.T) {
 			fiberApp := fiber.New()
 			fiberApp := fiber.New()
 			fiberApp.Get("/v1/things/search/:search", SearchThingHandler(tt.thingRepoMock(mc)))
 			fiberApp.Get("/v1/things/search/:search", SearchThingHandler(tt.thingRepoMock(mc)))
 
 
-			fiberRes, _ := fiberApp.Test(httptest.NewRequest(tt.req.method, tt.req.route, nil), API.DefaultTestTimeOut)
+			fiberRes, _ := fiberApp.Test(
+				httptest.NewRequest(tt.req.method, tt.req.route, nil),
+				test.TestTimeout,
+			)
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			if tt.resBody != nil {
 			if tt.resBody != nil {
-				assert.Equal(t, helpers.MarshalResponse(tt.resBody), helpers.ConvertBodyToString(fiberRes.Body))
+				assert.Equal(t, test.MarshalResponse(tt.resBody), test.ConvertBodyToString(fiberRes.Body))
 			}
 			}
 		})
 		})
 	}
 	}

+ 36 - 28
internal/api/v1/thing/update_thing.go

@@ -1,13 +1,16 @@
 package thing
 package thing
 
 
 import (
 import (
+	"context"
+
+	"git.dmitriygnatenko.ru/dima/go-common/logger"
 	"github.com/go-playground/validator/v10"
 	"github.com/go-playground/validator/v10"
 	"github.com/gofiber/fiber/v2"
 	"github.com/gofiber/fiber/v2"
 
 
-	API "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/location"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/request"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/mappers"
 )
 )
 
 
@@ -23,65 +26,70 @@ import (
 // @Accept      json
 // @Accept      json
 // @Produce     json
 // @Produce     json
 func UpdateThingHandler(
 func UpdateThingHandler(
+	tm TransactionManager,
 	thingRepository ThingRepository,
 	thingRepository ThingRepository,
 	placeThingRepository PlaceThingRepository,
 	placeThingRepository PlaceThingRepository,
 ) fiber.Handler {
 ) fiber.Handler {
 	return func(fctx *fiber.Ctx) error {
 	return func(fctx *fiber.Ctx) error {
 		ctx := fctx.Context()
 		ctx := fctx.Context()
-		id, err := fctx.ParamsInt("thingId")
+		id, err := request.ConvertToUint64(fctx, "thingId")
 		if err != nil {
 		if err != nil {
+			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
 		req := dto.UpdateThingRequest{}
 		req := dto.UpdateThingRequest{}
 		if err = fctx.BodyParser(&req); err != nil {
 		if err = fctx.BodyParser(&req); err != nil {
+			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 		}
 
 
 		var validate = validator.New()
 		var validate = validator.New()
 		if err = validate.Struct(req); err != nil {
 		if err = validate.Struct(req); err != nil {
+			logger.Info(ctx, err.Error())
 			return fctx.Status(fiber.StatusBadRequest).JSON(factory.CreateValidateErrorResponse(err))
 			return fctx.Status(fiber.StatusBadRequest).JSON(factory.CreateValidateErrorResponse(err))
 		}
 		}
 
 
-		thing, err := thingRepository.Get(ctx, id)
-		if err != nil {
-			return fiber.NewError(fiber.StatusBadRequest, err.Error())
-		}
-
-		placeThing, err := placeThingRepository.GetByThingID(ctx, id)
-		if err != nil {
-			return fiber.NewError(fiber.StatusBadRequest, err.Error())
-		}
+		err = tm.ReadCommitted(ctx, func(ctx context.Context) error {
+			thing, txErr := thingRepository.Get(ctx, id)
+			if txErr != nil {
+				return txErr
+			}
 
 
-		tx, err := thingRepository.BeginTx(ctx, API.DefaultTxLevel)
-		if err != nil {
-			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
-		}
+			placeThing, txErr := placeThingRepository.GetByThingID(ctx, id)
+			if txErr != nil {
+				return txErr
+			}
 
 
-		if req.Title != thing.Title || req.Description != thing.Description {
-			err = thingRepository.Update(ctx, mappers.ToUpdateThingRequest(id, req), tx)
-			if err != nil {
-				return fiber.NewError(fiber.StatusInternalServerError, err.Error())
+			if req.Title != thing.Title || req.Description != thing.Description {
+				txErr = thingRepository.Update(ctx, mappers.ToUpdateThingRequest(id, req))
+				if txErr != nil {
+					return txErr
+				}
 			}
 			}
-		}
 
 
-		if placeThing.PlaceID != req.PlaceID {
-			err = placeThingRepository.UpdatePlace(ctx, mappers.ToUpdatePlaceThingRequest(id, req.PlaceID), tx)
-			if err != nil {
-				return fiber.NewError(fiber.StatusInternalServerError, err.Error())
+			if placeThing.PlaceID != req.PlaceID {
+				txErr = placeThingRepository.UpdatePlace(ctx, mappers.ToUpdatePlaceThingRequest(id, req.PlaceID))
+				if txErr != nil {
+					return txErr
+				}
 			}
 			}
-		}
 
 
-		if err = thingRepository.CommitTx(tx); err != nil {
+			return nil
+		})
+
+		if err != nil {
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
 		res, err := thingRepository.Get(ctx, id)
 		res, err := thingRepository.Get(ctx, id)
 		if err != nil {
 		if err != nil {
+			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 		}
 
 
-		res = helpers.ApplyLocation(fctx, res)
+		res = location.ApplyLocation(fctx, res)
 
 
 		return fctx.JSON(mappers.ToThingResponse(*res))
 		return fctx.JSON(mappers.ToThingResponse(*res))
 	}
 	}

+ 82 - 107
internal/api/v1/thing/update_thing_test.go

@@ -3,7 +3,6 @@ package thing
 import (
 import (
 	"context"
 	"context"
 	"database/sql"
 	"database/sql"
-	"errors"
 	"net/http/httptest"
 	"net/http/httptest"
 	"strconv"
 	"strconv"
 	"testing"
 	"testing"
@@ -13,10 +12,9 @@ import (
 	"github.com/gojuno/minimock/v3"
 	"github.com/gojuno/minimock/v3"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
 
 
-	API "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/thing/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/thing/mocks"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
-	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/test"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 )
 )
 
 
@@ -31,16 +29,20 @@ func TestUpdateThingHandler(t *testing.T) {
 	}
 	}
 
 
 	var (
 	var (
-		placeID     = gofakeit.Number(1, 1000)
-		thingID     = gofakeit.Number(1, 1000)
+		placeID     = uint64(gofakeit.Number(1, 1000))
+		thingID     = uint64(gofakeit.Number(1, 1000))
 		title       = gofakeit.Phrase()
 		title       = gofakeit.Phrase()
 		description = gofakeit.Phrase()
 		description = gofakeit.Phrase()
-		testError   = errors.New(gofakeit.Phrase())
+		testError   = gofakeit.Error()
 		layout      = "2006-01-02 15:04:05"
 		layout      = "2006-01-02 15:04:05"
 
 
+		txMockFunc = func(ctx context.Context, f func(ctx context.Context) error) error {
+			return f(ctx)
+		}
+
 		correctReq = req{
 		correctReq = req{
 			method: fiber.MethodPut,
 			method: fiber.MethodPut,
-			route:  "/v1/things/" + strconv.Itoa(thingID),
+			route:  "/v1/things/" + strconv.FormatUint(thingID, 10),
 			body: &dto.UpdateThingRequest{
 			body: &dto.UpdateThingRequest{
 				PlaceID:     placeID,
 				PlaceID:     placeID,
 				Title:       title,
 				Title:       title,
@@ -58,7 +60,7 @@ func TestUpdateThingHandler(t *testing.T) {
 		}
 		}
 
 
 		placeThingRepoResBeforeUpdate = models.PlaceThing{
 		placeThingRepoResBeforeUpdate = models.PlaceThing{
-			PlaceID:   gofakeit.Number(1, 1000),
+			PlaceID:   uint64(gofakeit.Number(1, 1000)),
 			ThingID:   thingID,
 			ThingID:   thingID,
 			CreatedAt: gofakeit.Date(),
 			CreatedAt: gofakeit.Date(),
 		}
 		}
@@ -85,6 +87,7 @@ func TestUpdateThingHandler(t *testing.T) {
 		req                req
 		req                req
 		resCode            int
 		resCode            int
 		resBody            interface{}
 		resBody            interface{}
+		tmMock             func(mc *minimock.Controller) TransactionManager
 		thingRepoMock      func(mc *minimock.Controller) ThingRepository
 		thingRepoMock      func(mc *minimock.Controller) ThingRepository
 		placeThingRepoMock func(mc *minimock.Controller) PlaceThingRepository
 		placeThingRepoMock func(mc *minimock.Controller) PlaceThingRepository
 	}{
 	}{
@@ -93,19 +96,20 @@ func TestUpdateThingHandler(t *testing.T) {
 			req:     correctReq,
 			req:     correctReq,
 			resCode: fiber.StatusOK,
 			resCode: fiber.StatusOK,
 			resBody: expectedRes,
 			resBody: expectedRes,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
+				return mock
+			},
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				mock := mocks.NewThingRepositoryMock(mc)
 				mock := mocks.NewThingRepositoryMock(mc)
 
 
-				mock.BeginTxMock.Return(nil, nil)
-
-				mock.UpdateMock.Inspect(func(ctx context.Context, req models.UpdateThingRequest, tx *sql.Tx) {
+				mock.UpdateMock.Inspect(func(ctx context.Context, req models.UpdateThingRequest) {
 					assert.Equal(mc, title, req.Title)
 					assert.Equal(mc, title, req.Title)
 					assert.Equal(mc, description, req.Description)
 					assert.Equal(mc, description, req.Description)
 				}).Return(nil)
 				}).Return(nil)
 
 
-				mock.CommitTxMock.Return(nil)
-
-				mock.GetMock.Set(func(ctx context.Context, id int) (*models.Thing, error) {
+				mock.GetMock.Set(func(ctx context.Context, id uint64) (*models.Thing, error) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 					if mock.GetAfterCounter() == 0 {
 					if mock.GetAfterCounter() == 0 {
 						return &repoResBeforeUpdate, nil
 						return &repoResBeforeUpdate, nil
@@ -118,11 +122,11 @@ func TestUpdateThingHandler(t *testing.T) {
 			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
 			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
 				mock := mocks.NewPlaceThingRepositoryMock(mc)
 				mock := mocks.NewPlaceThingRepositoryMock(mc)
 
 
-				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 				}).Return(&placeThingRepoResBeforeUpdate, nil)
 				}).Return(&placeThingRepoResBeforeUpdate, nil)
 
 
-				mock.UpdatePlaceMock.Inspect(func(ctx context.Context, req models.UpdatePlaceThingRequest, tx *sql.Tx) {
+				mock.UpdatePlaceMock.Inspect(func(ctx context.Context, req models.UpdatePlaceThingRequest) {
 					assert.Equal(mc, placeID, req.PlaceID)
 					assert.Equal(mc, placeID, req.PlaceID)
 					assert.Equal(mc, thingID, req.ThingID)
 					assert.Equal(mc, thingID, req.ThingID)
 				}).Return(nil)
 				}).Return(nil)
@@ -137,6 +141,9 @@ func TestUpdateThingHandler(t *testing.T) {
 				route:  "/v1/things/" + gofakeit.Word(),
 				route:  "/v1/things/" + gofakeit.Word(),
 			},
 			},
 			resCode: fiber.StatusBadRequest,
 			resCode: fiber.StatusBadRequest,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				return mocks.NewTransactionManagerMock(mc)
+			},
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				return mocks.NewThingRepositoryMock(mc)
 				return mocks.NewThingRepositoryMock(mc)
 			},
 			},
@@ -148,9 +155,12 @@ func TestUpdateThingHandler(t *testing.T) {
 			name: "negative case - body parse error",
 			name: "negative case - body parse error",
 			req: req{
 			req: req{
 				method: fiber.MethodPut,
 				method: fiber.MethodPut,
-				route:  "/v1/things/" + strconv.Itoa(thingID),
+				route:  "/v1/things/" + strconv.FormatUint(thingID, 10),
 			},
 			},
 			resCode: fiber.StatusBadRequest,
 			resCode: fiber.StatusBadRequest,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				return mocks.NewTransactionManagerMock(mc)
+			},
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				return mocks.NewThingRepositoryMock(mc)
 				return mocks.NewThingRepositoryMock(mc)
 			},
 			},
@@ -162,7 +172,7 @@ func TestUpdateThingHandler(t *testing.T) {
 			name: "negative case - request without place_id",
 			name: "negative case - request without place_id",
 			req: req{
 			req: req{
 				method:      fiber.MethodPut,
 				method:      fiber.MethodPut,
-				route:       "/v1/things/" + strconv.Itoa(thingID),
+				route:       "/v1/things/" + strconv.FormatUint(thingID, 10),
 				contentType: fiber.MIMEApplicationJSON,
 				contentType: fiber.MIMEApplicationJSON,
 				body: &dto.UpdateThingRequest{
 				body: &dto.UpdateThingRequest{
 					Title: title,
 					Title: title,
@@ -175,6 +185,9 @@ func TestUpdateThingHandler(t *testing.T) {
 					Tag:   "required",
 					Tag:   "required",
 				},
 				},
 			},
 			},
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				return mocks.NewTransactionManagerMock(mc)
+			},
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				return mocks.NewThingRepositoryMock(mc)
 				return mocks.NewThingRepositoryMock(mc)
 			},
 			},
@@ -186,7 +199,7 @@ func TestUpdateThingHandler(t *testing.T) {
 			name: "negative case - request without title",
 			name: "negative case - request without title",
 			req: req{
 			req: req{
 				method:      fiber.MethodPut,
 				method:      fiber.MethodPut,
-				route:       "/v1/things/" + strconv.Itoa(thingID),
+				route:       "/v1/things/" + strconv.FormatUint(thingID, 10),
 				contentType: fiber.MIMEApplicationJSON,
 				contentType: fiber.MIMEApplicationJSON,
 				body: &dto.UpdateThingRequest{
 				body: &dto.UpdateThingRequest{
 					PlaceID: placeID,
 					PlaceID: placeID,
@@ -199,6 +212,9 @@ func TestUpdateThingHandler(t *testing.T) {
 					Tag:   "required",
 					Tag:   "required",
 				},
 				},
 			},
 			},
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				return mocks.NewTransactionManagerMock(mc)
+			},
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				return mocks.NewThingRepositoryMock(mc)
 				return mocks.NewThingRepositoryMock(mc)
 			},
 			},
@@ -209,11 +225,16 @@ func TestUpdateThingHandler(t *testing.T) {
 		{
 		{
 			name:    "negative case - repository error (get thing)",
 			name:    "negative case - repository error (get thing)",
 			req:     correctReq,
 			req:     correctReq,
-			resCode: fiber.StatusBadRequest,
+			resCode: fiber.StatusInternalServerError,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
+				return mock
+			},
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				mock := mocks.NewThingRepositoryMock(mc)
 				mock := mocks.NewThingRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 				}).Return(nil, sql.ErrNoRows)
 				}).Return(nil, sql.ErrNoRows)
 
 
@@ -226,47 +247,27 @@ func TestUpdateThingHandler(t *testing.T) {
 		{
 		{
 			name:    "negative case - repository error (get place thing)",
 			name:    "negative case - repository error (get place thing)",
 			req:     correctReq,
 			req:     correctReq,
-			resCode: fiber.StatusBadRequest,
-			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
-				mock := mocks.NewThingRepositoryMock(mc)
-
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
-					assert.Equal(mc, thingID, id)
-				}).Return(&repoResBeforeUpdate, nil)
-
-				return mock
-			},
-			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
-				mock := mocks.NewPlaceThingRepositoryMock(mc)
-
-				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id int) {
-					assert.Equal(mc, thingID, id)
-				}).Return(nil, sql.ErrNoRows)
-
+			resCode: fiber.StatusInternalServerError,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
 				return mock
 				return mock
 			},
 			},
-		},
-		{
-			name:    "negative case - repository error (begin tx)",
-			req:     correctReq,
-			resCode: fiber.StatusInternalServerError,
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				mock := mocks.NewThingRepositoryMock(mc)
 				mock := mocks.NewThingRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 				}).Return(&repoResBeforeUpdate, nil)
 				}).Return(&repoResBeforeUpdate, nil)
 
 
-				mock.BeginTxMock.Return(nil, testError)
-
 				return mock
 				return mock
 			},
 			},
 			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
 			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
 				mock := mocks.NewPlaceThingRepositoryMock(mc)
 				mock := mocks.NewPlaceThingRepositoryMock(mc)
 
 
-				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
-				}).Return(&placeThingRepoResBeforeUpdate, nil)
+				}).Return(nil, sql.ErrNoRows)
 
 
 				return mock
 				return mock
 			},
 			},
@@ -275,16 +276,19 @@ func TestUpdateThingHandler(t *testing.T) {
 			name:    "negative case - repository error (update thing)",
 			name:    "negative case - repository error (update thing)",
 			req:     correctReq,
 			req:     correctReq,
 			resCode: fiber.StatusInternalServerError,
 			resCode: fiber.StatusInternalServerError,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
+				return mock
+			},
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				mock := mocks.NewThingRepositoryMock(mc)
 				mock := mocks.NewThingRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 				}).Return(&repoResBeforeUpdate, nil)
 				}).Return(&repoResBeforeUpdate, nil)
 
 
-				mock.BeginTxMock.Return(nil, nil)
-
-				mock.UpdateMock.Inspect(func(ctx context.Context, req models.UpdateThingRequest, tx *sql.Tx) {
+				mock.UpdateMock.Inspect(func(ctx context.Context, req models.UpdateThingRequest) {
 					assert.Equal(mc, title, req.Title)
 					assert.Equal(mc, title, req.Title)
 					assert.Equal(mc, description, req.Description)
 					assert.Equal(mc, description, req.Description)
 				}).Return(testError)
 				}).Return(testError)
@@ -294,7 +298,7 @@ func TestUpdateThingHandler(t *testing.T) {
 			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
 			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
 				mock := mocks.NewPlaceThingRepositoryMock(mc)
 				mock := mocks.NewPlaceThingRepositoryMock(mc)
 
 
-				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 				}).Return(&placeThingRepoResBeforeUpdate, nil)
 				}).Return(&placeThingRepoResBeforeUpdate, nil)
 
 
@@ -305,70 +309,36 @@ func TestUpdateThingHandler(t *testing.T) {
 			name:    "negative case - repository error (update place)",
 			name:    "negative case - repository error (update place)",
 			req:     correctReq,
 			req:     correctReq,
 			resCode: fiber.StatusInternalServerError,
 			resCode: fiber.StatusInternalServerError,
-			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
-				mock := mocks.NewThingRepositoryMock(mc)
-
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
-					assert.Equal(mc, thingID, id)
-				}).Return(&repoResBeforeUpdate, nil)
-
-				mock.BeginTxMock.Return(nil, nil)
-
-				mock.UpdateMock.Inspect(func(ctx context.Context, req models.UpdateThingRequest, tx *sql.Tx) {
-					assert.Equal(mc, title, req.Title)
-					assert.Equal(mc, description, req.Description)
-				}).Return(nil)
-
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
 				return mock
 				return mock
 			},
 			},
-			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
-				mock := mocks.NewPlaceThingRepositoryMock(mc)
-
-				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id int) {
-					assert.Equal(mc, thingID, id)
-				}).Return(&placeThingRepoResBeforeUpdate, nil)
-
-				mock.UpdatePlaceMock.Inspect(func(ctx context.Context, req models.UpdatePlaceThingRequest, tx *sql.Tx) {
-					assert.Equal(mc, placeID, req.PlaceID)
-					assert.Equal(mc, thingID, req.ThingID)
-				}).Return(testError)
-
-				return mock
-			},
-		},
-		{
-			name:    "negative case - repository error (commit tx)",
-			req:     correctReq,
-			resCode: fiber.StatusInternalServerError,
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				mock := mocks.NewThingRepositoryMock(mc)
 				mock := mocks.NewThingRepositoryMock(mc)
 
 
-				mock.GetMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 				}).Return(&repoResBeforeUpdate, nil)
 				}).Return(&repoResBeforeUpdate, nil)
 
 
-				mock.BeginTxMock.Return(nil, nil)
-
-				mock.UpdateMock.Inspect(func(ctx context.Context, req models.UpdateThingRequest, tx *sql.Tx) {
+				mock.UpdateMock.Inspect(func(ctx context.Context, req models.UpdateThingRequest) {
 					assert.Equal(mc, title, req.Title)
 					assert.Equal(mc, title, req.Title)
 					assert.Equal(mc, description, req.Description)
 					assert.Equal(mc, description, req.Description)
 				}).Return(nil)
 				}).Return(nil)
 
 
-				mock.CommitTxMock.Return(testError)
-
 				return mock
 				return mock
 			},
 			},
 			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
 			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
 				mock := mocks.NewPlaceThingRepositoryMock(mc)
 				mock := mocks.NewPlaceThingRepositoryMock(mc)
 
 
-				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 				}).Return(&placeThingRepoResBeforeUpdate, nil)
 				}).Return(&placeThingRepoResBeforeUpdate, nil)
 
 
-				mock.UpdatePlaceMock.Inspect(func(ctx context.Context, req models.UpdatePlaceThingRequest, tx *sql.Tx) {
+				mock.UpdatePlaceMock.Inspect(func(ctx context.Context, req models.UpdatePlaceThingRequest) {
 					assert.Equal(mc, placeID, req.PlaceID)
 					assert.Equal(mc, placeID, req.PlaceID)
 					assert.Equal(mc, thingID, req.ThingID)
 					assert.Equal(mc, thingID, req.ThingID)
-				}).Return(nil)
+				}).Return(testError)
 
 
 				return mock
 				return mock
 			},
 			},
@@ -377,35 +347,36 @@ func TestUpdateThingHandler(t *testing.T) {
 			name:    "negative case - repository error (get thing)",
 			name:    "negative case - repository error (get thing)",
 			req:     correctReq,
 			req:     correctReq,
 			resCode: fiber.StatusInternalServerError,
 			resCode: fiber.StatusInternalServerError,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
+				return mock
+			},
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
 				mock := mocks.NewThingRepositoryMock(mc)
 				mock := mocks.NewThingRepositoryMock(mc)
 
 
-				mock.GetMock.Set(func(ctx context.Context, thingID int) (*models.Thing, error) {
+				mock.GetMock.Set(func(ctx context.Context, thingID uint64) (*models.Thing, error) {
 					if mock.GetAfterCounter() == 0 {
 					if mock.GetAfterCounter() == 0 {
 						return &repoResBeforeUpdate, nil
 						return &repoResBeforeUpdate, nil
 					}
 					}
 					return nil, sql.ErrNoRows
 					return nil, sql.ErrNoRows
 				})
 				})
 
 
-				mock.BeginTxMock.Return(nil, nil)
-
-				mock.UpdateMock.Inspect(func(ctx context.Context, req models.UpdateThingRequest, tx *sql.Tx) {
+				mock.UpdateMock.Inspect(func(ctx context.Context, req models.UpdateThingRequest) {
 					assert.Equal(mc, title, req.Title)
 					assert.Equal(mc, title, req.Title)
 					assert.Equal(mc, description, req.Description)
 					assert.Equal(mc, description, req.Description)
 				}).Return(nil)
 				}).Return(nil)
 
 
-				mock.CommitTxMock.Return(nil)
-
 				return mock
 				return mock
 			},
 			},
 			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
 			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
 				mock := mocks.NewPlaceThingRepositoryMock(mc)
 				mock := mocks.NewPlaceThingRepositoryMock(mc)
 
 
-				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id int) {
+				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
 					assert.Equal(mc, thingID, id)
 					assert.Equal(mc, thingID, id)
 				}).Return(&placeThingRepoResBeforeUpdate, nil)
 				}).Return(&placeThingRepoResBeforeUpdate, nil)
 
 
-				mock.UpdatePlaceMock.Inspect(func(ctx context.Context, req models.UpdatePlaceThingRequest, tx *sql.Tx) {
+				mock.UpdatePlaceMock.Inspect(func(ctx context.Context, req models.UpdatePlaceThingRequest) {
 					assert.Equal(mc, placeID, req.PlaceID)
 					assert.Equal(mc, placeID, req.PlaceID)
 					assert.Equal(mc, thingID, req.ThingID)
 					assert.Equal(mc, thingID, req.ThingID)
 				}).Return(nil)
 				}).Return(nil)
@@ -421,15 +392,19 @@ func TestUpdateThingHandler(t *testing.T) {
 
 
 			mc := minimock.NewController(t)
 			mc := minimock.NewController(t)
 			fiberApp := fiber.New()
 			fiberApp := fiber.New()
-			fiberApp.Put("/v1/things/:thingId", UpdateThingHandler(tt.thingRepoMock(mc), tt.placeThingRepoMock(mc)))
+			fiberApp.Put("/v1/things/:thingId", UpdateThingHandler(
+				tt.tmMock(mc),
+				tt.thingRepoMock(mc),
+				tt.placeThingRepoMock(mc),
+			))
 
 
-			fiberReq := httptest.NewRequest(tt.req.method, tt.req.route, helpers.ConvertDataToIOReader(tt.req.body))
+			fiberReq := httptest.NewRequest(tt.req.method, tt.req.route, test.ConvertDataToIOReader(tt.req.body))
 			fiberReq.Header.Add(fiber.HeaderContentType, tt.req.contentType)
 			fiberReq.Header.Add(fiber.HeaderContentType, tt.req.contentType)
-			fiberRes, _ := fiberApp.Test(fiberReq, API.DefaultTestTimeOut)
+			fiberRes, _ := fiberApp.Test(fiberReq, test.TestTimeout)
 
 
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
 			if tt.resBody != nil {
 			if tt.resBody != nil {
-				assert.Equal(t, helpers.MarshalResponse(tt.resBody), helpers.ConvertBodyToString(fiberRes.Body))
+				assert.Equal(t, test.MarshalResponse(tt.resBody), test.ConvertBodyToString(fiberRes.Body))
 			}
 			}
 		})
 		})
 	}
 	}

+ 20 - 0
internal/helpers/request/convert.go

@@ -0,0 +1,20 @@
+package request
+
+import (
+	"errors"
+
+	"github.com/gofiber/fiber/v2"
+)
+
+func ConvertToUint64(fctx *fiber.Ctx, key string) (uint64, error) {
+	val, err := fctx.ParamsInt(key)
+	if err != nil {
+		return 0, err
+	}
+
+	if val < 0 {
+		return 0, errors.New("value must be positive")
+	}
+
+	return uint64(val), nil
+}