diff --git a/delivery/http_server/benefactor/address/get.go b/delivery/http_server/benefactor/address/get.go new file mode 100644 index 0000000..15f1d45 --- /dev/null +++ b/delivery/http_server/benefactor/address/get.go @@ -0,0 +1,46 @@ +package benefactoraddresshandler + +import ( + "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" +) + +// GetAddress godoc +// @Summary Get a benefactor address +// @Tags Address +// @Accept json +// @Produce json +// @Param id path int true "Address ID" +// @Success 200 {object} param.GetAddressResponse +// @Failure 400 {string} "Bad request" +// @Security AuthBearerBenefactor +// @Router /address/{id} [get] +func (h Handler) GetAddress(c echo.Context) error { + var req param.GetAddressRequest + if bErr := echo.PathParamsBinder(c).Uint("id", &req.AddressID).BindError(); bErr != nil { + return echo.NewHTTPError(http.StatusBadRequest) + } + claims := claim.GetClaimsFromEchoContext(c) + req.BenefactorID = claims.UserID + + if fieldErrors, err := h.addressVld.ValidateGetAddress(req); err != nil { + msg, code := httpmsg.Error(err) + + return c.JSON(code, echo.Map{ + "message": msg, + "errors": fieldErrors, + }) + } + resp, sErr := h.addressSvc.Get(c.Request().Context(), req) + if sErr != nil { + msg, code := httpmsg.Error(sErr) + + return echo.NewHTTPError(code, msg) + } + + return c.JSON(http.StatusOK, resp) +} diff --git a/delivery/http_server/benefactor/address/get_all.go b/delivery/http_server/benefactor/address/get_all.go new file mode 100644 index 0000000..8ac4375 --- /dev/null +++ b/delivery/http_server/benefactor/address/get_all.go @@ -0,0 +1,35 @@ +package benefactoraddresshandler + +import ( + "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" +) + +// GetAddresses godoc +// @Summary Get all benefactor addresses +// @Tags Address +// @Accept json +// @Produce json +// @Success 200 {object} param.GetAllAddressesResponse +// @Failure 400 {string} "Bad request" +// @Security AuthBearerBenefactor +// @Router /address/ [get] +func (h Handler) GetAddresses(c echo.Context) error { + var req param.GetAllAddressesRequest + + claims := claim.GetClaimsFromEchoContext(c) + req.BenefactorID = claims.UserID + + resp, sErr := h.addressSvc.GetAll(c.Request().Context(), req) + if sErr != nil { + msg, code := httpmsg.Error(sErr) + + return echo.NewHTTPError(code, msg) + } + + return c.JSON(http.StatusOK, resp) +} diff --git a/delivery/http_server/benefactor/address/route.go b/delivery/http_server/benefactor/address/route.go index d8c6a06..ad891f7 100644 --- a/delivery/http_server/benefactor/address/route.go +++ b/delivery/http_server/benefactor/address/route.go @@ -13,4 +13,8 @@ func (h Handler) SetRoutes(e *echo.Echo) { r.GET("/cities", h.GetAllCities) r.POST("/", h.AddAddress, middleware.Auth(h.authSvc, h.authConfig), middleware.BenefactorAuthorization(entity.UserBenefactorRole)) + r.GET("/:id", h.GetAddress, middleware.Auth(h.authSvc, h.authConfig), + middleware.BenefactorAuthorization(entity.UserBenefactorRole)) + r.GET("/", h.GetAddresses, middleware.Auth(h.authSvc, h.authConfig), + middleware.BenefactorAuthorization(entity.UserBenefactorRole)) } diff --git a/docs/docs.go b/docs/docs.go index e071499..4b91f73 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -16,6 +16,37 @@ const docTemplate = `{ "basePath": "{{.BasePath}}", "paths": { "/address/": { + "get": { + "security": [ + { + "AuthBearerBenefactor": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Address" + ], + "summary": "Get all benefactor addresses", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/addressparam.GetAllAddressesResponse" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + } + } + }, "post": { "security": [ { @@ -116,6 +147,48 @@ const docTemplate = `{ } } }, + "/address/{id}": { + "get": { + "security": [ + { + "AuthBearerBenefactor": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Address" + ], + "summary": "Get a benefactor address", + "parameters": [ + { + "type": "integer", + "description": "Address ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/addressparam.GetAddressResponse" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + } + } + } + }, "/admin/kindboxreqs": { "get": { "security": [ @@ -763,6 +836,25 @@ const docTemplate = `{ } } }, + "addressparam.GetAddressResponse": { + "type": "object", + "properties": { + "address": { + "$ref": "#/definitions/entity.Address" + } + } + }, + "addressparam.GetAllAddressesResponse": { + "type": "object", + "properties": { + "all_addresses": { + "type": "array", + "items": { + "$ref": "#/definitions/entity.Address" + } + } + } + }, "addressparam.GetAllCitiesResponse": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 4191b49..388d915 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -5,6 +5,37 @@ }, "paths": { "/address/": { + "get": { + "security": [ + { + "AuthBearerBenefactor": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Address" + ], + "summary": "Get all benefactor addresses", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/addressparam.GetAllAddressesResponse" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + } + } + }, "post": { "security": [ { @@ -105,6 +136,48 @@ } } }, + "/address/{id}": { + "get": { + "security": [ + { + "AuthBearerBenefactor": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Address" + ], + "summary": "Get a benefactor address", + "parameters": [ + { + "type": "integer", + "description": "Address ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/addressparam.GetAddressResponse" + } + }, + "400": { + "description": "Bad request", + "schema": { + "type": "string" + } + } + } + } + }, "/admin/kindboxreqs": { "get": { "security": [ @@ -752,6 +825,25 @@ } } }, + "addressparam.GetAddressResponse": { + "type": "object", + "properties": { + "address": { + "$ref": "#/definitions/entity.Address" + } + } + }, + "addressparam.GetAllAddressesResponse": { + "type": "object", + "properties": { + "all_addresses": { + "type": "array", + "items": { + "$ref": "#/definitions/entity.Address" + } + } + } + }, "addressparam.GetAllCitiesResponse": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 56bda60..c8d42c5 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -25,6 +25,18 @@ definitions: address: $ref: '#/definitions/entity.Address' type: object + addressparam.GetAddressResponse: + properties: + address: + $ref: '#/definitions/entity.Address' + type: object + addressparam.GetAllAddressesResponse: + properties: + all_addresses: + items: + $ref: '#/definitions/entity.Address' + type: array + type: object addressparam.GetAllCitiesResponse: properties: cities: @@ -488,6 +500,25 @@ info: contact: {} paths: /address/: + get: + consumes: + - application/json + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/addressparam.GetAllAddressesResponse' + "400": + description: Bad request + schema: + type: string + security: + - AuthBearerBenefactor: [] + summary: Get all benefactor addresses + tags: + - Address post: consumes: - application/json @@ -516,6 +547,32 @@ paths: summary: Add a new address for a benefactor tags: - Address + /address/{id}: + get: + consumes: + - application/json + parameters: + - description: Address ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/addressparam.GetAddressResponse' + "400": + description: Bad request + schema: + type: string + security: + - AuthBearerBenefactor: [] + summary: Get a benefactor address + tags: + - Address /address/cities: get: consumes: diff --git a/param/benefactor/address/get.go b/param/benefactor/address/get.go index de98560..dbc087d 100644 --- a/param/benefactor/address/get.go +++ b/param/benefactor/address/get.go @@ -8,3 +8,11 @@ type GetAddressByIDRequest struct { type GetAddressByIDResponse struct { Address *entity.Address } + +type GetAddressRequest struct { + BenefactorID uint + AddressID uint +} +type GetAddressResponse struct { + Address entity.Address `json:"address"` +} diff --git a/param/benefactor/address/get_all.go b/param/benefactor/address/get_all.go new file mode 100644 index 0000000..580e258 --- /dev/null +++ b/param/benefactor/address/get_all.go @@ -0,0 +1,11 @@ +package addressparam + +import "git.gocasts.ir/ebhomengo/niki/entity" + +type GetAllAddressesRequest struct { + BenefactorID uint +} + +type GetAllAddressesResponse struct { + AllAddresses []entity.Address `json:"all_addresses"` +} diff --git a/repository/mysql/address/get.go b/repository/mysql/address/get.go index e4cccfb..9186c25 100644 --- a/repository/mysql/address/get.go +++ b/repository/mysql/address/get.go @@ -35,6 +35,29 @@ func (d *DB) GetAddressByID(ctx context.Context, id uint) (*entity.Address, erro return &address, nil } +func (d *DB) GetAddress(ctx context.Context, addressID uint, benefactorID uint) (entity.Address, error) { + const op = "mysqladdress.GetAddress" + + row := d.conn.Conn().QueryRowContext(ctx, `select * from addresses where id = ? and benefactor_id = ?`, addressID, benefactorID) + + address, err := scanAddress(row) + if err != nil { + sErr := sql.ErrNoRows + //TODO-errorsas: second argument to errors.As should not be *error + //nolint + if errors.As(err, &sErr) { + return entity.Address{}, richerror.New(op).WithMessage(errmsg.ErrorMsgNotFound). + WithKind(richerror.KindNotFound) + } + + // TODO - log unexpected error for better observability + return entity.Address{}, richerror.New(op).WithErr(err). + WithMessage(errmsg.ErrorMsgCantScanQueryResult).WithKind(richerror.KindUnexpected) + } + + return address, nil +} + func scanAddress(scanner mysql.Scanner) (entity.Address, error) { var createdAt, updatedAt time.Time var address entity.Address diff --git a/repository/mysql/address/get_all.go b/repository/mysql/address/get_all.go new file mode 100644 index 0000000..3f0eb9d --- /dev/null +++ b/repository/mysql/address/get_all.go @@ -0,0 +1,38 @@ +package mysqladdress + +import ( + "context" + + "git.gocasts.ir/ebhomengo/niki/entity" + errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg" + richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error" +) + +func (d *DB) GetAddresses(ctx context.Context, benefactorID uint) ([]entity.Address, error) { + const op = "mysqladdress.GetAddresses" + + rows, err := d.conn.Conn().QueryContext(ctx, `select * from addresses where benefactor_id = ?`, benefactorID) + if err != nil { + return nil, richerror.New(op).WithErr(err). + WithMessage(errmsg.ErrorMsgSomethingWentWrong).WithKind(richerror.KindUnexpected) + } + defer rows.Close() + + addresses := make([]entity.Address, 0) + + for rows.Next() { + address, sErr := scanAddress(rows) + if sErr != nil { + return nil, richerror.New(op).WithErr(sErr). + WithMessage(errmsg.ErrorMsgCantScanQueryResult).WithKind(richerror.KindUnexpected) + } + addresses = append(addresses, address) + } + + if rErr := rows.Err(); rErr != nil { + return nil, richerror.New(op).WithErr(rErr). + WithMessage(errmsg.ErrorMsgSomethingWentWrong).WithKind(richerror.KindUnexpected) + } + + return addresses, nil +} diff --git a/service/benefactor/address/get.go b/service/benefactor/address/get.go new file mode 100644 index 0000000..48a78c3 --- /dev/null +++ b/service/benefactor/address/get.go @@ -0,0 +1,18 @@ +package benefactoraddressservice + +import ( + "context" + param "git.gocasts.ir/ebhomengo/niki/param/benefactor/address" + richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error" +) + +func (s Service) Get(ctx context.Context, req param.GetAddressRequest) (param.GetAddressResponse, error) { + const op = "benefactoraddressservice.Get" + + address, err := s.repo.GetAddress(ctx, req.AddressID, req.BenefactorID) + if err != nil { + return param.GetAddressResponse{}, richerror.New(op).WithErr(err) + } + + return param.GetAddressResponse{Address: address}, nil +} diff --git a/service/benefactor/address/get_all.go b/service/benefactor/address/get_all.go new file mode 100644 index 0000000..88edbe1 --- /dev/null +++ b/service/benefactor/address/get_all.go @@ -0,0 +1,19 @@ +package benefactoraddressservice + +import ( + "context" + + param "git.gocasts.ir/ebhomengo/niki/param/benefactor/address" + richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error" +) + +func (s Service) GetAll(ctx context.Context, request param.GetAllAddressesRequest) (param.GetAllAddressesResponse, error) { + const op = "benefactoraddressservice.GetAll" + + addresses, err := s.repo.GetAddresses(ctx, request.BenefactorID) + if err != nil { + return param.GetAllAddressesResponse{}, richerror.New(op).WithErr(err).WithKind(richerror.KindUnexpected) + } + + return param.GetAllAddressesResponse{AllAddresses: addresses}, nil +} diff --git a/service/benefactor/address/service.go b/service/benefactor/address/service.go index 646e45e..1061b6f 100644 --- a/service/benefactor/address/service.go +++ b/service/benefactor/address/service.go @@ -11,6 +11,8 @@ 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) + GetAddress(ctx context.Context, addressID uint, benefactorID uint) (entity.Address, error) + GetAddresses(ctx context.Context, benefactorID uint) ([]entity.Address, error) } type Service struct { diff --git a/validator/benefactor/address/get.go b/validator/benefactor/address/get.go new file mode 100644 index 0000000..48c5165 --- /dev/null +++ b/validator/benefactor/address/get.go @@ -0,0 +1,42 @@ +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) ValidateGetAddress(req param.GetAddressRequest) (map[string]string, error) { + const op = "benefactorvalidator.ValidateGetRequest" + if err := validation.ValidateStruct(&req, + + validation.Field(&req.BenefactorID, validation.Required, + validation.By(v.doesBenefactorExist)), + + validation.Field(&req.AddressID, validation.Required, + validation.Min(uint(1))), + ); 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 +}