Skip to content

Commit

Permalink
feat(report) : CRUD report
Browse files Browse the repository at this point in the history
  • Loading branch information
Ndraaa15 committed Apr 27, 2024
1 parent 4680160 commit 3d4cbd0
Show file tree
Hide file tree
Showing 9 changed files with 349 additions and 2 deletions.
3 changes: 3 additions & 0 deletions cmd/api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func main() {
verifRepo := repository.NewVerificationRepository(db)
merchantRepo := repository.NewMerchantRepository(db)
campaignRepo := repository.NewCampaignRepository(db)
reportRepo := repository.NewReportRepository(db)
authUsecase := usecase.NewAuthUsecase(userRepo, verifRepo, mailer, log)
rest.RegisterAuthHandler(authUsecase, validator, api)
userUsecase := usecase.NewUserUsecase(userRepo, merchantRepo, supabaseImg, log)
Expand All @@ -43,6 +44,8 @@ func main() {
rest.RegisterAnimalHandler(animalUsecase, api)
campaignUsecase := usecase.NewCampaignUsecase(campaignRepo, log, supabaseClient)
rest.RegisterCampaignHandler(campaignUsecase, api, validator)
reportUsecase := usecase.NewReportUsecase(reportRepo, supabaseClient)
rest.RegisterReportHandler(reportUsecase, validator, api)

if err := app.Listen(":" + os.Getenv("APP_PORT")); err != nil {
log.Fatalf("Fail to start server: %v", err)
Expand Down
2 changes: 1 addition & 1 deletion db/migrations/20240318091221_users.up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ CREATE TABLE Users (
Email VARCHAR(255) NOT NULL UNIQUE,
Password CHAR(60) NOT NULL,
Name VARCHAR(255) NOT NULL,
ProfilePicture TEXT NOT NULL DEFAULT '',
ProfilePicture TEXT NOT NULL,
Active BOOLEAN NOT NULL DEFAULT FALSE,
Admin BOOLEAN NOT NULL DEFAULT FALSE,
Premium BOOLEAN NOT NULL DEFAULT FALSE,
Expand Down
1 change: 1 addition & 0 deletions db/migrations/20240427111543_reports.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE IF EXISTS Reports;
11 changes: 11 additions & 0 deletions db/migrations/20240427111543_reports.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CREATE TABLE Reports (
ID BIGINT UNSIGNED AUTO_INCREMENT,

Picture TEXT NOT NULL,
Description TEXT NOT NULL,
Location TEXT NOT NULL,
Action ENUM('PENDING', 'APPROVED', 'REJECTED') NOT NULL DEFAULT 'PENDING',
CreatedAt DATETIME NOT NULL DEFAULT NOW(),

PRIMARY KEY (ID)
) ENGINE = INNODB DEFAULT CHARSET = UTF8;
68 changes: 68 additions & 0 deletions internal/delivery/rest/report.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package rest

import (
"strconv"

"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
"github.com/mirzahilmi/hackathon/internal/model"
"github.com/mirzahilmi/hackathon/internal/usecase"
)

type ReportHandler struct {
reportUsecase usecase.ReportUsecaseItf
validator *validator.Validate
}

func RegisterReportHandler(
usecase usecase.ReportUsecaseItf,
validator *validator.Validate,
router fiber.Router,
) {
reportHandler := ReportHandler{usecase, validator}
router = router.Group("/reports")
router.Post("", reportHandler.CreateReport)
router.Get("", reportHandler.GetReports)
router.Patch("/:id<int>", reportHandler.UpdateReport)
}

func (h *ReportHandler) CreateReport(c *fiber.Ctx) error {
pict, err := c.FormFile("picture")
if err != nil {
return err
}

req := model.ReportRequest{
Picture: pict,
Description: c.FormValue("description"),
Location: c.FormValue("location"),
}
if err := h.validator.Struct(&req); err != nil {
return err
}
if err := h.reportUsecase.CreateReport(c.Context(), req); err != nil {
return err
}
return c.Status(fiber.StatusOK).JSON(fiber.Map{"message": "Report created"})
}

func (h *ReportHandler) GetReports(c *fiber.Ctx) error {
reports, err := h.reportUsecase.GetReports(c.Context())
if err != nil {
return err
}
return c.Status(fiber.StatusOK).JSON(reports)
}

func (h *ReportHandler) UpdateReport(c *fiber.Ctx) error {
id, err := strconv.ParseInt(c.Params("id"), 10, 64)
if err != nil {
return err
}

value := c.Query("action")
if err := h.reportUsecase.UpdateReport(c.Context(), id, value); err != nil {
return err
}
return c.Status(fiber.StatusOK).JSON(fiber.Map{"message": "Report updated"})
}
18 changes: 18 additions & 0 deletions internal/model/report.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package model

import "mime/multipart"

type ReportResource struct {
ID int64 `json:"id" db:"ID"`
Picture string `json:"picture" db:"Picture"`
Description string `json:"description" db:"Description"`
Location string `json:"location" db:"Location"`
CreatedAt string `json:"createdAt" db:"CreatedAt"`
Action string `json:"action" db:"Action"`
}

type ReportRequest struct {
Picture *multipart.FileHeader `form:"picture" validate:"required"`
Description string `form:"description" validate:"required"`
Location string `form:"location" validate:"required"`
}
38 changes: 37 additions & 1 deletion internal/repository/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,5 +195,41 @@ const (
Code
FROM Merchants
WHERE %s = ?
`
`

// Reports
qCreateReport = `
INSERT INTO Reports
(Picture, Description, Location)
VALUE
(:Picture, :Description, :Location);
`
qGetReports = `
SELECT
ID,
Picture,
Description,
Location,
CreatedAt,
Action
FROM Reports
ORDER BY CreatedAt DESC;
`
qUpdateReport = `
UPDATE Reports
SET
Action = :Action
WHERE ID = :ID;
`
qGetReportByID = `
SELECT
ID,
Description,
Location,
CreatedAt,
Action
FROM Reports
WHERE ID = ?
LIMIT 1;
`
)
117 changes: 117 additions & 0 deletions internal/repository/report.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package repository

import (
"context"
"log"

"github.com/gofiber/fiber/v2"
"github.com/jmoiron/sqlx"
"github.com/mirzahilmi/hackathon/internal/model"
)

type ReportRepositoryItf interface {
NewClient(withTx bool, tx sqlx.ExtContext) (ReportQueryerItf, error)
}

type ReportRepository struct {
db *sqlx.DB
}

func NewReportRepository(db *sqlx.DB) ReportRepositoryItf {
return &ReportRepository{db}
}

type ReportQueryerItf interface {
txCompat
CreateReport(ctx context.Context, report *model.ReportResource) error
GetReports(ctx context.Context) ([]model.ReportResource, error)
UpdateActionReport(ctx context.Context, report *model.ReportResource) error
GetReportByID(ctx context.Context, id int64) (model.ReportResource, error)
}

type reportQueryer struct {
ext sqlx.ExtContext
}

func (r *ReportRepository) NewClient(withTx bool, tx sqlx.ExtContext) (ReportQueryerItf, error) {
if withTx {
if tx != nil {
return &reportQueryer{tx}, nil
}
tx, err := r.db.Beginx()
if err != nil {
return nil, err
}
return &reportQueryer{tx}, nil
}
return &reportQueryer{r.db}, nil
}

func (q *reportQueryer) Commit() error {
tx, ok := q.ext.(*sqlx.Tx)
if !ok {
return ErrNotTransaction
}
return tx.Commit()
}

func (q *reportQueryer) Rollback() error {
tx, ok := q.ext.(*sqlx.Tx)
if !ok {
return ErrNotTransaction
}
return tx.Rollback()
}

func (q *reportQueryer) Ext() sqlx.ExtContext {
return q.ext
}

func (q *reportQueryer) CreateReport(ctx context.Context, report *model.ReportResource) error {
query, args, err := sqlx.Named(qCreateReport, fiber.Map{
"Picture": report.Picture,
"Description": report.Description,
"Location": report.Location,
})
log.Println(query)
log.Println(args)
if err != nil {
return err
}
_, err = q.ext.ExecContext(ctx, query, args...)
if err != nil {
return err
}
return nil
}

func (q *reportQueryer) GetReports(ctx context.Context) ([]model.ReportResource, error) {
var reports []model.ReportResource
if err := sqlx.SelectContext(ctx, q.ext, &reports, qGetReports); err != nil {
return nil, err
}
return reports, nil
}

func (q *reportQueryer) UpdateActionReport(ctx context.Context, report *model.ReportResource) error {
query, args, err := sqlx.Named(qUpdateReport, fiber.Map{
"Action": report.Action,
"ID": report.ID,
})
if err != nil {
return err
}
_, err = q.ext.ExecContext(ctx, query, args...)
if err != nil {
return err
}
return nil
}

func (q *reportQueryer) GetReportByID(ctx context.Context, id int64) (model.ReportResource, error) {
var report model.ReportResource
if err := sqlx.GetContext(ctx, q.ext, &report, qGetReportByID, id); err != nil {
return model.ReportResource{}, err
}
return report, nil
}
93 changes: 93 additions & 0 deletions internal/usecase/report.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package usecase

import (
"context"
"fmt"
"log"
"os"
"time"

"github.com/mirzahilmi/hackathon/internal/model"
"github.com/mirzahilmi/hackathon/internal/repository"
storage_go "github.com/supabase-community/storage-go"
)

type ReportUsecaseItf interface {
CreateReport(ctx context.Context, req model.ReportRequest) error
GetReports(ctx context.Context) ([]model.ReportResource, error)
UpdateReport(ctx context.Context, id int64, action string) error
}

type ReportUsecase struct {
reportRepo repository.ReportRepositoryItf
supabase *storage_go.Client
}

func NewReportUsecase(reportRepo repository.ReportRepositoryItf, supabase *storage_go.Client) ReportUsecaseItf {
return &ReportUsecase{reportRepo, supabase}
}

func (u *ReportUsecase) CreateReport(ctx context.Context, req model.ReportRequest) error {
client, err := u.reportRepo.NewClient(true, nil)
if err != nil {
return err
}
defer client.Rollback()

req.Picture.Filename = fmt.Sprintf("%d_%s", time.Now().UnixMilli(), req.Picture.Filename)
pict, err := req.Picture.Open()
if err != nil {
return err
}

_, err = u.supabase.UploadFile(os.Getenv("SUPABASE_BUCKET_ID"), req.Picture.Filename, pict)
if err != nil {
return err
}

pictUrl := u.supabase.GetPublicUrl(os.Getenv("SUPABASE_BUCKET_ID"), req.Picture.Filename)
report := model.ReportResource{
Picture: pictUrl.SignedURL,
Description: req.Description,
Location: req.Location,
}
log.Println(report)
if err := client.CreateReport(ctx, &report); err != nil {
return err
}

return client.Commit()
}

func (u *ReportUsecase) GetReports(ctx context.Context) ([]model.ReportResource, error) {
client, err := u.reportRepo.NewClient(false, nil)
if err != nil {
return nil, err
}

reports, err := client.GetReports(ctx)
if err != nil {
return nil, err
}

return reports, nil
}

func (u *ReportUsecase) UpdateReport(ctx context.Context, id int64, action string) error {
client, err := u.reportRepo.NewClient(true, nil)
if err != nil {
return err
}

report, err := client.GetReportByID(ctx, id)
if err != nil {
return err
}

report.Action = action
if err := client.UpdateActionReport(ctx, &report); err != nil {
return err
}

return client.Commit()
}

0 comments on commit 3d4cbd0

Please sign in to comment.