make get and post endpoints work, update enums to be strings, add log file output

This commit is contained in:
katefort 2025-04-23 15:34:19 -05:00
parent 79977f1a18
commit e762fc2abd
12 changed files with 94 additions and 72 deletions

View file

@ -1,6 +1,8 @@
package main package main
import "vegan-barcode/internal/application" import (
"vegan-barcode/internal/application"
)
func main() { func main() {
application.Start() application.Start()

View file

@ -3,20 +3,18 @@ package application
import ( import (
"vegan-barcode/internal/database" "vegan-barcode/internal/database"
"vegan-barcode/internal/utils" "vegan-barcode/internal/utils"
"github.com/sirupsen/logrus"
) )
type Application struct { type Application struct {
db database.Database db database.Database
log *logrus.Logger // TODO: possibly include logger?
} }
func Start() { func Start() {
application := Application{ application := Application{
db: database.InitializeDatabase(), db: database.InitializeDatabase(),
log: utils.InitializeLogger(),
} }
utils.InitializeLogger()
application.bindRoutes() application.bindRoutes()
} }

View file

@ -1,10 +1,12 @@
package application package application
import ( import (
"fmt"
"net/http" "net/http"
"vegan-barcode/internal/models" "vegan-barcode/internal/models"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
) )
func (a *Application) TestHandler(c *gin.Context) { func (a *Application) TestHandler(c *gin.Context) {
@ -17,7 +19,7 @@ func (a *Application) GetClaimsHandler(c *gin.Context) {
productClaims, err := a.GetClaims(system, barcode) productClaims, err := a.GetClaims(system, barcode)
if err != nil { if err != nil {
c.JSON(http.StatusInternalServerError, nil) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return return
} }
c.JSON(http.StatusOK, productClaims) c.JSON(http.StatusOK, productClaims)
@ -30,9 +32,10 @@ func (a *Application) PostClaimHandler(c *gin.Context) {
var requestBody models.UserClaimForm var requestBody models.UserClaimForm
err := c.BindJSON(&requestBody) err := c.BindJSON(&requestBody)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, nil) c.JSON(http.StatusBadRequest, fmt.Errorf("improperly formatted request: %w", err))
return return
} }
log.Debugf("requestbody: %v", requestBody)
claim, err := a.CreateClaim(system, barcode, requestBody) claim, err := a.CreateClaim(system, barcode, requestBody)
if err != nil { if err != nil {

View file

@ -1 +0,0 @@
package application

View file

@ -12,7 +12,7 @@ func (a *Application) GetClaims(system string, barcode string) (*models.ProductC
return nil, err return nil, err
} }
if product == nil { if product == nil {
return nil, errors.New("Product not found") return nil, errors.New("product not found")
} }
claims, err := a.db.FindClaimsByProductID(product.Id) claims, err := a.db.FindClaimsByProductID(product.Id)

View file

@ -1,13 +0,0 @@
package utils
import (
"github.com/sirupsen/logrus"
)
func InitializeLogger() *logrus.Logger {
log := logrus.New()
log.SetFormatter(&logrus.TextFormatter{
FullTimestamp: true, // Include the full timestamp (with date and time)
})
return log
}

View file

@ -2,6 +2,7 @@ package database
import ( import (
"context" "context"
"fmt"
"vegan-barcode/internal/models" "vegan-barcode/internal/models"
"github.com/vingarcia/ksql" "github.com/vingarcia/ksql"
@ -10,7 +11,7 @@ import (
func (d *Database) FindClaimsByProductID(product_id int) (claims []models.Claim, err error) { func (d *Database) FindClaimsByProductID(product_id int) (claims []models.Claim, err error) {
ctx := context.Background() ctx := context.Background()
err = d.db.Query(ctx, claims, ` err = d.db.Query(ctx, &claims, `
SELECT SELECT
cluster, cluster,
id, id,
@ -35,29 +36,30 @@ func (d *Database) FindClaimsByProductID(product_id int) (claims []models.Claim,
ROW_NUMBER() OVER (PARTITION BY category ORDER BY created_at DESC) AS rn ROW_NUMBER() OVER (PARTITION BY category ORDER BY created_at DESC) AS rn
FROM ( FROM (
( (
SELECT "user" as cluster, id, product_id, null as worker_type, evidence_type, evidence, unnest(claim) as category, true as polarity, created_at, created_by FROM user_claims SELECT 'user' as cluster, id, product_id, null as worker_type, evidence_type, evidence, unnest(claims) as category, true as polarity, created_at, created_by FROM user_claims
UNION ALL UNION ALL
SELECT "automated" as cluster, id, product_id, worker_type, null as evidence_type, evidence, unnest(claim) as category, true as polarity, created_at, null as created_by FROM automated_claims SELECT 'automated' as cluster, id, product_id, worker_type, null as evidence_type, evidence, unnest(claims) as category, true as polarity, created_at, null as created_by FROM automated_claims
) )
UNION ALL UNION ALL
( (
SELECT "user" as cluster, id, product_id, null as worker_type, evidence_type, evidence, unnest(counter_claim) as category, false as polarity, created_at, created_by FROM user_claims SELECT 'user' as cluster, id, product_id, null as worker_type, evidence_type, evidence, unnest(counterclaims) as category, false as polarity, created_at, created_by FROM user_claims
UNION ALL UNION ALL
SELECT "automated" as cluster, id, product_id, worker_type, null as evidence_type, evidence, unnest(counter_claim) as category, false as polarity, created_at, null as created_by FROM automated_claims SELECT 'automated' as cluster, id, product_id, worker_type, null as evidence_type, evidence, unnest(counterclaims) as category, false as polarity, created_at, null as created_by FROM automated_claims
) )
) ) AS combined_claims
WHERE product_id = $1 WHERE product_id = $1
) ) AS ranked_claims
WHERE rn = 1 WHERE rn = 1
ORDER BY created_at; ORDER BY created_at;
`, product_id) `, product_id)
if err != nil { if err != nil {
return claims, err return nil, fmt.Errorf("query failed: %w", err)
} }
return return
} }
// exists only for testing
func (database *Database) FindUserClaimById(claim_id int) (*UserClaim, error) { func (database *Database) FindUserClaimById(claim_id int) (*UserClaim, error) {
ctx := context.Background() ctx := context.Background()
@ -79,6 +81,7 @@ func (d *Database) CreateUserClaim(product_id int, form models.UserClaimForm) (*
Evidence: form.Evidence, Evidence: form.Evidence,
Claims: form.Claims, Claims: form.Claims,
Counterclaims: form.Counterclaims, Counterclaims: form.Counterclaims,
// TODO: Add created by
} }
err := d.db.Insert(ctx, UserClaimsTable, &uc) err := d.db.Insert(ctx, UserClaimsTable, &uc)

View file

@ -16,7 +16,7 @@ func TestCreateClaimByProductId(t *testing.T) {
Ingredients: "flour,egg,milk,water,salt,butter", Ingredients: "flour,egg,milk,water,salt,butter",
}, },
Claims: []models.ClaimType{ Claims: []models.ClaimType{
models.ContainsEggs, models.ContainsEgg,
models.ContainsMilk, models.ContainsMilk,
}, },
Counterclaims: []models.ClaimType{ Counterclaims: []models.ClaimType{

View file

@ -61,10 +61,10 @@ func (database *Database) createTables(ctx context.Context) {
CREATE TABLE IF NOT EXISTS user_claims ( CREATE TABLE IF NOT EXISTS user_claims (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
product_id INTEGER, product_id INTEGER,
evidence_type INTEGER, evidence_type TEXT,
evidence JSONB, evidence JSONB,
claims INTEGER[], claims TEXT[],
counterclaims INTEGER[], counterclaims TEXT[],
created_at TIMESTAMPTZ, created_at TIMESTAMPTZ,
created_by TEXT, created_by TEXT,
@ -80,10 +80,10 @@ func (database *Database) createTables(ctx context.Context) {
CREATE TABLE IF NOT EXISTS automated_claims ( CREATE TABLE IF NOT EXISTS automated_claims (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
product_id INTEGER, product_id INTEGER,
worker_type INTEGER, worker_type TEXT,
evidence JSONB, evidence JSONB,
claims INTEGER[], claims TEXT[],
counterclaims INTEGER[], counterclaims TEXT[],
created_at TIMESTAMPTZ, created_at TIMESTAMPTZ,
CONSTRAINT fk_product_id FOREIGN KEY(product_id) REFERENCES products(id) CONSTRAINT fk_product_id FOREIGN KEY(product_id) REFERENCES products(id)

View file

@ -2,8 +2,10 @@ package database
import ( import (
"context" "context"
"fmt"
"time" "time"
log "github.com/sirupsen/logrus"
"github.com/vingarcia/ksql" "github.com/vingarcia/ksql"
) )
@ -14,6 +16,8 @@ func (d *Database) FindProductByBarcode(system string, barcode string) (*Product
var product Product var product Product
err := d.db.QueryOne(ctx, &product, "FROM products WHERE system = $1 AND barcode = $2", system, barcode) err := d.db.QueryOne(ctx, &product, "FROM products WHERE system = $1 AND barcode = $2", system, barcode)
// No error because product may just not exist.
if err == ksql.ErrRecordNotFound { if err == ksql.ErrRecordNotFound {
return nil, nil return nil, nil
} }
@ -23,6 +27,7 @@ func (d *Database) FindProductByBarcode(system string, barcode string) (*Product
return &product, nil return &product, nil
} }
// Doesnt handle checking if product exists.
func (d *Database) CreateProduct(system string, barcode string) (*Product, error) { func (d *Database) CreateProduct(system string, barcode string) (*Product, error) {
ctx := context.Background() ctx := context.Background()
var product = Product{ var product = Product{
@ -31,8 +36,10 @@ func (d *Database) CreateProduct(system string, barcode string) (*Product, error
Created_at: time.Now(), Created_at: time.Now(),
} }
log.Debugf("made new product: %v", product)
if err := d.db.Insert(ctx, ProductsTable, &product); err != nil { if err := d.db.Insert(ctx, ProductsTable, &product); err != nil {
return nil, err return nil, fmt.Errorf("failed to insert new product: %w", err)
} }
return &product, nil return &product, nil

View file

@ -4,59 +4,60 @@ import (
"time" "time"
) )
type ClaimType int type ClaimType string
const ( const (
ContainsMeat ClaimType = iota ContainsMeat ClaimType = "meat"
ContainsFish ContainsFish ClaimType = "fish"
ContainsEggs ContainsEgg ClaimType = "egg"
ContainsMilk ContainsMilk ClaimType = "milk"
ContainsHoney ContainsHoney ClaimType = "honey"
ContainsWax ContainsWax ClaimType = "wax"
ContainsFur ContainsFur ClaimType = "fur"
ContainsLeather ContainsLeather ClaimType = "leather"
ContainsAnimalFibers ContainsAnimalFibers ClaimType = "animal_fibers"
ContainsWool ContainsWool ClaimType = "wool"
ContainsFeathers ContainsFeathers ClaimType = "feathers"
AnimalTesting AnimalTesting ClaimType = "animal_testing"
MonkeySlavery MonkeySlavery ClaimType = "monkey_slavery"
) )
type WorkerType int type WorkerType string
const ( const (
Barnivore WorkerType = iota Barnivore WorkerType = "barnivore"
) )
type EvidenceType int // Using a string here to make database modification easier.
type EvidenceType string
const ( const (
ManufacturerWebsite EvidenceType = iota ManufacturerWebsite EvidenceType = "manufacturer"
IngredientsList IngredientsList EvidenceType = "ingredients"
) )
type IngredientsListEvidence struct { type IngredientsListEvidence struct {
Ingredients string Ingredients string
} }
type ClusterType int type ClusterType string
const ( const (
User ClusterType = iota User ClusterType = "user"
Automated Automated ClusterType = "auto"
) )
// Generic claim type for combining both automated and user claims. // Generic claim type for combining both automated and user claims.
type Claim struct { type Claim struct {
Id int Id int `ksql:"id"`
Worker_type WorkerType Worker_type *WorkerType `ksql:"worker_type"`
Evidence_type EvidenceType Evidence_type EvidenceType `ksql:"evidence_type"`
Evidence struct{} Evidence struct{} `ksql:"evidence"`
Category ClaimType Category ClaimType `ksql:"category"`
Polarity bool Polarity bool `ksql:"polarity"`
Created_at time.Time Created_at time.Time `ksql:"created_at"`
Created_by string Created_by string `ksql:"created_by"`
Cluster ClusterType Cluster ClusterType `ksql:"cluster"`
} }
type ProductClaims struct { type ProductClaims struct {

22
internal/utils/logger.go Normal file
View file

@ -0,0 +1,22 @@
package utils
import (
"io"
"os"
log "github.com/sirupsen/logrus"
)
func InitializeLogger() {
file, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
log.Fatal(err)
}
log.SetOutput(io.MultiWriter(os.Stdout, file))
log.SetFormatter(&log.TextFormatter{
FullTimestamp: true,
})
log.SetLevel(log.DebugLevel)
}