Skip to content

Commit

Permalink
Merge pull request #19 from abaldeweg/feat/format
Browse files Browse the repository at this point in the history
implement format
  • Loading branch information
abaldeweg authored Nov 26, 2024
2 parents 2aec307 + 5a07947 commit 3744c64
Show file tree
Hide file tree
Showing 7 changed files with 455 additions and 18 deletions.
190 changes: 190 additions & 0 deletions gateway/core/controllers/format.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
package controllers

import (
"net/http"
"strconv"

"github.com/abaldeweg/warehouse-server/gateway/auth"
"github.com/abaldeweg/warehouse-server/gateway/core/models"
"github.com/abaldeweg/warehouse-server/gateway/core/repository"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)

// FormatController handles requests related to book formats.
type FormatController struct {
formatRepo *repository.FormatRepository
db *gorm.DB
}

// NewFormatController instantiates a new FormatController.
func NewFormatController(db *gorm.DB) *FormatController {
return &FormatController{
formatRepo: repository.NewFormatRepository(db),
db: db,
}
}

// FindAll retrieves all formats for the authenticated user's branch.
func (fc *FormatController) FindAll(c *gin.Context) {
user, ok := c.Get("user")
if !ok {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"msg": "Unauthorized"})
return
}

formats, err := fc.formatRepo.FindAllByBranchID(uint(user.(auth.User).Branch.Id))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve formats"})
return
}

c.JSON(http.StatusOK, formats)
}

// FindOne retrieves a specific format by ID for the authenticated user's branch.
func (fc *FormatController) FindOne(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
return
}

format, err := fc.formatRepo.FindOne(uint(id))
if err != nil {
if err == gorm.ErrRecordNotFound {
c.JSON(http.StatusNotFound, gin.H{"error": "Format not found"})
return
}

c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve format"})
return
}

c.JSON(http.StatusOK, format)
}

// Create creates a new format for the authenticated user's branch.
func (fc *FormatController) Create(c *gin.Context) {
var format models.Format
if err := c.ShouldBindJSON(&format); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

user, ok := c.Get("user")
if !ok {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"msg": "Unauthorized"})
return
}
format.BranchID = uint(user.(auth.User).Branch.Id)

if !format.Validate(fc.db) {
c.JSON(http.StatusBadRequest, gin.H{"error": "Validation failed"})
return
}

if err := fc.formatRepo.Create(&format); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create format"})
return
}

createdFormat, err := fc.formatRepo.FindOne(format.ID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve created format"})
return
}

c.JSON(http.StatusCreated, createdFormat)

}

// Update updates an existing format for the authenticated user's branch.
func (fc *FormatController) Update(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
return
}

existingFormat, err := fc.formatRepo.FindOne(uint(id))
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Format not found"})
return
}

user, ok := c.Get("user")
if !ok {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"msg": "Unauthorized"})
return
}

if uint(user.(auth.User).Branch.Id) != existingFormat.BranchID {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"msg": "Forbidden"})
return
}

var format models.Format
if err := c.ShouldBindJSON(&format); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Format has invalid data"})
return
}

format.ID = existingFormat.ID
format.BranchID = existingFormat.BranchID

if !format.Validate(fc.db) {
c.JSON(http.StatusBadRequest, gin.H{"error": "Validation failed"})
return
}

if err := fc.formatRepo.Update(&format); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update format"})
return
}

updatedFormat, err := fc.formatRepo.FindOne(uint(id))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve updated format"})
return
}

c.JSON(http.StatusOK, updatedFormat)
}

// Delete deletes a format by ID for the authenticated user's branch.
func (fc *FormatController) Delete(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
return
}

user, ok := c.Get("user")
if !ok {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"msg": "Unauthorized"})
return
}

format, err := fc.formatRepo.FindOne(uint(id))
if err != nil {
if err == gorm.ErrRecordNotFound {
c.JSON(http.StatusNotFound, gin.H{"error": "Format not found"})
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve format"})
return
}

if uint(user.(auth.User).Branch.Id) != format.BranchID {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"msg": "Forbidden"})
return
}

if err := fc.formatRepo.Delete(uint(id)); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete format"})
return
}

c.JSON(http.StatusNoContent, nil)
}
1 change: 1 addition & 0 deletions gateway/core/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func runMigrations(db *gorm.DB) {
&models.Condition{},
&models.Tag{},
&models.Genre{},
&models.Format{},
)

if err != nil {
Expand Down
12 changes: 6 additions & 6 deletions gateway/core/models/book.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ type Book struct {
ConditionID uint `json:"condition_id"`
Tags []*Tag `json:"tags" gorm:"many2many:book_tag;"`
// Reservation []*Reservation `json:"reservations" gorm:"foreignKey:BookID"`
Recommendation bool `json:"recommendations" gorm:"foreignKey:BookID"`
Inventory bool `json:"inventory" gorm:"default:false"`
// Format *Format `json:"format" gorm:"foreignKey:FormatID"`
FormatID uint `json:"format_id" gorm:"not null"`
Subtitle string `json:"subtitle" validate:"max=255"`
Duplicate bool `gorm:"default:false"`
Recommendation bool `json:"recommendations" gorm:"foreignKey:BookID"`
Inventory bool `json:"inventory" gorm:"default:false"`
Format *Format `json:"format" gorm:"foreignKey:FormatID"`
FormatID uint `json:"format_id" gorm:"not null"`
Subtitle string `json:"subtitle" validate:"max=255"`
Duplicate bool `gorm:"default:false"`
}

// TableName overrides the default table name for Book model.
Expand Down
30 changes: 30 additions & 0 deletions gateway/core/models/format.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package models

import (
"github.com/go-playground/validator/v10"
"gorm.io/gorm"
)

// Format represents a book format entity.
type Format struct {
ID uint `json:"id" gorm:"primaryKey;autoIncrement;->"`
Name string `json:"name" validate:"required,min=1,max=255"`
BranchID uint `json:"branch_id" gorm:"index"`
Branch Branch `json:"branch" gorm:"foreignKey:BranchID"`
Books []Book `json:"-" gorm:"foreignKey:FormatID"`
}

// TableName overrides the default table name for Format model.
func (Format) TableName() string {
return "format"
}

// Validate validates the Format model based on defined rules.
func (f *Format) Validate(db *gorm.DB) bool {
validate := validator.New()
if err := validate.StructExcept(f, "Branch", "Books"); err != nil {
return false
}

return true
}
48 changes: 48 additions & 0 deletions gateway/core/repository/format.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package repository

import (
"github.com/abaldeweg/warehouse-server/gateway/core/models"
"gorm.io/gorm"
)

// FormatRepository struct for format repository.
type FormatRepository struct {
db *gorm.DB
}

// NewFormatRepository creates a new format repository.
func NewFormatRepository(db *gorm.DB) *FormatRepository {
return &FormatRepository{db: db}
}

// FindAllByBranchID returns all formats for a given branch ID, ordered alphabetically by name.
func (r *FormatRepository) FindAllByBranchID(branchID uint) ([]models.Format, error) {
var formats []models.Format
result := r.db.Preload("Branch").Where("branch_id = ?", branchID).Order("name asc").Find(&formats)
return formats, result.Error
}

// FindOne returns one format by id and branchID.
func (r *FormatRepository) FindOne(id uint) (models.Format, error) {
var format models.Format
result := r.db.Preload("Branch").Where("id = ?", id).First(&format)
return format, result.Error
}

// Create creates a new format.
func (r *FormatRepository) Create(format *models.Format) error {
result := r.db.Create(format)
return result.Error
}

// Update updates a format.
func (r *FormatRepository) Update(format *models.Format) error {
result := r.db.Save(format)
return result.Error
}

// Delete deletes a format.
func (r *FormatRepository) Delete(id uint) error {
result := r.db.Delete(&models.Format{}, id)
return result.Error
}
Loading

0 comments on commit 3744c64

Please sign in to comment.