forked from ebhomengo/niki
feat: add product list api
This commit is contained in:
parent
0948b36012
commit
c01d3678ae
|
|
@ -1,6 +1,23 @@
|
||||||
package errmsg
|
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 (
|
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"
|
ErrorMsgAdminNotAllowed = "admin is not allowed"
|
||||||
ErrorMsgNotFound = "record not found"
|
ErrorMsgNotFound = "record not found"
|
||||||
ErrorMsgSomethingWentWrong = "something went wrong"
|
ErrorMsgSomethingWentWrong = "something went wrong"
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package productapp
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.gocasts.ir/ebhomengo/niki/productapp/service/product"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
httpserver "git.gocasts.ir/ebhomengo/niki/delivery/http_server"
|
httpserver "git.gocasts.ir/ebhomengo/niki/delivery/http_server"
|
||||||
|
|
@ -25,6 +26,7 @@ type Config struct {
|
||||||
type Application struct {
|
type Application struct {
|
||||||
Config Config
|
Config Config
|
||||||
HTTPServer *producthttp.Server
|
HTTPServer *producthttp.Server
|
||||||
|
productSvc product.Service
|
||||||
DB *productdb.DB
|
DB *productdb.DB
|
||||||
Echo *echo.Echo
|
Echo *echo.Echo
|
||||||
}
|
}
|
||||||
|
|
@ -40,11 +42,14 @@ func Setup(cfg Config) *Application {
|
||||||
Router: e,
|
Router: e,
|
||||||
}
|
}
|
||||||
|
|
||||||
srv := producthttp.NewServer(hsrv)
|
productSvc := product.New(productDB)
|
||||||
|
|
||||||
|
srv := producthttp.NewServer(hsrv, productSvc)
|
||||||
srv.RegisterRoutes()
|
srv.RegisterRoutes()
|
||||||
|
|
||||||
return &Application{
|
return &Application{
|
||||||
Config: cfg,
|
Config: cfg,
|
||||||
|
productSvc: productSvc,
|
||||||
HTTPServer: srv,
|
HTTPServer: srv,
|
||||||
DB: productDB,
|
DB: productDB,
|
||||||
Echo: e,
|
Echo: e,
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,38 @@
|
||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
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"
|
"github.com/labstack/echo/v4"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Handler struct{}
|
type Handler struct {
|
||||||
|
productService product.Service
|
||||||
func NewHandler() *Handler {
|
Logger *slog.Logger
|
||||||
return &Handler{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h Handler) HealthCheck(c echo.Context) error {
|
func NewHandler(productService product.Service) *Handler {
|
||||||
return c.JSON(http.StatusOK, map[string]string{"status": "ok"})
|
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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"})
|
||||||
|
}
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
package http
|
|
||||||
|
|
@ -1,16 +1,19 @@
|
||||||
package http
|
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 {
|
type Server struct {
|
||||||
HTTPServer *httpserver.Server
|
HTTPServer *httpserver.Server
|
||||||
Handler *Handler
|
Handler *Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(httpserver *httpserver.Server) *Server {
|
func NewServer(httpserver *httpserver.Server, productSvc product.Service) *Server {
|
||||||
return &Server{
|
return &Server{
|
||||||
HTTPServer: httpserver,
|
HTTPServer: httpserver,
|
||||||
Handler: NewHandler(),
|
Handler: NewHandler(productSvc),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -22,5 +25,10 @@ func (s *Server) Stop() {}
|
||||||
|
|
||||||
func (s *Server) RegisterRoutes() {
|
func (s *Server) RegisterRoutes() {
|
||||||
r := s.HTTPServer.Router
|
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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,11 @@
|
||||||
package database
|
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 {
|
type DB struct {
|
||||||
conn *mysql.DB
|
conn *mysql.DB
|
||||||
|
|
@ -11,3 +16,42 @@ func New(conn *mysql.DB) *DB {
|
||||||
conn: conn,
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,16 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Product struct {
|
type Product struct {
|
||||||
ID uint
|
ID uint `json:"id"`
|
||||||
Name string
|
Name string `json:"name"`
|
||||||
Slug string
|
Slug string `json:"slug"`
|
||||||
Description string
|
Description string `json:"description"`
|
||||||
Price float64
|
Price float64 `json:"price"`
|
||||||
Stock int
|
Stock int `json:"stock"`
|
||||||
IsActive bool
|
IsActive bool `json:"is_active"`
|
||||||
Features string
|
Features string `json:"features"`
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time `json:"created_at"`
|
||||||
DeletedAt sql.NullTime
|
DeletedAt sql.NullTime `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProductImage struct {
|
type ProductImage struct {
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
)
|
||||||
|
|
@ -1 +1,12 @@
|
||||||
package product
|
package product
|
||||||
|
|
||||||
|
import "git.gocasts.ir/ebhomengo/niki/param"
|
||||||
|
|
||||||
|
type GetProductListRequest struct {
|
||||||
|
param.PaginationRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetProductListResponse struct {
|
||||||
|
Data []Product `json:"data"`
|
||||||
|
param.PaginationResponse
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,38 @@
|
||||||
package product
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue