add patch widget router

This commit is contained in:
harold 2025-03-06 17:12:39 +05:00
parent 0d4a2d84d9
commit 06b9624c12
11 changed files with 488 additions and 11 deletions

View File

@ -63,7 +63,7 @@ func (pg *Postgres) SelectOne(ctx context.Context, query string, args ...interfa
}
func (pg *Postgres) Update(ctx context.Context, query string, args ...interface{}) error {
_, err := pg.db.Query(ctx, query, args...)
_, err := pg.db.Exec(ctx, query, args...)
if err != nil {
return err
}

View File

@ -7,12 +7,13 @@ import (
"github.com/labstack/echo/v4"
"log/slog"
"net/http"
"strconv"
)
// CreateWidget godoc
// @Summary Create new widget
// @Description Create new widget
// @Tags GetWidgetDb
// @Tags Widget
// @Accept json
// @Produce json
// @Security BearerAuth
@ -62,7 +63,7 @@ func CreateWidget(widgetService model.WidgetService) echo.HandlerFunc {
// GetStreamersWidgets godoc
// @Summary Get all streamer's widgets
// @Description Get all streamer's widgets
// @Tags GetWidgetDb
// @Tags Widget
// @Accept json
// @Produce json
// @Security BearerAuth
@ -101,6 +102,58 @@ func GetStreamersWidgets(widgetService model.WidgetService) echo.HandlerFunc {
}
}
// UpdateWidget godoc
// @Summary Update an existing widget
// @Description Update an existing widget
// @Tags Widget
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param widget_id path int true "Widget ID"
// @Param request body model.UpdateWidget true "Update widget"
// @Success 200 {string} string "Widget has been updated successfully!"
// @Failure 400 {object} echo.HTTPError "Bad request"
// @Failure 401 {object} echo.HTTPError "Unauthorized or expired token"
// @Failure 422 {object} echo.HTTPError "Validation error"
// @Failure 404 {object} echo.HTTPError "Widget not found"
// @Router /widgets/{widget_id} [patch]
func UpdateWidget(widgetService model.WidgetService) echo.HandlerFunc {
return func(request echo.Context) error {
ctx := context.Background()
widgetIDStr := request.Param("widgetID")
widgetID, err := strconv.Atoi(widgetIDStr)
if err != nil {
return echo.NewHTTPError(http.StatusUnprocessableEntity, "Invalid widget ID")
}
var body model.UpdateWidget
err = validator.ParseAndValidate(&body, request)
if err != nil {
return echo.NewHTTPError(http.StatusUnprocessableEntity, err.Error())
}
authData, err := widgetService.CheckToken(request)
if err != nil {
slog.Error(err.Error())
return echo.NewHTTPError(http.StatusUnauthorized, err.Error())
}
err = widgetService.UpdateWidget(
ctx,
body,
widgetID,
authData.AccountID,
)
if err != nil {
slog.Error(err.Error())
return echo.NewHTTPError(http.StatusInternalServerError, "Internal Server Error")
}
return request.JSON(http.StatusOK, "Widget has been updated successfully!")
}
}
//func GetWidgetHTML(widgetService model.WidgetService) echo.HandlerFunc {
// return func(request echo.Context) error {
// ctx := context.Background()

View File

@ -86,7 +86,7 @@ func IncludeWidgetHandlers(
) {
server.POST(PREFIX+"/widgets", widget.CreateWidget(widgetService))
server.GET(PREFIX+"/widgets", widget.GetStreamersWidgets(widgetService))
server.PATCH(PREFIX+"/widgets/:widgetID", widget.UpdateWidget(widgetService))
//server.GET(PREFIX+"/html/:streamerID", model.GetWidgetHTML(widgetService))
//server.GET(PREFIX+"/info/:widgetID", model.GetWidgetInfo(widgetService))
//

View File

@ -628,7 +628,7 @@ const docTemplate = `{
"application/json"
],
"tags": [
"GetWidgetDb"
"Widget"
],
"summary": "Get all streamer's widgets",
"responses": {
@ -675,7 +675,7 @@ const docTemplate = `{
"application/json"
],
"tags": [
"GetWidgetDb"
"Widget"
],
"summary": "Create new widget",
"parameters": [
@ -716,6 +716,76 @@ const docTemplate = `{
}
}
}
},
"/widgets/{widget_id}": {
"patch": {
"security": [
{
"BearerAuth": []
}
],
"description": "Update an existing widget",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Widget"
],
"summary": "Update an existing widget",
"parameters": [
{
"type": "integer",
"description": "Widget ID",
"name": "widget_id",
"in": "path",
"required": true
},
{
"description": "Update widget",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/donat-widget_internal_model.UpdateWidget"
}
}
],
"responses": {
"200": {
"description": "Widget has been updated successfully!",
"schema": {
"type": "string"
}
},
"400": {
"description": "Bad request",
"schema": {
"$ref": "#/definitions/echo.HTTPError"
}
},
"401": {
"description": "Unauthorized or expired token",
"schema": {
"$ref": "#/definitions/echo.HTTPError"
}
},
"404": {
"description": "Widget not found",
"schema": {
"$ref": "#/definitions/echo.HTTPError"
}
},
"422": {
"description": "Validation error",
"schema": {
"$ref": "#/definitions/echo.HTTPError"
}
}
}
}
}
},
"definitions": {
@ -784,6 +854,9 @@ const docTemplate = `{
"id": {
"type": "string"
},
"size": {
"type": "number"
},
"streamer_id": {
"type": "integer"
}
@ -1015,6 +1088,39 @@ const docTemplate = `{
}
}
},
"donat-widget_internal_model.UpdateWidget": {
"type": "object",
"properties": {
"audio": {
"type": "string",
"example": "a0f9e244-f61f-4bfe-a7a0-3b5e91fe7364"
},
"duration": {
"type": "integer",
"example": 120
},
"image": {
"type": "string",
"example": "d2c2f03f-3fe5-4bfc-b963-5478a057149e"
},
"is_active": {
"type": "boolean",
"example": true
},
"max_amount": {
"type": "integer",
"example": 100
},
"min_amount": {
"type": "integer",
"example": 10
},
"name": {
"type": "string",
"example": "Awesome Widget"
}
}
},
"donat-widget_internal_model.VoiceSettingsResponse": {
"type": "object",
"properties": {

View File

@ -621,7 +621,7 @@
"application/json"
],
"tags": [
"GetWidgetDb"
"Widget"
],
"summary": "Get all streamer's widgets",
"responses": {
@ -668,7 +668,7 @@
"application/json"
],
"tags": [
"GetWidgetDb"
"Widget"
],
"summary": "Create new widget",
"parameters": [
@ -709,6 +709,76 @@
}
}
}
},
"/widgets/{widget_id}": {
"patch": {
"security": [
{
"BearerAuth": []
}
],
"description": "Update an existing widget",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Widget"
],
"summary": "Update an existing widget",
"parameters": [
{
"type": "integer",
"description": "Widget ID",
"name": "widget_id",
"in": "path",
"required": true
},
{
"description": "Update widget",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/donat-widget_internal_model.UpdateWidget"
}
}
],
"responses": {
"200": {
"description": "Widget has been updated successfully!",
"schema": {
"type": "string"
}
},
"400": {
"description": "Bad request",
"schema": {
"$ref": "#/definitions/echo.HTTPError"
}
},
"401": {
"description": "Unauthorized or expired token",
"schema": {
"$ref": "#/definitions/echo.HTTPError"
}
},
"404": {
"description": "Widget not found",
"schema": {
"$ref": "#/definitions/echo.HTTPError"
}
},
"422": {
"description": "Validation error",
"schema": {
"$ref": "#/definitions/echo.HTTPError"
}
}
}
}
}
},
"definitions": {
@ -777,6 +847,9 @@
"id": {
"type": "string"
},
"size": {
"type": "number"
},
"streamer_id": {
"type": "integer"
}
@ -1008,6 +1081,39 @@
}
}
},
"donat-widget_internal_model.UpdateWidget": {
"type": "object",
"properties": {
"audio": {
"type": "string",
"example": "a0f9e244-f61f-4bfe-a7a0-3b5e91fe7364"
},
"duration": {
"type": "integer",
"example": 120
},
"image": {
"type": "string",
"example": "d2c2f03f-3fe5-4bfc-b963-5478a057149e"
},
"is_active": {
"type": "boolean",
"example": true
},
"max_amount": {
"type": "integer",
"example": 100
},
"min_amount": {
"type": "integer",
"example": 10
},
"name": {
"type": "string",
"example": "Awesome Widget"
}
}
},
"donat-widget_internal_model.VoiceSettingsResponse": {
"type": "object",
"properties": {

View File

@ -48,6 +48,8 @@ definitions:
type: string
id:
type: string
size:
type: number
streamer_id:
type: integer
type: object
@ -209,6 +211,30 @@ definitions:
voice_speed:
type: integer
type: object
donat-widget_internal_model.UpdateWidget:
properties:
audio:
example: a0f9e244-f61f-4bfe-a7a0-3b5e91fe7364
type: string
duration:
example: 120
type: integer
image:
example: d2c2f03f-3fe5-4bfc-b963-5478a057149e
type: string
is_active:
example: true
type: boolean
max_amount:
example: 100
type: integer
min_amount:
example: 10
type: integer
name:
example: Awesome Widget
type: string
type: object
donat-widget_internal_model.VoiceSettingsResponse:
properties:
min_price:
@ -649,7 +675,7 @@ paths:
- BearerAuth: []
summary: Get all streamer's widgets
tags:
- GetWidgetDb
- Widget
post:
consumes:
- application/json
@ -684,7 +710,52 @@ paths:
- BearerAuth: []
summary: Create new widget
tags:
- GetWidgetDb
- Widget
/widgets/{widget_id}:
patch:
consumes:
- application/json
description: Update an existing widget
parameters:
- description: Widget ID
in: path
name: widget_id
required: true
type: integer
- description: Update widget
in: body
name: request
required: true
schema:
$ref: '#/definitions/donat-widget_internal_model.UpdateWidget'
produces:
- application/json
responses:
"200":
description: Widget has been updated successfully!
schema:
type: string
"400":
description: Bad request
schema:
$ref: '#/definitions/echo.HTTPError'
"401":
description: Unauthorized or expired token
schema:
$ref: '#/definitions/echo.HTTPError'
"404":
description: Widget not found
schema:
$ref: '#/definitions/echo.HTTPError'
"422":
description: Validation error
schema:
$ref: '#/definitions/echo.HTTPError'
security:
- BearerAuth: []
summary: Update an existing widget
tags:
- Widget
securityDefinitions:
BearerAuth:
in: header

View File

@ -2,6 +2,7 @@ package model
import (
"donat-widget/internal/model/api"
"github.com/google/uuid"
"mime/multipart"
)
@ -25,6 +26,7 @@ type WidgetService interface {
name string,
) (WidgetID, error)
GetWidgetsByStreamer(ctx context.Context, streamerID int) ([]*WidgetWithFileLink, error)
UpdateWidget(ctx context.Context, updateWidget UpdateWidget, widgetID, accountID int) error
//GetWidgetByID(ctx context.Context, widgetID WidgetID) ([]*GetWidgetDb, error)
//GetWidgetHTML(ctx context.Context, streamerID StreamerID) (WidgetHTML, error)
@ -47,7 +49,18 @@ type WidgetRepo interface {
) (WidgetID, error)
CheckWidgetName(ctx context.Context, streamerID int, name string) (bool, error)
GetWidgetsByStreamerID(ctx context.Context, streamerID int) ([]*GetWidgetDb, error)
UpdateWidget(
ctx context.Context,
widgetID int,
duration *int,
minAmount *int,
maxAmount *int,
isActive *bool,
image *uuid.UUID,
audio *uuid.UUID,
name *string,
) error
WidgetByID(ctx context.Context, widgetID, accountID int) error
//GetWidgetByID(ctx context.Context, widgetID WidgetID) ([]*GetWidgetDb, error)
//GetAllWidget(ctx context.Context, streamerID StreamerID) ([]*GetWidgetDb, error)
//GetMediaFile(fileID FileID) (DownloadFile, error)

View File

@ -46,6 +46,16 @@ type WidgetWithFileLink struct {
Widgets []*GetWidgetDb `json:"widgets"`
}
type UpdateWidget struct {
IsActive *bool `json:"is_active" example:"true" description:"Indicates whether the widget is active or not"`
Name *string `json:"name" example:"Awesome Widget" description:"Name of the widget"`
Image *uuid.UUID `json:"image" example:"d2c2f03f-3fe5-4bfc-b963-5478a057149e" description:"UUID of the widget image"`
Audio *uuid.UUID `json:"audio" example:"a0f9e244-f61f-4bfe-a7a0-3b5e91fe7364" description:"UUID of the widget audio file"`
Duration *int `json:"duration" example:"120" description:"Duration of the widget in seconds"`
MinAmount *int `json:"min_amount" example:"10" description:"Minimum amount for the widget"`
MaxAmount *int `json:"max_amount" example:"100" description:"Maximum amount for the widget"`
}
type Donat struct {
ID DonatID `db:"id"`
StreamerID StreamerID `db:"streamer_id"`

View File

@ -139,3 +139,20 @@ FROM files AS f
JOIN widgets AS w ON w.image = f.id
WHERE f.streamer_id = (@streamer_id);
`
var UpdateWidget = `
UPDATE widgets
SET
duration = COALESCE(@duration, duration),
min_amount = COALESCE(@min_amount, min_amount),
max_amount = COALESCE(@max_amount, max_amount),
is_active = COALESCE(@is_active, is_active),
image = COALESCE(@image, image),
audio = COALESCE(@audio, audio),
name = COALESCE(@name, name)
WHERE id = @id;
`
var GetWidgetById = `
SELECT id FROM widgets WHERE id = (@widget_id) AND streamer_id = (@streamer_id);
`

View File

@ -4,7 +4,9 @@ import (
"context"
"donat-widget/internal/model"
"donat-widget/internal/model/sql"
"errors"
"github.com/georgysavva/scany/v2/pgxscan"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
"log/slog"
)
@ -97,6 +99,74 @@ func (widgetRepo *RepoWidget) GetWidgetsByStreamerID(
return widgets, nil
}
func (widgetRepo *RepoWidget) UpdateWidget(
ctx context.Context,
widgetID int,
duration *int,
minAmount *int,
maxAmount *int,
isActive *bool,
image *uuid.UUID,
audio *uuid.UUID,
name *string,
) error {
args := pgx.NamedArgs{
"id": widgetID,
}
if duration != nil {
args["duration"] = *duration
}
if minAmount != nil {
args["min_amount"] = *minAmount
}
if maxAmount != nil {
args["max_amount"] = *maxAmount
}
if image != nil {
args["image"] = *image
}
if audio != nil {
args["audio"] = *audio
}
if name != nil {
args["name"] = *name
}
if isActive != nil {
args["is_active"] = *isActive
}
err := widgetRepo.db.Update(ctx, sql.UpdateWidget, args)
if err != nil {
slog.Error(err.Error())
return err
}
return nil
}
func (widgetRepo *RepoWidget) WidgetByID(
ctx context.Context,
widgetID int,
accountID int,
) error {
args := pgx.NamedArgs{
"widget_id": widgetID,
"streamer_id": accountID,
}
row, err := widgetRepo.db.SelectOne(ctx, sql.GetWidgetById, args)
if err != nil {
slog.Error(err.Error())
return err
}
if row == nil {
return errors.New("Widget not exists!")
}
return nil
}
//
//func (widgetRepo *RepoWidget) GetWidgetByID(
// ctx context.Context,

View File

@ -97,6 +97,37 @@ func (widgetService *ServiceWidget) GetWidgetsByStreamer(
return result, nil
}
func (widgetService *ServiceWidget) UpdateWidget(
ctx context.Context,
updateWidget model.UpdateWidget,
widgetID int,
accountID int,
) error {
err := widgetService.widgetRepo.WidgetByID(ctx, widgetID, accountID)
if err != nil {
slog.Error(err.Error())
return err
}
err = widgetService.widgetRepo.UpdateWidget(
ctx,
widgetID,
updateWidget.Duration,
updateWidget.MinAmount,
updateWidget.MaxAmount,
updateWidget.IsActive,
updateWidget.Image,
updateWidget.Audio,
updateWidget.Name,
)
if err != nil {
slog.Error(err.Error())
return err
}
return nil
}
//
//func (widgetService *ServiceWidget) GetWidgetHTML(
// ctx context.Context,