feat: add product list api

This commit is contained in:
amir-ys 2026-04-29 01:53:23 +03:30
parent 0948b36012
commit c01d3678ae
11 changed files with 191 additions and 25 deletions

View File

@ -1,6 +1,23 @@
package errmsg
type ErrorResponse struct {
Message string `json:"message"` // General error message
Errors map[string]interface{} `json:"errors,omitempty"` // Additional detail of error
InternalErrCode string `json:"internal_error_code,omitempty"` // Custom error code (optional)
}
func (e ErrorResponse) Error() string {
return e.Message
}
const (
ErrValidationFailed = "input validation failed"
ErrUnexpectedError = "unexpected error occurred"
ErrInvalidRequestFormat = "invalid request format"
ErrGetUserInfo = "get user info failed"
ErrFailedDecodeBase64 = "decode data to base 64 failed"
ErrFailedUnmarshalJson = "unmarshal data to JSON failed"
ErrUnauthorized = "unauthorized"
ErrorMsgAdminNotAllowed = "admin is not allowed"
ErrorMsgNotFound = "record not found"
ErrorMsgSomethingWentWrong = "something went wrong"

View File

@ -3,6 +3,7 @@ package productapp
import (
"context"
"fmt"
"git.gocasts.ir/ebhomengo/niki/productapp/service/product"
"log"
httpserver "git.gocasts.ir/ebhomengo/niki/delivery/http_server"
@ -25,6 +26,7 @@ type Config struct {
type Application struct {
Config Config
HTTPServer *producthttp.Server
productSvc product.Service
DB *productdb.DB
Echo *echo.Echo
}
@ -40,11 +42,14 @@ func Setup(cfg Config) *Application {
Router: e,
}
srv := producthttp.NewServer(hsrv)
productSvc := product.New(productDB)
srv := producthttp.NewServer(hsrv, productSvc)
srv.RegisterRoutes()
return &Application{
Config: cfg,
productSvc: productSvc,
HTTPServer: srv,
DB: productDB,
Echo: e,

View File

@ -1,17 +1,38 @@
package http
import (
"net/http"
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
"git.gocasts.ir/ebhomengo/niki/productapp/service/product"
"github.com/labstack/echo/v4"
"log/slog"
"net/http"
)
type Handler struct{}
func NewHandler() *Handler {
return &Handler{}
type Handler struct {
productService product.Service
Logger *slog.Logger
}
func (h Handler) HealthCheck(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]string{"status": "ok"})
func NewHandler(productService product.Service) *Handler {
return &Handler{
productService: productService,
}
}
func (h Handler) getProductList(c echo.Context) error {
var req product.GetProductListRequest
if err := c.Bind(&req); err != nil {
return c.JSON(http.StatusBadRequest, errmsg.ErrorResponse{
Message: errmsg.ErrInvalidRequestFormat,
})
}
response, err := h.productService.GetProducts(c.Request().Context(), req)
if err != nil {
// todo handle validation error
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
return c.JSON(http.StatusOK, response)
}

View File

@ -0,0 +1,11 @@
package http
import (
"net/http"
"github.com/labstack/echo/v4"
)
func (h Handler) HealthCheck(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]string{"status": "healthy"})
}

View File

@ -1 +0,0 @@
package http

View File

@ -1,16 +1,19 @@
package http
import httpserver "git.gocasts.ir/ebhomengo/niki/delivery/http_server"
import (
httpserver "git.gocasts.ir/ebhomengo/niki/delivery/http_server"
"git.gocasts.ir/ebhomengo/niki/productapp/service/product"
)
type Server struct {
HTTPServer *httpserver.Server
Handler *Handler
}
func NewServer(httpserver *httpserver.Server) *Server {
func NewServer(httpserver *httpserver.Server, productSvc product.Service) *Server {
return &Server{
HTTPServer: httpserver,
Handler: NewHandler(),
Handler: NewHandler(productSvc),
}
}
@ -22,5 +25,10 @@ func (s *Server) Stop() {}
func (s *Server) RegisterRoutes() {
r := s.HTTPServer.Router
r.GET("health-check", s.Handler.HealthCheck)
r.GET("health-check/1", s.Handler.HealthCheck)
productGroup := r.Group("/v1/products")
productGroup.GET("", s.Handler.getProductList)
}

View File

@ -1,6 +1,11 @@
package database
import "git.gocasts.ir/ebhomengo/niki/repository/mysql"
import (
"context"
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
"git.gocasts.ir/ebhomengo/niki/productapp/service/product"
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
)
type DB struct {
conn *mysql.DB
@ -11,3 +16,42 @@ func New(conn *mysql.DB) *DB {
conn: conn,
}
}
func (db *DB) GetProducts(ctx context.Context, page, pageSize uint) ([]product.Product, error) {
const Op = "domain.repository.mysql.order.get-shipping"
query := "SELECT * FROM products ORDER BY id DESC limit ? offset ?"
rows, err := db.conn.Conn().Query(query, page, pageSize)
if rows.Err() != nil {
return []product.Product{}, richerror.New(Op).WithErr(err)
}
defer rows.Close()
var products []product.Product
for rows.Next() {
var s product.Product
err := rows.Scan(
&s.ID,
&s.Name,
&s.Slug,
&s.Description,
&s.Price,
&s.Stock,
&s.IsActive,
&s.Features,
&s.CreatedAt,
&s.DeletedAt,
)
if err != nil {
return nil, richerror.New(Op).WithErr(err)
}
products = append(products, s)
}
return products, nil
}

View File

@ -6,16 +6,16 @@ import (
)
type Product struct {
ID uint
Name string
Slug string
Description string
Price float64
Stock int
IsActive bool
Features string
CreatedAt time.Time
DeletedAt sql.NullTime
ID uint `json:"id"`
Name string `json:"name"`
Slug string `json:"slug"`
Description string `json:"description"`
Price float64 `json:"price"`
Stock int `json:"stock"`
IsActive bool `json:"is_active"`
Features string `json:"features"`
CreatedAt time.Time `json:"created_at"`
DeletedAt sql.NullTime `json:"-"`
}
type ProductImage struct {

View File

@ -0,0 +1,13 @@
package product
import "errors"
var (
ErrFailedGetProductList = errors.New("failed to get products list")
ErrFailedCreateProduct = errors.New("failed to create product")
ErrFailedFindProduct = errors.New("failed to find product")
ErrCantAccessToUpdateProduct = errors.New("you are not allowed to edit this product")
ErrFailedUpdateProduct = errors.New("failed to update product")
ErrFailedToPreparedStatement = errors.New("failed to prepared statement: %v")
ErrCantAccess = errors.New("you are not allowed to this action")
)

View File

@ -1 +1,12 @@
package product
import "git.gocasts.ir/ebhomengo/niki/param"
type GetProductListRequest struct {
param.PaginationRequest
}
type GetProductListResponse struct {
Data []Product `json:"data"`
param.PaginationResponse
}

View File

@ -1 +1,38 @@
package product
import (
"context"
"fmt"
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
)
type Repository interface {
GetProducts(ctx context.Context, page, pageSize uint) ([]Product, error)
}
type Service struct {
repository Repository
}
func New(repo Repository) Service {
return Service{
repository: repo,
}
}
func (s Service) GetProducts(ctx context.Context, req GetProductListRequest) (GetProductListResponse, error) {
page := req.GetPageNumber()
pageSize := req.PageSize
products, err := s.repository.GetProducts(ctx, page, pageSize)
if err != nil {
fmt.Println(err)
return GetProductListResponse{}, errmsg.ErrorResponse{
Message: ErrFailedGetProductList.Error(),
Errors: map[string]interface{}{"repository_error": err.Error()},
}
}
return GetProductListResponse{Data: products}, nil
}