Skip to content

Commit

Permalink
Merge pull request #194 from lab-64/188-consistent-item-contribution
Browse files Browse the repository at this point in the history
  • Loading branch information
GR0ZA committed May 31, 2024
2 parents d4d3778 + 079b7c0 commit 82d5bb0
Show file tree
Hide file tree
Showing 8 changed files with 272 additions and 0 deletions.
62 changes: 62 additions & 0 deletions docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,46 @@ const docTemplate = `{
}
}
},
"/api/bill/{id}/contribution": {
"put": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Bill"
],
"summary": "Update Item Contribution",
"parameters": [
{
"type": "string",
"description": "Bill ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Request Body",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.ContributionInput"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.GeneralResponse"
}
}
}
}
},
"/api/group": {
"get": {
"consumes": [
Expand Down Expand Up @@ -908,6 +948,17 @@ const docTemplate = `{
}
}
},
"dto.ContributionInput": {
"type": "object",
"properties": {
"contribution": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.HasContributed"
}
}
}
},
"dto.GeneralResponse": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -988,6 +1039,17 @@ const docTemplate = `{
}
}
},
"dto.HasContributed": {
"type": "object",
"properties": {
"contributed": {
"type": "boolean"
},
"itemID": {
"type": "string"
}
}
},
"dto.ItemInput": {
"type": "object",
"properties": {
Expand Down
62 changes: 62 additions & 0 deletions docs/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,46 @@
}
}
},
"/api/bill/{id}/contribution": {
"put": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Bill"
],
"summary": "Update Item Contribution",
"parameters": [
{
"type": "string",
"description": "Bill ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Request Body",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.ContributionInput"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.GeneralResponse"
}
}
}
}
},
"/api/group": {
"get": {
"consumes": [
Expand Down Expand Up @@ -901,6 +941,17 @@
}
}
},
"dto.ContributionInput": {
"type": "object",
"properties": {
"contribution": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.HasContributed"
}
}
}
},
"dto.GeneralResponse": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -981,6 +1032,17 @@
}
}
},
"dto.HasContributed": {
"type": "object",
"properties": {
"contributed": {
"type": "boolean"
},
"itemID": {
"type": "string"
}
}
},
"dto.ItemInput": {
"type": "object",
"properties": {
Expand Down
40 changes: 40 additions & 0 deletions docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ definitions:
name:
type: string
type: object
dto.ContributionInput:
properties:
contribution:
items:
$ref: '#/definitions/dto.HasContributed'
type: array
type: object
dto.GeneralResponse:
properties:
data: {}
Expand Down Expand Up @@ -103,6 +110,13 @@ definitions:
$ref: '#/definitions/dto.TransactionOutput'
type: array
type: object
dto.HasContributed:
properties:
contributed:
type: boolean
itemID:
type: string
type: object
dto.ItemInput:
properties:
billId:
Expand Down Expand Up @@ -298,6 +312,32 @@ paths:
summary: Update Bill
tags:
- Bill
/api/bill/{id}/contribution:
put:
consumes:
- application/json
parameters:
- description: Bill ID
in: path
name: id
required: true
type: string
- description: Request Body
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.ContributionInput'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/dto.GeneralResponse'
summary: Update Item Contribution
tags:
- Bill
/api/group:
get:
consumes:
Expand Down
4 changes: 4 additions & 0 deletions domain/service/bill_service_inf.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ type IBillService interface {
// If no filter is provided, all bills from the groups in which the user is a member are returned.
// *Authorization required: requester == userID
GetAllByUserID(requesterID uuid.UUID, userID uuid.UUID, isUnseen *bool, isOwner *bool) ([]dto.BillDetailedOutput, error)

// HandleContribution handles the item contribution of the requester to the bill with the given id.
// *Authorization required: requester in bill.Group.Members
HandleContribution(requesterID uuid.UUID, billID uuid.UUID, contribution dto.ContributionInput) error
}
56 changes: 56 additions & 0 deletions domain/service/impl/bill_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,42 @@ func (b *BillService) GetAllByUserID(requesterID uuid.UUID, userID uuid.UUID, is
return orderedBills, err
}

func (b *BillService) HandleContribution(requesterID uuid.UUID, billID uuid.UUID, contribution dto.ContributionInput) error {
// Get bill
bill, err := b.billStorage.GetByID(billID)
if err != nil {
return err
}
// Get group
group, err := b.groupStorage.GetGroupByID(bill.GroupID)
if err != nil {
return err
}
// Authorization
if !group.IsMember(requesterID) {
return domain.ErrNotAuthorized
}
for _, contributionEntry := range contribution.Contribution {
// get item
i, item := getItemFromID(contributionEntry.ItemID, bill.Items)
// skip items which are not in the bill
if i == -1 {
break
}
if contributionEntry.Contributed {
// add item to contributor list
bill.Items[i].Contributors = append(item.Contributors, model.User{ID: requesterID})
}
if !contributionEntry.Contributed {
// remove item from contributor list
bill.Items[i].Contributors = removeUserFromContributors(requesterID, item.Contributors)
}
}
// update bill
_, err = b.billStorage.UpdateBill(bill)
return err
}

// removeEntryFromSlice removes the first occurrence of entry from slice
func removeEntryFromSlice(slice []uuid.UUID, entry uuid.UUID) []uuid.UUID {
for i, e := range slice {
Expand All @@ -175,10 +211,30 @@ func removeEntryFromSlice(slice []uuid.UUID, entry uuid.UUID) []uuid.UUID {
return slice
}

// removeUserFromContributors removes the user with the given userID from the contributors list
func removeUserFromContributors(userID uuid.UUID, contributors []model.User) []model.User {
for i, contributor := range contributors {
if contributor.ID == userID {
return append(contributors[:i], contributors[i+1:]...)
}
}
return contributors
}

// orderBillsByDate orders bills by date descending
func orderBillsByDate(bills []dto.BillDetailedOutput) []dto.BillDetailedOutput {
sort.Slice(bills, func(i, j int) bool {
return bills[i].Date.After(bills[j].Date)
})
return bills
}

// getItemFromID returns the index and the item with the given id from the items list
func getItemFromID(id uuid.UUID, items []model.Item) (int, model.Item) {
for i, item := range items {
if item.ID == id {
return i, item
}
}
return -1, model.Item{}
}
9 changes: 9 additions & 0 deletions presentation/dto/item.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ type ItemInput struct {
Contributors []uuid.UUID `json:"contributorIDs"`
}

type ContributionInput struct {
Contribution []HasContributed `json:"contribution"`
}

type HasContributed struct {
ItemID uuid.UUID `json:"itemID"`
Contributed bool `json:"contributed"`
}

type ItemOutput struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Expand Down
38 changes: 38 additions & 0 deletions presentation/handler/bill_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,41 @@ func (h BillHandler) GetAllByUser(c *fiber.Ctx) error {

return Success(c, fiber.StatusOK, SuccessMsgBillGetAll, bills)
}

// UpdateContribution updates the item contribution of the requester to the bill with the given id.
//
// @Summary Update Item Contribution
// @Tags Bill
// @Accept json
// @Produce json
// @Param id path string true "Bill ID"
// @Param request body dto.ContributionInput true "Request Body"
// @Success 200 {object} dto.GeneralResponse
// @Router /api/bill/{id}/contribution [put]
func (h BillHandler) UpdateContribution(c *fiber.Ctx) error {
// parse parameter
id := c.Params("id")
if id == "" {
return Error(c, fiber.StatusBadRequest, fmt.Sprintf(ErrMsgParameterRequired, "id"))
}
uid, err := uuid.Parse(id)
if err != nil {
return Error(c, fiber.StatusBadRequest, fmt.Sprintf(ErrMsgParseUUID, uid, err))
}
// parse request
var request dto.ContributionInput
if err = c.BodyParser(&request); err != nil {
return Error(c, fiber.StatusBadRequest, fmt.Sprintf(ErrMsgBillParse, err))
}
// get authenticated requester from context
requesterID := c.Locals(middleware.UserKey).(uuid.UUID)
// update item contribution
err = h.billService.HandleContribution(requesterID, uid, request)
if err != nil {
if errors.Is(err, domain.ErrNotAuthorized) {
return Error(c, fiber.StatusUnauthorized, fmt.Sprintf(ErrMsgBillUpdate, err))
}
return Error(c, fiber.StatusInternalServerError, fmt.Sprintf(ErrMsgBillUpdate, err))
}
return Success(c, fiber.StatusOK, SuccessMsgBillUpdate, nil)
}
1 change: 1 addition & 0 deletions presentation/router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func SetupRoutes(app *fiber.App, u UserHandler, g GroupHandler, b BillHandler, a
billRoute := api.Group("/bill")
// routes
billRoute.Post("/", a.Authenticate, b.Create)
billRoute.Put("/:id/contribution", a.Authenticate, b.UpdateContribution)
billRoute.Put("/:id", a.Authenticate, b.Update)
billRoute.Get("/:id", a.Authenticate, b.GetByID)
billRoute.Delete("/:id", a.Authenticate, b.Delete)
Expand Down

0 comments on commit 82d5bb0

Please sign in to comment.