refactor(marketplace): enforce unified api response envelope
This commit is contained in:
parent
e7ae13736b
commit
af0e42c656
5 changed files with 56 additions and 95 deletions
|
|
@ -64,19 +64,13 @@ const docTemplate = `{
|
|||
"403": {
|
||||
"description": "No license",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
"$ref": "#/definitions/veza-backend-api_internal_response.APIResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
"$ref": "#/definitions/veza-backend-api_internal_response.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -119,21 +113,15 @@ const docTemplate = `{
|
|||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"description": "Validation Error",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
"$ref": "#/definitions/veza-backend-api_internal_response.APIResponse"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
"$ref": "#/definitions/veza-backend-api_internal_response.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -214,21 +202,15 @@ const docTemplate = `{
|
|||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"description": "Validation Error",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
"$ref": "#/definitions/veza-backend-api_internal_response.APIResponse"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
"$ref": "#/definitions/veza-backend-api_internal_response.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,19 +58,13 @@
|
|||
"403": {
|
||||
"description": "No license",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
"$ref": "#/definitions/veza-backend-api_internal_response.APIResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
"$ref": "#/definitions/veza-backend-api_internal_response.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -113,21 +107,15 @@
|
|||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"description": "Validation Error",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
"$ref": "#/definitions/veza-backend-api_internal_response.APIResponse"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
"$ref": "#/definitions/veza-backend-api_internal_response.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -208,21 +196,15 @@
|
|||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"description": "Validation Error",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
"$ref": "#/definitions/veza-backend-api_internal_response.APIResponse"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
"$ref": "#/definitions/veza-backend-api_internal_response.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -558,15 +558,11 @@ paths:
|
|||
"403":
|
||||
description: No license
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
$ref: '#/definitions/veza-backend-api_internal_response.APIResponse'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
$ref: '#/definitions/veza-backend-api_internal_response.APIResponse'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Get download URL
|
||||
|
|
@ -592,17 +588,13 @@ paths:
|
|||
schema:
|
||||
$ref: '#/definitions/veza-backend-api_internal_core_marketplace.Order'
|
||||
"400":
|
||||
description: Bad Request
|
||||
description: Validation Error
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
$ref: '#/definitions/veza-backend-api_internal_response.APIResponse'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
$ref: '#/definitions/veza-backend-api_internal_response.APIResponse'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Create a new order
|
||||
|
|
@ -653,17 +645,13 @@ paths:
|
|||
schema:
|
||||
$ref: '#/definitions/veza-backend-api_internal_core_marketplace.Product'
|
||||
"400":
|
||||
description: Bad Request
|
||||
description: Validation Error
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
$ref: '#/definitions/veza-backend-api_internal_response.APIResponse'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
$ref: '#/definitions/veza-backend-api_internal_response.APIResponse'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Create a new product
|
||||
|
|
|
|||
|
|
@ -67,3 +67,11 @@ func NewUnauthorizedError(message string) *AppError {
|
|||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
// NewForbiddenError crée une nouvelle erreur "forbidden"
|
||||
func NewForbiddenError(message string) *AppError {
|
||||
return &AppError{
|
||||
Code: ErrCodeForbidden,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"go.uber.org/zap"
|
||||
"veza-backend-api/internal/core/marketplace"
|
||||
"veza-backend-api/internal/response"
|
||||
)
|
||||
|
||||
// MarketplaceHandler gère les opérations de la marketplace
|
||||
|
|
@ -43,8 +42,9 @@ type CreateProductRequest struct {
|
|||
// @Security BearerAuth
|
||||
// @Param product body CreateProductRequest true "Product info"
|
||||
// @Success 201 {object} marketplace.Product
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 401 {object} map[string]string
|
||||
// @Success 201 {object} marketplace.Product
|
||||
// @Failure 400 {object} response.APIResponse "Validation Error"
|
||||
// @Failure 401 {object} response.APIResponse "Unauthorized"
|
||||
// @Router /api/v1/marketplace/products [post]
|
||||
func (h *MarketplaceHandler) CreateProduct(c *gin.Context) {
|
||||
userID := c.MustGet("user_id").(uuid.UUID)
|
||||
|
|
@ -68,7 +68,7 @@ func (h *MarketplaceHandler) CreateProduct(c *gin.Context) {
|
|||
if req.TrackID != "" {
|
||||
trackUUID, err := uuid.Parse(req.TrackID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid track_id format"})
|
||||
response.BadRequest(c, "Invalid track_id format")
|
||||
return
|
||||
}
|
||||
product.TrackID = &trackUUID
|
||||
|
|
@ -76,18 +76,18 @@ func (h *MarketplaceHandler) CreateProduct(c *gin.Context) {
|
|||
|
||||
if err := h.service.CreateProduct(c.Request.Context(), product); err != nil {
|
||||
if err == marketplace.ErrInvalidSeller {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "You do not own this track"})
|
||||
response.Forbidden(c, "You do not own this track")
|
||||
return
|
||||
}
|
||||
if err == marketplace.ErrTrackNotFound {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Track not found"})
|
||||
response.NotFound(c, "Track not found")
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create product"})
|
||||
response.InternalServerError(c, "Failed to create product")
|
||||
return
|
||||
}
|
||||
|
||||
RespondSuccess(c, http.StatusCreated, product)
|
||||
response.Created(c, product)
|
||||
}
|
||||
|
||||
// CreateOrderRequest DTO pour la création de commande
|
||||
|
|
@ -106,8 +106,9 @@ type CreateOrderRequest struct {
|
|||
// @Security BearerAuth
|
||||
// @Param order body CreateOrderRequest true "Order items"
|
||||
// @Success 201 {object} marketplace.Order
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 401 {object} map[string]string
|
||||
// @Success 201 {object} marketplace.Order
|
||||
// @Failure 400 {object} response.APIResponse "Validation Error"
|
||||
// @Failure 401 {object} response.APIResponse "Unauthorized"
|
||||
// @Router /api/v1/marketplace/orders [post]
|
||||
func (h *MarketplaceHandler) CreateOrder(c *gin.Context) {
|
||||
buyerID := c.MustGet("user_id").(uuid.UUID)
|
||||
|
|
@ -122,7 +123,7 @@ func (h *MarketplaceHandler) CreateOrder(c *gin.Context) {
|
|||
for _, item := range req.Items {
|
||||
pid, err := uuid.Parse(item.ProductID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid product_id: " + item.ProductID})
|
||||
response.BadRequest(c, "Invalid product_id: "+item.ProductID)
|
||||
return
|
||||
}
|
||||
items = append(items, marketplace.NewOrderItem{ProductID: pid})
|
||||
|
|
@ -130,11 +131,11 @@ func (h *MarketplaceHandler) CreateOrder(c *gin.Context) {
|
|||
|
||||
order, err := h.service.CreateOrder(c.Request.Context(), buyerID, items)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
RespondSuccess(c, http.StatusCreated, order)
|
||||
response.Created(c, order)
|
||||
}
|
||||
|
||||
// GetDownloadURL récupère l'URL de téléchargement pour un achat
|
||||
|
|
@ -146,8 +147,8 @@ func (h *MarketplaceHandler) CreateOrder(c *gin.Context) {
|
|||
// @Security BearerAuth
|
||||
// @Param product_id path string true "Product ID"
|
||||
// @Success 200 {object} map[string]string
|
||||
// @Failure 403 {object} map[string]string "No license"
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Failure 403 {object} response.APIResponse "No license"
|
||||
// @Failure 404 {object} response.APIResponse "Not Found"
|
||||
// @Router /api/v1/marketplace/download/{product_id} [get]
|
||||
func (h *MarketplaceHandler) GetDownloadURL(c *gin.Context) {
|
||||
userID := c.MustGet("user_id").(uuid.UUID)
|
||||
|
|
@ -155,25 +156,25 @@ func (h *MarketplaceHandler) GetDownloadURL(c *gin.Context) {
|
|||
|
||||
productID, err := uuid.Parse(productIDStr)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid product_id"})
|
||||
response.BadRequest(c, "Invalid product_id")
|
||||
return
|
||||
}
|
||||
|
||||
url, err := h.service.GetDownloadURL(c.Request.Context(), userID, productID)
|
||||
if err != nil {
|
||||
if err == marketplace.ErrNoLicense {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "No valid license for this product"})
|
||||
response.Forbidden(c, "No valid license for this product")
|
||||
return
|
||||
}
|
||||
if err == marketplace.ErrTrackNotFound {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Track file not found"})
|
||||
response.NotFound(c, "Track file not found")
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get download URL"})
|
||||
response.InternalServerError(c, "Failed to get download URL")
|
||||
return
|
||||
}
|
||||
|
||||
RespondSuccess(c, http.StatusOK, gin.H{"url": url})
|
||||
response.Success(c, gin.H{"url": url})
|
||||
}
|
||||
|
||||
// ListProducts liste les produits
|
||||
|
|
@ -198,9 +199,9 @@ func (h *MarketplaceHandler) ListProducts(c *gin.Context) {
|
|||
|
||||
products, err := h.service.ListProducts(c.Request.Context(), filters)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list products"})
|
||||
response.InternalServerError(c, "Failed to list products")
|
||||
return
|
||||
}
|
||||
|
||||
RespondSuccess(c, http.StatusOK, products)
|
||||
response.Success(c, products)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue