feat(niki): add benefactor edit address

This commit is contained in:
Erfan Mohammadi 2024-06-06 21:24:56 +03:30
parent ec81297f98
commit ba905cf334
13 changed files with 453 additions and 10 deletions

View File

@ -19,4 +19,6 @@ func (h Handler) SetRoutes(e *echo.Echo) {
middleware.BenefactorAuthorization(entity.UserBenefactorRole)) middleware.BenefactorAuthorization(entity.UserBenefactorRole))
r.DELETE("/:id", h.DeleteAddress, middleware.Auth(h.authSvc, h.authConfig), r.DELETE("/:id", h.DeleteAddress, middleware.Auth(h.authSvc, h.authConfig),
middleware.BenefactorAuthorization(entity.UserBenefactorRole)) middleware.BenefactorAuthorization(entity.UserBenefactorRole))
r.PATCH("/:id", h.UpdateAddress, middleware.Auth(h.authSvc, h.authConfig),
middleware.BenefactorAuthorization(entity.UserBenefactorRole))
} }

View File

@ -0,0 +1,54 @@
package benefactoraddresshandler
import (
"fmt"
"net/http"
param "git.gocasts.ir/ebhomengo/niki/param/benefactor/address"
"git.gocasts.ir/ebhomengo/niki/pkg/claim"
httpmsg "git.gocasts.ir/ebhomengo/niki/pkg/http_msg"
"github.com/labstack/echo/v4"
)
// UpdateAddress godoc
// @Summary Edit benefactor address
// @Tags Address
// @Accept json
// @Produce json
// @Param id path int true "Address ID"
// @Param Request body param.UpdateAddressRequest true "Edit address details"
// @Success 204
// @Failure 400 {string} "Bad request"
// @Security AuthBearerBenefactor
// @Router /address/{id} [patch]
func (h Handler) UpdateAddress(c echo.Context) error {
var req param.UpdateAddressRequest
if bErr := c.Bind(&req); bErr != nil {
return echo.NewHTTPError(http.StatusBadRequest)
}
fmt.Println(req)
if bErr := echo.PathParamsBinder(c).Uint("id", &req.ID).BindError(); bErr != nil {
return echo.NewHTTPError(http.StatusBadRequest)
}
claims := claim.GetClaimsFromEchoContext(c)
req.BenefactorID = claims.UserID
if fieldErrors, err := h.addressVld.ValidateUpdateAddress(req); err != nil {
msg, code := httpmsg.Error(err)
return c.JSON(code, echo.Map{
"message": msg,
"errors": fieldErrors,
})
}
sErr := h.addressSvc.Update(c.Request().Context(), req)
if sErr != nil {
msg, code := httpmsg.Error(sErr)
return echo.NewHTTPError(code, msg)
}
return c.JSON(http.StatusNoContent, nil)
}

View File

@ -221,6 +221,54 @@ const docTemplate = `{
} }
} }
}, },
"/address/{id}": {
"patch": {
"security": [
{
"AuthBearerBenefactor": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Address"
],
"summary": "Edit benefactor address",
"parameters": [
{
"type": "integer",
"description": "Address ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Edit address details",
"name": "Request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/addressparam.UpdateAddressRequest"
}
}
],
"responses": {
"204": {
"description": "No Content"
},
"400": {
"description": "Bad request",
"schema": {
"type": "string"
}
}
}
}
},
"/admin/kindboxreqs": { "/admin/kindboxreqs": {
"get": { "get": {
"security": [ "security": [
@ -942,6 +990,35 @@ const docTemplate = `{
} }
} }
}, },
"addressparam.UpdateAddressRequest": {
"type": "object",
"properties": {
"address": {
"type": "string",
"example": "Zanjan Province, Zanjan, Etemadieh, 6th St, Iran"
},
"city_id": {
"type": "integer",
"example": 163
},
"lat": {
"type": "number",
"example": 123.456
},
"lon": {
"type": "number",
"example": 123.456
},
"name": {
"type": "string",
"example": "Home"
},
"postal_code": {
"type": "string",
"example": "1234567890"
}
}
},
"adminkindboxreqparam.AssignSenderRequest": { "adminkindboxreqparam.AssignSenderRequest": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@ -210,6 +210,54 @@
} }
} }
}, },
"/address/{id}": {
"patch": {
"security": [
{
"AuthBearerBenefactor": []
}
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Address"
],
"summary": "Edit benefactor address",
"parameters": [
{
"type": "integer",
"description": "Address ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Edit address details",
"name": "Request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/addressparam.UpdateAddressRequest"
}
}
],
"responses": {
"204": {
"description": "No Content"
},
"400": {
"description": "Bad request",
"schema": {
"type": "string"
}
}
}
}
},
"/admin/kindboxreqs": { "/admin/kindboxreqs": {
"get": { "get": {
"security": [ "security": [
@ -931,6 +979,35 @@
} }
} }
}, },
"addressparam.UpdateAddressRequest": {
"type": "object",
"properties": {
"address": {
"type": "string",
"example": "Zanjan Province, Zanjan, Etemadieh, 6th St, Iran"
},
"city_id": {
"type": "integer",
"example": 163
},
"lat": {
"type": "number",
"example": 123.456
},
"lon": {
"type": "number",
"example": 123.456
},
"name": {
"type": "string",
"example": "Home"
},
"postal_code": {
"type": "string",
"example": "1234567890"
}
}
},
"adminkindboxreqparam.AssignSenderRequest": { "adminkindboxreqparam.AssignSenderRequest": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@ -51,6 +51,27 @@ definitions:
$ref: '#/definitions/entity.Province' $ref: '#/definitions/entity.Province'
type: array type: array
type: object type: object
addressparam.UpdateAddressRequest:
properties:
address:
example: Zanjan Province, Zanjan, Etemadieh, 6th St, Iran
type: string
city_id:
example: 163
type: integer
lat:
example: 123.456
type: number
lon:
example: 123.456
type: number
name:
example: Home
type: string
postal_code:
example: "1234567890"
type: string
type: object
adminkindboxreqparam.AssignSenderRequest: adminkindboxreqparam.AssignSenderRequest:
properties: properties:
sender_agent_id: sender_agent_id:
@ -620,6 +641,36 @@ paths:
summary: Get a benefactor address summary: Get a benefactor address
tags: tags:
- Address - Address
/address/{id}:
patch:
consumes:
- application/json
parameters:
- description: Address ID
in: path
name: id
required: true
type: integer
- description: Edit address details
in: body
name: Request
required: true
schema:
$ref: '#/definitions/addressparam.UpdateAddressRequest'
produces:
- application/json
responses:
"204":
description: No Content
"400":
description: Bad request
schema:
type: string
security:
- AuthBearerBenefactor: []
summary: Edit benefactor address
tags:
- Address
/address/cities: /address/cities:
get: get:
consumes: consumes:

View File

@ -0,0 +1,12 @@
package addressparam
type UpdateAddressRequest struct {
ID uint `json:"-"`
PostalCode string `json:"postal_code" example:"1234567890"`
Address string `json:"address" example:"Zanjan Province, Zanjan, Etemadieh, 6th St, Iran"`
Name string `json:"name" example:"Home"`
Lat float64 `json:"lat" example:"123.456"`
Lon float64 `json:"lon" example:"123.456"`
CityID uint `json:"city_id" example:"163"`
BenefactorID uint `json:"-"`
}

View File

@ -21,7 +21,10 @@ const (
ErrorMsgAssignSenderAgentKindBoxReqStatus = "only accepted kind_box_reqs will have the ability to be assign sender agent" ErrorMsgAssignSenderAgentKindBoxReqStatus = "only accepted kind_box_reqs will have the ability to be assign sender agent"
ErrorMsgDeliverKindBoxReqStatus = "only assigned requests will have the ability to be delivered" ErrorMsgDeliverKindBoxReqStatus = "only assigned requests will have the ability to be delivered"
ErrorMsgAdminIsNotAgent = "admin is not agent" ErrorMsgAdminIsNotAgent = "admin is not agent"
ErrorMsgCountAcceptedOverflow = "count accepted is greather than count requested" ErrorMsgCountAcceptedOverflow = "count accepted is greater than count requested"
ErrorMsgCantInsertRecord = "can't insert record"
ErrorMsgCantRetrieveLastInsertID = "can't retrieve last insert id"
ErrorMsgCantUpdateRecord = "can't update record"
ErrorMsgReferTimeNotFound = "refer time not found" ErrorMsgReferTimeNotFound = "refer time not found"
ErrorMsgReferTimeIsNotActive = "refer time is not active" ErrorMsgReferTimeIsNotActive = "refer time is not active"
ErrorMsgKindBoxReqDoesntBelongToBenefactor = "kind box req doesnt belong to benefactor" ErrorMsgKindBoxReqDoesntBelongToBenefactor = "kind box req doesnt belong to benefactor"

View File

@ -0,0 +1,22 @@
package querybuilder
import (
"fmt"
"strings"
)
// BuildUpdateQuery constructs a dynamic SQL update query based on the provided fields
func BuildUpdateQuery(table string, fields map[string]interface{}, idField string, idValue interface{}) (string, []interface{}) {
var setClauses []string
var args []interface{}
for field, value := range fields {
setClauses = append(setClauses, fmt.Sprintf("%s = ?", field))
args = append(args, value)
}
query := fmt.Sprintf("UPDATE %s SET %s WHERE %s = ?", table, strings.Join(setClauses, ", "), idField)
args = append(args, idValue)
return query, args
}

View File

@ -10,14 +10,11 @@ import (
) )
func (d *DB) CreateBenefactorAddress(ctx context.Context, address entity.Address) (entity.Address, error) { func (d *DB) CreateBenefactorAddress(ctx context.Context, address entity.Address) (entity.Address, error) {
const op = "mysqlbenefactor.createBenefactorAddress" const op = "mysqladdress.createBenefactorAddress"
// Get Province ID by City ID provinceID, err := d.getProvinceIDByCityID(ctx, address.CityID)
var provinceID uint if err != nil {
pErr := d.conn.Conn().QueryRowContext(ctx, `SELECT province_id FROM cities WHERE id = ?`, address.CityID).Scan(&provinceID) return entity.Address{}, err
if pErr != nil && pErr != sql.ErrNoRows {
return entity.Address{}, richerror.New(op).WithErr(pErr).
WithMessage("error querying for existing main address").WithKind(richerror.KindUnexpected)
} }
address.ProvinceID = provinceID address.ProvinceID = provinceID
@ -26,16 +23,29 @@ func (d *DB) CreateBenefactorAddress(ctx context.Context, address entity.Address
address.PostalCode, address.Address, address.Lat, address.Lon, address.Name, address.CityID, provinceID, address.BenefactorID) address.PostalCode, address.Address, address.Lat, address.Lon, address.Name, address.CityID, provinceID, address.BenefactorID)
if err != nil { if err != nil {
return entity.Address{}, richerror.New(op).WithErr(err). return entity.Address{}, richerror.New(op).WithErr(err).
WithMessage(errmsg.ErrorMsgNotFound).WithKind(richerror.KindUnexpected) WithMessage(errmsg.ErrorMsgCantInsertRecord).WithKind(richerror.KindUnexpected)
} }
// Get the ID of the newly inserted record // Get the ID of the newly inserted record
id, err := res.LastInsertId() id, err := res.LastInsertId()
if err != nil { if err != nil {
return entity.Address{}, richerror.New(op).WithErr(err). return entity.Address{}, richerror.New(op).WithErr(err).
WithMessage("error retrieving last insert id").WithKind(richerror.KindUnexpected) WithMessage(errmsg.ErrorMsgCantRetrieveLastInsertID).WithKind(richerror.KindUnexpected)
} }
address.ID = uint(id) address.ID = uint(id)
return address, nil return address, nil
} }
func (d *DB) getProvinceIDByCityID(ctx context.Context, cityID uint) (uint, error) {
const op = "mysqladdress.getProvinceIDByCityID"
var provinceID uint
pErr := d.conn.Conn().QueryRowContext(ctx, `SELECT province_id FROM cities WHERE id = ?`, cityID).Scan(&provinceID)
if pErr != nil && pErr != sql.ErrNoRows {
return 0, richerror.New(op).WithErr(pErr).
WithMessage(errmsg.ErrorMsgCantScanQueryResult).WithKind(richerror.KindUnexpected)
}
return provinceID, nil
}

View File

@ -0,0 +1,51 @@
package mysqladdress
import (
"context"
"git.gocasts.ir/ebhomengo/niki/entity"
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
querybuilder "git.gocasts.ir/ebhomengo/niki/pkg/query_builder"
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
)
func (d *DB) UpdateAddress(ctx context.Context, address entity.Address) error {
const op = "mysqladdress.UpdateAddress"
fields := make(map[string]interface{})
if address.PostalCode != "" {
fields["postal_code"] = address.PostalCode
}
if address.Address != "" {
fields["address"] = address.Address
}
if address.Name != "" {
fields["name"] = address.Name
}
if address.Lat != 0 {
fields["lat"] = address.Lat
}
if address.Lon != 0 {
fields["lon"] = address.Lon
}
if address.CityID != 0 {
provinceID, err := d.getProvinceIDByCityID(ctx, address.CityID)
if err != nil {
return err
}
fields["province_id"] = provinceID
fields["city_id"] = address.CityID
}
query, values := querybuilder.BuildUpdateQuery("addresses", fields, "id", address.ID)
query += " and benefactor_id = ?"
values = append(values, address.BenefactorID)
_, err := d.conn.Conn().ExecContext(ctx, query, values...)
if err != nil {
return richerror.New(op).WithErr(err).WithMessage(errmsg.ErrorMsgCantUpdateRecord).
WithKind(richerror.KindUnexpected)
}
return nil
}

View File

@ -10,6 +10,7 @@ type Repository interface {
GetAddressByID(ctx context.Context, id uint) (*entity.Address, error) GetAddressByID(ctx context.Context, id uint) (*entity.Address, error)
GetAllProvinces(ctx context.Context) ([]entity.Province, error) GetAllProvinces(ctx context.Context) ([]entity.Province, error)
GetAllCities(ctx context.Context) ([]entity.City, error) GetAllCities(ctx context.Context) ([]entity.City, error)
UpdateAddress(ctx context.Context, address entity.Address) error
GetAddress(ctx context.Context, addressID uint, benefactorID uint) (entity.Address, error) GetAddress(ctx context.Context, addressID uint, benefactorID uint) (entity.Address, error)
GetAddresses(ctx context.Context, benefactorID uint) ([]entity.Address, error) GetAddresses(ctx context.Context, benefactorID uint) ([]entity.Address, error)
DeleteBenefactorAddress(ctx context.Context, addressID uint, benefactorID uint) error DeleteBenefactorAddress(ctx context.Context, addressID uint, benefactorID uint) error

View File

@ -0,0 +1,29 @@
package benefactoraddressservice
import (
"context"
"git.gocasts.ir/ebhomengo/niki/entity"
param "git.gocasts.ir/ebhomengo/niki/param/benefactor/address"
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
)
func (s Service) Update(ctx context.Context, req param.UpdateAddressRequest) error {
const op = "benefactoraddressservice.Update"
err := s.repo.UpdateAddress(ctx, entity.Address{
ID: req.ID,
PostalCode: req.PostalCode,
Address: req.Address,
Name: req.Name,
Lat: req.Lat,
Lon: req.Lon,
CityID: req.CityID,
BenefactorID: req.BenefactorID,
})
if err != nil {
return richerror.New(op).WithErr(err)
}
return nil
}

View File

@ -0,0 +1,54 @@
package benefactoraddressvalidator
import (
"errors"
param "git.gocasts.ir/ebhomengo/niki/param/benefactor/address"
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
validation "github.com/go-ozzo/ozzo-validation/v4"
)
func (v Validator) ValidateUpdateAddress(req param.UpdateAddressRequest) (map[string]string, error) {
const op = "benefactoraddressvalidator.ValidateUpdateAddress"
if req.PostalCode == "" && req.Address == "" && req.Name == "" &&
req.Lat == 0 && req.Lon == 0 && req.CityID == 0 {
return map[string]string{"update": "at least one field should be provided"}, richerror.New(op).
WithMessage(errmsg.ErrorMsgInvalidInput).
WithKind(richerror.KindInvalid).
WithMeta(map[string]interface{}{"req": req}).
WithErr(errors.New("at least one field should be provided"))
}
if err := validation.ValidateStruct(&req,
validation.Field(&req.BenefactorID, validation.Required,
validation.By(v.doesBenefactorExist)),
validation.Field(&req.ID, validation.Required,
validation.By(v.doesAddressExist(req.BenefactorID))),
validation.Field(&req.CityID,
validation.When(req.CityID != 0, validation.By(v.doesCityExist))),
); err != nil {
fieldErrors := make(map[string]string)
var errV validation.Errors
if errors.As(err, &errV) {
for key, value := range errV {
if value != nil {
fieldErrors[key] = value.Error()
}
}
}
return fieldErrors, richerror.New(op).
WithMessage(errmsg.ErrorMsgInvalidInput).
WithKind(richerror.KindInvalid).
WithMeta(map[string]interface{}{"req": req}).
WithErr(err)
}
return map[string]string{}, nil
}