From ba905cf334652e73c4999aabec44169f865d1309 Mon Sep 17 00:00:00 2001 From: ErfanTech Date: Thu, 6 Jun 2024 21:24:56 +0330 Subject: [PATCH] feat(niki): add benefactor edit address --- .../http_server/benefactor/address/route.go | 2 + .../http_server/benefactor/address/update.go | 54 +++++++++++++ docs/docs.go | 77 +++++++++++++++++++ docs/swagger.json | 77 +++++++++++++++++++ docs/swagger.yaml | 51 ++++++++++++ param/benefactor/address/update.go | 12 +++ pkg/err_msg/message.go | 5 +- pkg/query_builder/update.go | 22 ++++++ repository/mysql/address/create.go | 28 ++++--- repository/mysql/address/update.go | 51 ++++++++++++ service/benefactor/address/service.go | 1 + service/benefactor/address/update.go | 29 +++++++ validator/benefactor/address/update.go | 54 +++++++++++++ 13 files changed, 453 insertions(+), 10 deletions(-) create mode 100644 delivery/http_server/benefactor/address/update.go create mode 100644 param/benefactor/address/update.go create mode 100644 pkg/query_builder/update.go create mode 100644 repository/mysql/address/update.go create mode 100644 service/benefactor/address/update.go create mode 100644 validator/benefactor/address/update.go diff --git a/delivery/http_server/benefactor/address/route.go b/delivery/http_server/benefactor/address/route.go index fbe9c95..f0934e8 100644 --- a/delivery/http_server/benefactor/address/route.go +++ b/delivery/http_server/benefactor/address/route.go @@ -19,4 +19,6 @@ func (h Handler) SetRoutes(e *echo.Echo) { middleware.BenefactorAuthorization(entity.UserBenefactorRole)) r.DELETE("/:id", h.DeleteAddress, middleware.Auth(h.authSvc, h.authConfig), middleware.BenefactorAuthorization(entity.UserBenefactorRole)) + r.PATCH("/:id", h.UpdateAddress, middleware.Auth(h.authSvc, h.authConfig), + middleware.BenefactorAuthorization(entity.UserBenefactorRole)) } diff --git a/delivery/http_server/benefactor/address/update.go b/delivery/http_server/benefactor/address/update.go new file mode 100644 index 0000000..a83e917 --- /dev/null +++ b/delivery/http_server/benefactor/address/update.go @@ -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) +} diff --git a/docs/docs.go b/docs/docs.go index 6976d7b..97c94cf 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -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": { "get": { "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": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 13b97a3..0455951 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -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": { "get": { "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": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 8ea0dff..12c085c 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -51,6 +51,27 @@ definitions: $ref: '#/definitions/entity.Province' type: array 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: properties: sender_agent_id: @@ -620,6 +641,36 @@ paths: summary: Get a benefactor address tags: - 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: get: consumes: diff --git a/param/benefactor/address/update.go b/param/benefactor/address/update.go new file mode 100644 index 0000000..1b16cd7 --- /dev/null +++ b/param/benefactor/address/update.go @@ -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:"-"` +} diff --git a/pkg/err_msg/message.go b/pkg/err_msg/message.go index 818d54d..ec38276 100644 --- a/pkg/err_msg/message.go +++ b/pkg/err_msg/message.go @@ -21,7 +21,10 @@ const ( 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" 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" ErrorMsgReferTimeIsNotActive = "refer time is not active" ErrorMsgKindBoxReqDoesntBelongToBenefactor = "kind box req doesnt belong to benefactor" diff --git a/pkg/query_builder/update.go b/pkg/query_builder/update.go new file mode 100644 index 0000000..bfaef1d --- /dev/null +++ b/pkg/query_builder/update.go @@ -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 +} diff --git a/repository/mysql/address/create.go b/repository/mysql/address/create.go index e84561c..535fc94 100644 --- a/repository/mysql/address/create.go +++ b/repository/mysql/address/create.go @@ -10,14 +10,11 @@ import ( ) 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 - var provinceID uint - pErr := d.conn.Conn().QueryRowContext(ctx, `SELECT province_id FROM cities WHERE id = ?`, address.CityID).Scan(&provinceID) - if pErr != nil && pErr != sql.ErrNoRows { - return entity.Address{}, richerror.New(op).WithErr(pErr). - WithMessage("error querying for existing main address").WithKind(richerror.KindUnexpected) + provinceID, err := d.getProvinceIDByCityID(ctx, address.CityID) + if err != nil { + return entity.Address{}, err } 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) if err != nil { 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 id, err := res.LastInsertId() if err != nil { 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) 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 +} diff --git a/repository/mysql/address/update.go b/repository/mysql/address/update.go new file mode 100644 index 0000000..8d406b8 --- /dev/null +++ b/repository/mysql/address/update.go @@ -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 +} diff --git a/service/benefactor/address/service.go b/service/benefactor/address/service.go index 3117899..6c6fe01 100644 --- a/service/benefactor/address/service.go +++ b/service/benefactor/address/service.go @@ -10,6 +10,7 @@ type Repository interface { GetAddressByID(ctx context.Context, id uint) (*entity.Address, error) GetAllProvinces(ctx context.Context) ([]entity.Province, 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) GetAddresses(ctx context.Context, benefactorID uint) ([]entity.Address, error) DeleteBenefactorAddress(ctx context.Context, addressID uint, benefactorID uint) error diff --git a/service/benefactor/address/update.go b/service/benefactor/address/update.go new file mode 100644 index 0000000..1790df5 --- /dev/null +++ b/service/benefactor/address/update.go @@ -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 +} diff --git a/validator/benefactor/address/update.go b/validator/benefactor/address/update.go new file mode 100644 index 0000000..1f45152 --- /dev/null +++ b/validator/benefactor/address/update.go @@ -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 +}