Skip to content
This repository has been archived by the owner on Mar 18, 2023. It is now read-only.

Commit

Permalink
Merge pull request #20 from jmgooden94/feat/copy-enhancements
Browse files Browse the repository at this point in the history
feat: support query params for overwriting or renaming a drink during…
  • Loading branch information
nicjohnson145 authored Jul 21, 2022
2 parents 77fe9f0 + e1a397b commit 0284679
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 9 deletions.
4 changes: 4 additions & 0 deletions pkg/drink/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ func copyDrink(db *sql.DB) auth.FiberClaimsHandler {
return err
}

if c.Query("newName") != "" {
drink.DrinkData.Name = c.Query("newName")
}

id, err := createDrinkInternal(db, c, claims, drink.DrinkData)
if err != nil {
return err
Expand Down
35 changes: 29 additions & 6 deletions pkg/drink/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,33 @@ func createDrink(db *sql.DB) auth.FiberClaimsHandler {
}
}

type DrinkAlreadyExistsError struct {
DrinkId int64
Msg string
}

func (e DrinkAlreadyExistsError) Error() string {
return e.Msg
}

func createDrinkInternal(db *sql.DB, c *fiber.Ctx, claims jwt.Claims, drinkData DrinkData) (int64, error) {
existingDrink, err := getByNameAndUsername(drinkData.Name, claims.Username, db)
if err != nil && !errors.Is(err, common.ErrNotFound) {
return 0, common.NewInternalServerErrorResp("checking existance in DB", err)
}

if existingDrink != nil {
return 0, common.ErrorResponse{
Msg: fmt.Sprintf("user %v already has a drink named %v", claims.Username, drinkData.Name),
Err: fmt.Errorf("exising name/user combination"),
Status: fiber.StatusBadRequest,
if c.Query("overwrite") != "true" {
if existingDrink != nil {
e := DrinkAlreadyExistsError{
DrinkId: existingDrink.ID,
Msg: fmt.Sprintf("existing drink named %v", drinkData.Name),
}
return 0, common.ErrorResponse{
Err: e,
Msg: e.Msg,
Context: fmt.Sprintf("User: %v, Existing DrinkId: %d", claims.Username, e.DrinkId),
Status: fiber.StatusConflict,
}
}
}

Expand All @@ -64,7 +80,14 @@ func createDrinkInternal(db *sql.DB, c *fiber.Ctx, claims jwt.Claims, drinkData
return 0, common.NewInternalServerErrorResp("converting to DB model", err)
}

id, err := create(model, db)
var id int64
if existingDrink != nil {
model.ID = existingDrink.ID
err = updateModel(model, db)
id = existingDrink.ID
} else {
id, err = create(model, db)
}
if err != nil {
return 0, common.NewInternalServerErrorResp("inserting into DB", err)
}
Expand Down
1 change: 1 addition & 0 deletions pkg/drink/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package drink

import (
"database/sql"

"github.com/huandu/go-sqlbuilder"
"github.com/nicjohnson145/mixer-service/pkg/common"
)
Expand Down
150 changes: 147 additions & 3 deletions pkg/drink/drink_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,18 @@ func t_createDrink_ok(t *testing.T, app *fiber.App, r CreateDrinkRequest, o comm
return commontest.T_call_ok[CreateDrinkResponse](t, app, req)
}

func t_createDrink_fail(t *testing.T, app *fiber.App, r CreateDrinkRequest, o commontest.AuthOpts) (int, common.OutboundErrResponse) {
func t_createDrinkOverwrite_ok(t *testing.T, app *fiber.App, r CreateDrinkRequest, o commontest.AuthOpts) (int, CreateDrinkResponse) {
t.Helper()
req := commontest.T_req(t, commontest.Req[CreateDrinkRequest]{
Method: http.MethodPost,
Path: common.DrinksV1 + "/create?overwrite=true",
Body: &r,
Auth: &o,
})
return commontest.T_call_ok[CreateDrinkResponse](t, app, req)
}

func t_createDrink_DrinkAlreadyExists(t *testing.T, app *fiber.App, r CreateDrinkRequest, o commontest.AuthOpts) (int, common.OutboundErrResponse) {
t.Helper()
req := commontest.T_req(t, commontest.Req[CreateDrinkRequest]{
Method: http.MethodPost,
Expand Down Expand Up @@ -130,15 +141,36 @@ func t_copyDrink_ok(t *testing.T, app *fiber.App, id int64, o commontest.AuthOpt
return commontest.T_call_ok[CopyDrinkResponse](t, app, req)
}

func t_copyDrink_fail(t *testing.T, app *fiber.App, id int64, o commontest.AuthOpts) (int, common.OutboundErrResponse) {
func t_copyDrinkRename_ok(t *testing.T, app *fiber.App, id int64, o commontest.AuthOpts) (int, CopyDrinkResponse) {
t.Helper()
req := commontest.T_req(t, commontest.Req[any]{
req := commontest.T_req(t, commontest.Req[CopyDrinkResponse]{
Method: http.MethodPost,
Path: common.DrinksV1 + fmt.Sprintf("/%v", id) + "/copy?newName=DrinkCopy",
Auth: &o,
})
return commontest.T_call_ok[CopyDrinkResponse](t, app, req)
}

func t_copyDrinkOverwrite_ok(t *testing.T, app *fiber.App, id int64, o commontest.AuthOpts) (int, CopyDrinkResponse) {
t.Helper()
req := commontest.T_req(t, commontest.Req[CopyDrinkResponse]{
Method: http.MethodPost,
Path: common.DrinksV1 + fmt.Sprintf("/%v", id) + "/copy?overwrite=true",
Auth: &o,
})
return commontest.T_call_ok[CopyDrinkResponse](t, app, req)
}

func t_copyDrink_DrinkAlreadyExists(t *testing.T, app *fiber.App, id int64, o commontest.AuthOpts) (int, common.OutboundErrResponse) {
t.Helper()
req := commontest.T_req(t, commontest.Req[CopyDrinkResponse]{
Method: http.MethodPost,
Path: common.DrinksV1 + fmt.Sprintf("/%v", id) + "/copy",
Auth: &o,
})
return commontest.T_call_fail(t, app, req)
}

func TestFullCRUDLoop(t *testing.T) {
app, cleanup := setupDbAndApp(t)
defer cleanup()
Expand Down Expand Up @@ -231,6 +263,54 @@ func TestFullCRUDLoop(t *testing.T) {
_, _ = t_getDrink_fail(t, app, createResp.ID, commontest.AuthOpts{Username: to.StringPtr("user1")})
}

func TestCreateWithOverwriteAndGet(t *testing.T) {
app, cleanup := setupDbAndApp(t)
defer cleanup()

origDrinkData := DrinkData{
Name: "Daquiri",
PrimaryAlcohol: "Rum",
PreferredGlass: "Coupe",
Ingredients: []string{
"2.5 oz white rum",
"0.5 oz simple syrup",
"1 oz lime",
},
Publicity: DrinkPublicityPrivate,
}
overwriteDrinkData := DrinkData{
Name: "Daquiri",
PrimaryAlcohol: "Rum",
PreferredGlass: "Coupe",
Ingredients: []string{
"2.5 oz white rum",
"0.5 oz strawberry syrup",
"1 oz lime",
},
Publicity: DrinkPublicityPrivate,
}
body := CreateDrinkRequest{DrinkData: origDrinkData}
overwriteBody := CreateDrinkRequest{DrinkData: overwriteDrinkData}

// Creating a drink
_, createResp := t_createDrink_ok(t, app, body, commontest.AuthOpts{Username: to.StringPtr("user1")})
// Create it again with overwrite
_, overwriteResp := t_createDrinkOverwrite_ok(t, app, overwriteBody, commontest.AuthOpts{Username: to.StringPtr("user1")})

require.Equal(t, createResp.ID, overwriteResp.ID)

// Fetch it and it should be eqwual to the overwritten value
_, getResp := t_getDrink_ok(t, app, overwriteResp.ID, commontest.AuthOpts{Username: to.StringPtr("user1")})
expectedGetResp := GetDrinkResponse{
Drink: &Drink{
ID: 1,
Username: "user1",
DrinkData: overwriteDrinkData,
},
}
require.Equal(t, expectedGetResp, getResp)
}

func TestPublicDrinksFetchableAndCopyableByAnyone(t *testing.T) {
app, cleanup := setupDbAndApp(t)
defer cleanup()
Expand All @@ -247,10 +327,26 @@ func TestPublicDrinksFetchableAndCopyableByAnyone(t *testing.T) {
Publicity: DrinkPublicityPublic,
}

updatedDrinkData := DrinkData{
Name: "Daquari",
PrimaryAlcohol: "Rum",
PreferredGlass: "Coupe",
Ingredients: []string{
"2.5 oz white rum",
"0.5 oz strawberry syrup",
"1 oz lime",
},
Publicity: DrinkPublicityPublic,
}

body := CreateDrinkRequest{DrinkData: drinkData}

// Creating a drink
_, createResp := t_createDrink_ok(t, app, body, commontest.AuthOpts{Username: to.StringPtr("user1")})
// Creating exact same drink again should fail
status, errResp := t_createDrink_DrinkAlreadyExists(t, app, body, commontest.AuthOpts{Username: to.StringPtr("user1")})
require.Equal(t, http.StatusConflict, status)
require.Equal(t, "existing drink named Daquari", errResp.Error)

// Fetch it as someone else, it should succeed since it's public
_, getResp := t_getDrink_ok(t, app, createResp.ID, commontest.AuthOpts{Username: to.StringPtr("user2")})
Expand All @@ -265,6 +361,10 @@ func TestPublicDrinksFetchableAndCopyableByAnyone(t *testing.T) {

// Copy it as someone else
_, copyResp := t_copyDrink_ok(t, app, createResp.ID, commontest.AuthOpts{Username: to.StringPtr("user2")})
// Copy again without overwrite or rename should fail
status, errResp = t_copyDrink_DrinkAlreadyExists(t, app, createResp.ID, commontest.AuthOpts{Username: to.StringPtr("user2")})
require.Equal(t, http.StatusConflict, status)
require.Equal(t, "existing drink named Daquari", errResp.Error)

// Get it and it should be the same as the original but with a new owner and id
_, getResp = t_getDrink_ok(t, app, copyResp.ID, commontest.AuthOpts{Username: to.StringPtr("user2")})
Expand All @@ -276,6 +376,50 @@ func TestPublicDrinksFetchableAndCopyableByAnyone(t *testing.T) {
},
}
require.Equal(t, expectedGetResp, getResp)

// update it (so we can copy it again with overwrite)
// Update it
updateReq := UpdateDrinkRequest{
DrinkData: updatedDrinkData,
}
_, _ = t_updateDrink_ok(t, app, 2, updateReq, commontest.AuthOpts{Username: to.StringPtr("user2")})
// Get it and it should be updated
_, getResp = t_getDrink_ok(t, app, copyResp.ID, commontest.AuthOpts{Username: to.StringPtr("user2")})
expectedGetResp = GetDrinkResponse{
Drink: &Drink{
ID: 2,
Username: "user2",
DrinkData: updatedDrinkData,
},
}
require.Equal(t, expectedGetResp, getResp)
// Copy it again with overwrite
_, copyResp = t_copyDrinkOverwrite_ok(t, app, createResp.ID, commontest.AuthOpts{Username: to.StringPtr("user2")})
// Get it and it should be back to the original
_, getResp = t_getDrink_ok(t, app, copyResp.ID, commontest.AuthOpts{Username: to.StringPtr("user2")})
expectedGetResp = GetDrinkResponse{
Drink: &Drink{
ID: 2,
Username: "user2",
DrinkData: drinkData,
},
}
require.Equal(t, expectedGetResp, getResp)

// Copy it again with new name
_, copyResp = t_copyDrinkRename_ok(t, app, createResp.ID, commontest.AuthOpts{Username: to.StringPtr("user2")})

// Get it and it should be the same as the original but with a new name and id
_, getResp = t_getDrink_ok(t, app, copyResp.ID, commontest.AuthOpts{Username: to.StringPtr("user2")})
drinkData.Name = "DrinkCopy"
expectedGetResp = GetDrinkResponse{
Drink: &Drink{
ID: 3,
Username: "user2",
DrinkData: drinkData,
},
}
require.Equal(t, expectedGetResp, getResp)
}

func TestGetDrinksByUser(t *testing.T) {
Expand Down

0 comments on commit 0284679

Please sign in to comment.