Skip to content

Commit

Permalink
feat: add software and software logs endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
bfabio committed Jul 14, 2022
1 parent 6ee8c93 commit abd53fd
Show file tree
Hide file tree
Showing 9 changed files with 317 additions and 19 deletions.
6 changes: 6 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ linters-settings:
- .WithMessage(
- .WithMessagef(
- .WithStack(
funlen:
# Increase the number of lines, considering funlen counts comments as well
# (https://github.com/ultraware/funlen/issues/12)
#
# default: 60
lines: 80

linters:
enable-all: true
Expand Down
8 changes: 7 additions & 1 deletion internal/database/postgres_database.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ func (d *PostgresDB) Init(dsn string) (*gorm.DB, error) {
return nil, fmt.Errorf("can't open database: %w", err)
}

if err = database.AutoMigrate(&models.Publisher{}, &models.CodeHosting{}, &models.Log{}); err != nil {
if err = database.AutoMigrate(
&models.Publisher{},
&models.CodeHosting{},
&models.Log{},
&models.Software{},
&models.SoftwareURL{},
); err != nil {
return nil, fmt.Errorf("can't migrate database: %w", err)
}

Expand Down
8 changes: 7 additions & 1 deletion internal/database/sqlite_database.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ func (d *SQLiteDB) Init(dsn string) (*gorm.DB, error) {
return nil, fmt.Errorf("can't open database: %w", err)
}

if err = database.AutoMigrate(&models.Publisher{}, &models.CodeHosting{}, &models.Log{}); err != nil {
if err = database.AutoMigrate(
&models.Publisher{},
&models.CodeHosting{},
&models.Log{},
&models.Software{},
&models.SoftwareURL{},
); err != nil {
return nil, fmt.Errorf("can't migrate database: %w", err)
}

Expand Down
3 changes: 1 addition & 2 deletions internal/handlers/general/pagination.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ type PaginationLinks paginator.Cursor

func NewPaginator(ctx *fiber.Ctx) *paginator.Paginator {
paginator := paginator.New(&paginator.Config{
Rules: []paginator.Rule{{Key: "ID"}, {Key: "CreatedAt"}},
Keys: []string{},
Keys: []string{"ID", "CreatedAt"},
Limit: DefaultLimitCount,
Order: paginator.ASC,
})
Expand Down
101 changes: 94 additions & 7 deletions internal/handlers/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"

"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/utils"
"github.com/italia/developers-italia-api/internal/common"
"github.com/italia/developers-italia-api/internal/handlers/general"
"github.com/italia/developers-italia-api/internal/models"
Expand All @@ -17,6 +18,9 @@ type LogInterface interface {
PostLog(ctx *fiber.Ctx) error
PatchLog(ctx *fiber.Ctx) error
DeleteLog(ctx *fiber.Ctx) error

GetSoftwareLogs(ctx *fiber.Ctx) error
PostSoftwareLog(ctx *fiber.Ctx) error
}

type Log struct {
Expand Down Expand Up @@ -68,7 +72,7 @@ func (p *Log) GetLogs(ctx *fiber.Ctx) error {
func (p *Log) GetLog(ctx *fiber.Ctx) error {
log := models.Log{}

if err := p.db.First(&log, ctx.Params("id")).Error; err != nil {
if err := p.db.First(&log, "id = ?", ctx.Params("id")).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return common.Error(fiber.StatusNotFound, "can't get Log", "Log was not found")
}
Expand All @@ -91,7 +95,7 @@ func (p *Log) PostLog(ctx *fiber.Ctx) error {
return common.Error(fiber.StatusUnprocessableEntity, "can't create Log", "invalid format")
}

log := models.Log{Message: logReq.Message}
log := models.Log{ID: utils.UUIDv4(), Message: logReq.Message}

if err := p.db.Create(&log).Error; err != nil {
return common.Error(fiber.StatusInternalServerError, "can't create Log", "db error")
Expand All @@ -114,7 +118,7 @@ func (p *Log) PatchLog(ctx *fiber.Ctx) error {

log := models.Log{}

if err := p.db.First(&log, ctx.Params("id")).Error; err != nil {
if err := p.db.First(&log, "id = ?", ctx.Params("id")).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return common.Error(fiber.StatusNotFound, "can't update Log", "Log was not found")
}
Expand All @@ -135,13 +139,96 @@ func (p *Log) PatchLog(ctx *fiber.Ctx) error {
func (p *Log) DeleteLog(ctx *fiber.Ctx) error {
var log models.Log

if err := p.db.Delete(&log, ctx.Params("id")).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return common.Error(fiber.StatusNotFound, "can't delete Log", "Log was not found")
}
result := p.db.Delete(&log, "id = ?", ctx.Params("id"))

if result.Error != nil {
return common.Error(fiber.StatusInternalServerError, "can't delete Log", "db error")
}

if result.RowsAffected == 0 {
return common.Error(fiber.StatusNotFound, "can't delete Log", "Log was not found")
}

return ctx.SendStatus(fiber.StatusNoContent)
}

// GetSoftwareLogs gets the logs associated to a Software with the given ID and returns any error encountered.
func (p *Log) GetSoftwareLogs(ctx *fiber.Ctx) error {
var logs []models.Log

software := models.Software{}

if err := p.db.First(&software, "id = ?", ctx.Params("id")).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return common.Error(fiber.StatusNotFound, "can't get Software", "Software was not found")
}

return common.Error(
fiber.StatusInternalServerError,
"can't get Software",
fiber.ErrInternalServerError.Message,
)
}

stmt := p.db.Where("entity_id = ? AND entity_type = 'Software'", software.ID)

paginator := general.NewPaginator(ctx)

result, cursor, err := paginator.Paginate(stmt, &logs)
if err != nil {
return common.Error(
fiber.StatusBadRequest,
"can't get Software",
"wrong cursor format in page[after] or page[before]",
)
}

if result.Error != nil {
return common.Error(
fiber.StatusInternalServerError,
"can't get Software",
fiber.ErrInternalServerError.Message,
)
}

return ctx.JSON(fiber.Map{"data": &logs, "links": general.PaginationLinks(cursor)})
}

// PostSoftwareLog creates a new log associated to a Software with the given ID and returns any error encountered.
func (p *Log) PostSoftwareLog(ctx *fiber.Ctx) error {
logReq := new(requests.Log)

if err := ctx.BodyParser(&logReq); err != nil {
return common.Error(fiber.StatusBadRequest, "can't create Log", "invalid json")
}

if err := common.ValidateStruct(*logReq); err != nil {
return common.Error(fiber.StatusUnprocessableEntity, "can't create Log", "invalid format")
}

software := models.Software{}
if err := p.db.First(&software, "id = ?", ctx.Params("id")).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return common.Error(fiber.StatusNotFound, "can't get Software", "Software was not found")
}

return common.Error(
fiber.StatusInternalServerError,
"can't get Software",
fiber.ErrInternalServerError.Message,
)
}

log := models.Log{
ID: utils.UUIDv4(),
Message: logReq.Message,
EntityID: software.ID,
EntityType: "Software",
}

if err := p.db.Create(&log).Error; err != nil {
return common.Error(fiber.StatusInternalServerError, "can't create Log", "db error")
}

return ctx.JSON(&log)
}
158 changes: 158 additions & 0 deletions internal/handlers/software.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package handlers

import (
"errors"

"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/utils"
"github.com/italia/developers-italia-api/internal/common"
"github.com/italia/developers-italia-api/internal/handlers/general"
"github.com/italia/developers-italia-api/internal/models"
"github.com/italia/developers-italia-api/internal/requests"
"gorm.io/gorm"
)

type SoftwareInterface interface {
GetAllSoftware(ctx *fiber.Ctx) error
GetSoftware(ctx *fiber.Ctx) error
PostSoftware(ctx *fiber.Ctx) error
PatchSoftware(ctx *fiber.Ctx) error
DeleteSoftware(ctx *fiber.Ctx) error
}

type Software struct {
db *gorm.DB
}

func NewSoftware(db *gorm.DB) *Software {
return &Software{db: db}
}

// GetAllSoftware gets the list of all software and returns any error encountered.
func (p *Software) GetAllSoftware(ctx *fiber.Ctx) error {
var software []models.Software

stmt := p.db.Begin().Preload("URLs")

paginator := general.NewPaginator(ctx)

result, cursor, err := paginator.Paginate(stmt, &software)
if err != nil {
return common.Error(
fiber.StatusBadRequest,
"can't get Software",
"wrong cursor format in page[after] or page[before]",
)
}

if result.Error != nil {
return common.Error(
fiber.StatusInternalServerError,
"can't get Software",
result.Error.Error(),
// fiber.ErrInternalServerError.Message,
)
}

return ctx.JSON(fiber.Map{"data": &software, "links": general.PaginationLinks(cursor)})
}

// GetSoftware gets the software with the given ID and returns any error encountered.
func (p *Software) GetSoftware(ctx *fiber.Ctx) error {
software := models.Software{}

if err := p.db.First(&software, ctx.Params("id")).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return common.Error(fiber.StatusNotFound, "can't get Software", "Software was not found")
}

return common.Error(
fiber.StatusInternalServerError,
"can't get Software",
// fiber.ErrInternalServerError.Message,
err.Error(),
)
}

return ctx.JSON(&software)
}

// PostSoftware creates a new software.
func (p *Software) PostSoftware(ctx *fiber.Ctx) error {
softwareReq := new(requests.Software)

if err := ctx.BodyParser(&softwareReq); err != nil {
return common.Error(fiber.StatusBadRequest, "can't create Software", "invalid json")
}

if err := common.ValidateStruct(*softwareReq); err != nil {
return common.Error(fiber.StatusUnprocessableEntity, "can't create Software", "invalid format", err)
}

softwareURLs := []models.SoftwareURL{}
for _, u := range softwareReq.URLs {
softwareURLs = append(softwareURLs, models.SoftwareURL{ID: utils.UUIDv4(), URL: u})
}

software := models.Software{
ID: utils.UUIDv4(),
URLs: softwareURLs,
}

if err := p.db.Create(&software).Error; err != nil {
return common.Error(fiber.StatusInternalServerError, "can't create Software", err.Error())
}

return ctx.JSON(&software)
}

// PatchSoftware updates the software with the given ID.
func (p *Software) PatchSoftware(ctx *fiber.Ctx) error {
softwareReq := new(requests.Software)

if err := ctx.BodyParser(softwareReq); err != nil {
return common.Error(fiber.StatusBadRequest, "can't update Software", "invalid json")
}

if err := common.ValidateStruct(*softwareReq); err != nil {
return common.Error(fiber.StatusUnprocessableEntity, "can't update Software", "invalid format", err)
}

software := models.Software{}

if err := p.db.First(&software, ctx.Params("id")).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return common.Error(fiber.StatusNotFound, "can't update Software", "Software was not found")
}

return common.Error(fiber.StatusInternalServerError, "can't update Software", "internal server error")
}

softwareURLs := []models.SoftwareURL{}
for _, u := range softwareReq.URLs {
softwareURLs = append(softwareURLs, models.SoftwareURL{ID: utils.UUIDv4(), URL: u})
}

software.URLs = softwareURLs

if err := p.db.Updates(&software).Error; err != nil {
return common.Error(fiber.StatusInternalServerError, "can't update Software", "db error")
}

return ctx.JSON(&software)
}

// DeleteSoftware deletes the software with the given ID.
func (p *Software) DeleteSoftware(ctx *fiber.Ctx) error {
var software models.Software

if err := p.db.Delete(&software, ctx.Params("id")).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return common.Error(fiber.StatusNotFound, "can't delete Software", "Software was not found")
}

return common.Error(fiber.StatusInternalServerError, "can't delete Software", "db error")
}

return ctx.SendStatus(fiber.StatusNoContent)
}
Loading

0 comments on commit abd53fd

Please sign in to comment.