-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* SAC-9 Create Category POST * SAC-9 Docs Fix * GML nitpicky changes & clean up * fixed so tests passing on local * lots of progress * casing permutations * forgot to add oops * fixed typo * move title casing logic to category --------- Co-authored-by: Alder Whiteford <[email protected]> Co-authored-by: garrettladley <[email protected]>
- Loading branch information
1 parent
2391ad3
commit 0472697
Showing
14 changed files
with
377 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package controllers | ||
|
||
import ( | ||
"backend/src/models" | ||
"backend/src/services" | ||
|
||
"github.com/gofiber/fiber/v2" | ||
) | ||
|
||
type CategoryController struct { | ||
categoryService services.CategoryServiceInterface | ||
} | ||
|
||
func NewCategoryController(categoryService services.CategoryServiceInterface) *CategoryController { | ||
return &CategoryController{categoryService: categoryService} | ||
} | ||
|
||
// CreateCategory godoc | ||
// | ||
// @Summary Create a category | ||
// @Description Creates a category that is used to group tags | ||
// @ID create-category | ||
// @Tags category | ||
// @Produce json | ||
// @Success 201 {object} models.Category | ||
// @Failure 400 {string} string "failed to process the request" | ||
// @Failure 400 {string} string "failed to validate data" | ||
// @Failure 400 {string} string "category with that name already exists" | ||
// @Failure 500 {string} string "failed to create category" | ||
// @Router /api/v1/category/ [post] | ||
func (t *CategoryController) CreateCategory(c *fiber.Ctx) error { | ||
var categoryBody models.CreateCategoryRequestBody | ||
|
||
if err := c.BodyParser(&categoryBody); err != nil { | ||
return fiber.NewError(fiber.StatusBadRequest, "failed to process the request") | ||
} | ||
|
||
category := models.Category{ | ||
Name: categoryBody.Name, | ||
} | ||
|
||
newCategory, err := t.categoryService.CreateCategory(category) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
return c.Status(fiber.StatusCreated).JSON(newCategory) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package services | ||
|
||
import ( | ||
"backend/src/models" | ||
"backend/src/transactions" | ||
"backend/src/utilities" | ||
|
||
"github.com/gofiber/fiber/v2" | ||
"golang.org/x/text/cases" | ||
"golang.org/x/text/language" | ||
"gorm.io/gorm" | ||
) | ||
|
||
type CategoryServiceInterface interface { | ||
CreateCategory(category models.Category) (*models.Category, error) | ||
} | ||
|
||
type CategoryService struct { | ||
DB *gorm.DB | ||
} | ||
|
||
func (c *CategoryService) CreateCategory(category models.Category) (*models.Category, error) { | ||
if err := utilities.ValidateData(category); err != nil { | ||
return nil, fiber.NewError(fiber.StatusBadRequest, "failed to validate the data") | ||
} | ||
|
||
category.Name = cases.Title(language.English).String(category.Name) | ||
|
||
return transactions.CreateCategory(c.DB, category) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package transactions | ||
|
||
import ( | ||
"backend/src/models" | ||
"errors" | ||
|
||
"github.com/gofiber/fiber/v2" | ||
"gorm.io/gorm" | ||
) | ||
|
||
func CreateCategory(db *gorm.DB, category models.Category) (*models.Category, error) { | ||
var existingCategory models.Category | ||
|
||
if err := db.Where("name = ?", category.Name).First(&existingCategory).Error; err != nil { | ||
if !errors.Is(err, gorm.ErrRecordNotFound) { | ||
return nil, fiber.NewError(fiber.StatusInternalServerError, "failed to create category") | ||
} | ||
} else { | ||
return nil, fiber.NewError(fiber.StatusBadRequest, "category with that name already exists") | ||
} | ||
|
||
if err := db.Create(&category).Error; err != nil { | ||
return nil, fiber.NewError(fiber.StatusInternalServerError, "failed to create category") | ||
} | ||
|
||
return &category, nil | ||
} | ||
|
||
func GetCategory(db *gorm.DB, id uint) (*models.Category, error) { | ||
var category models.Category | ||
|
||
if err := db.First(&category, id).Error; err != nil { | ||
if errors.Is(err, gorm.ErrRecordNotFound) { | ||
return nil, fiber.NewError(fiber.StatusNotFound, "invalid category id") | ||
} else { | ||
return nil, fiber.NewError(fiber.StatusInternalServerError, "unable to retrieve category") | ||
} | ||
} | ||
|
||
return &category, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package utilities | ||
|
||
func Contains[T comparable](slice []T, element T) bool { | ||
for _, v := range slice { | ||
if v == element { | ||
return true | ||
} | ||
} | ||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package tests | ||
|
||
import ( | ||
"backend/src/models" | ||
"backend/src/transactions" | ||
"io" | ||
"testing" | ||
|
||
"github.com/goccy/go-json" | ||
) | ||
|
||
func TestCreateCategoryWorks(t *testing.T) { | ||
app, assert, resp := RequestTesterWithJSONBody(t, "POST", "/api/v1/categories/", &map[string]interface{}{ | ||
"category_name": "Science", | ||
}, nil, nil, nil) | ||
defer app.DropDB() | ||
|
||
assert.Equal(201, resp.StatusCode) | ||
|
||
var respCategory models.Category | ||
|
||
err := json.NewDecoder(resp.Body).Decode(&respCategory) | ||
|
||
assert.NilError(err) | ||
|
||
dbCategory, err := transactions.GetCategory(app.Conn, respCategory.ID) | ||
|
||
assert.NilError(err) | ||
|
||
assert.Equal(dbCategory, &respCategory) | ||
} | ||
|
||
func TestCreateCategoryIgnoresid(t *testing.T) { | ||
app, assert, resp := RequestTesterWithJSONBody(t, "POST", "/api/v1/categories/", &map[string]interface{}{ | ||
"id": 12, | ||
"category_name": "Science", | ||
}, nil, nil, nil) | ||
defer app.DropDB() | ||
|
||
var respCategory models.Category | ||
|
||
err := json.NewDecoder(resp.Body).Decode(&respCategory) | ||
|
||
assert.NilError(err) | ||
|
||
dbCategory, err := transactions.GetCategory(app.Conn, respCategory.ID) | ||
|
||
assert.NilError(err) | ||
|
||
assert.NotEqual(12, dbCategory.ID) | ||
} | ||
|
||
func TestCreateCategoryFailsIfNameIsNotString(t *testing.T) { | ||
app, assert, resp := RequestTesterWithJSONBody(t, "POST", "/api/v1/categories/", &map[string]interface{}{ | ||
"category_name": 1231, | ||
}, nil, nil, nil) | ||
defer app.DropDB() | ||
|
||
defer resp.Body.Close() | ||
|
||
bodyBytes, err := io.ReadAll(resp.Body) | ||
|
||
assert.NilError(err) | ||
|
||
msg := string(bodyBytes) | ||
|
||
assert.Equal("failed to process the request", msg) | ||
|
||
assert.Equal(400, resp.StatusCode) | ||
} | ||
|
||
func TestCreateCategoryFailsIfNameIsMissing(t *testing.T) { | ||
app, assert, resp := RequestTesterWithJSONBody(t, "POST", "/api/v1/categories/", &map[string]interface{}{}, nil, nil, nil) | ||
defer app.DropDB() | ||
|
||
defer resp.Body.Close() | ||
|
||
bodyBytes, err := io.ReadAll(resp.Body) | ||
|
||
assert.NilError(err) | ||
|
||
msg := string(bodyBytes) | ||
|
||
assert.Equal("failed to validate the data", msg) | ||
|
||
assert.Equal(400, resp.StatusCode) | ||
} | ||
|
||
func TestCreateCategoryFailsIfCategoryWithThatNameAlreadyExists(t *testing.T) { | ||
categoryName := "Science" | ||
app, assert, resp := RequestTesterWithJSONBody(t, "POST", "/api/v1/categories/", &map[string]interface{}{ | ||
"category_name": categoryName, | ||
}, nil, nil, nil) | ||
|
||
assert.Equal(201, resp.StatusCode) | ||
|
||
var respCategory models.Category | ||
|
||
err := json.NewDecoder(resp.Body).Decode(&respCategory) | ||
|
||
assert.NilError(err) | ||
|
||
dbCategory, err := transactions.GetCategory(app.Conn, respCategory.ID) | ||
|
||
assert.NilError(err) | ||
|
||
assert.Equal(dbCategory, &respCategory) | ||
|
||
for _, permutation := range AllCasingPermutations(categoryName) { | ||
_, _, resp = RequestTesterWithJSONBody(t, "POST", "/api/v1/categories/", &map[string]interface{}{ | ||
"category_name": permutation, | ||
}, nil, &app, assert) | ||
|
||
defer resp.Body.Close() | ||
|
||
bodyBytes, err := io.ReadAll(resp.Body) | ||
|
||
assert.NilError(err) | ||
|
||
msg := string(bodyBytes) | ||
|
||
assert.Equal("category with that name already exists", msg) | ||
|
||
assert.Equal(400, resp.StatusCode) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.