restructure application, add get endpoint
This commit is contained in:
parent
51a34008f1
commit
4ebc663d3b
16 changed files with 266 additions and 165 deletions
|
@ -1,72 +0,0 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"vegan-barcode/internal/database"
|
||||
"vegan-barcode/internal/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/labstack/gommon/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/vingarcia/ksql"
|
||||
)
|
||||
|
||||
// TODO Figure out where this should be
|
||||
// This exists so that you don't have to individually pass around the logger and database.
|
||||
type ApiService struct {
|
||||
db *ksql.DB
|
||||
log *logrus.Logger
|
||||
}
|
||||
|
||||
var s *ApiService
|
||||
|
||||
// TODO: Service should get moved somewhere else. Not sure on naming.
|
||||
func BindRoutes() {
|
||||
|
||||
s = &ApiService{db: database.InitializeDatabase(), log: utils.InitializeLogger()}
|
||||
// s = &ApiService{db: &ksql.DB{}, log: utils.InitializeLogger()}
|
||||
router := gin.Default()
|
||||
router.GET("/test/:id", s.runTest)
|
||||
router.GET("/", s.runTest)
|
||||
|
||||
// // Search for item info
|
||||
// router.GET("/claims/{barcode}", runTest)
|
||||
|
||||
// // by user ID or worker
|
||||
// router.DELETE("/claims/{barcode}", deleteClaims)
|
||||
|
||||
// // Update item info
|
||||
// router.GET("/test", runTest)
|
||||
// // Add new item info
|
||||
|
||||
router.Run("localhost:8080")
|
||||
}
|
||||
|
||||
func (s *ApiService) runTest(c *gin.Context) {
|
||||
queryParam := c.Param("id")
|
||||
log.Debug("Test was successful.")
|
||||
|
||||
err := s.db.Insert(c, database.ProductsTable, &database.Product{System: "upc", Barcode: "fubar", Created_at: time.Now()})
|
||||
if err != nil {
|
||||
// TODO: Figure out correct status code.
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to insert item to product table", "error": err.Error()})
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Hello World!", "query_param": queryParam})
|
||||
}
|
||||
|
||||
// func claimsByBarcode(c *gin.Context) {
|
||||
// system := c.DefaultQuery("system", "upc")
|
||||
// barcode := c.Query("barcode")
|
||||
|
||||
// }
|
||||
|
||||
// // deleteClaims will delete
|
||||
// func deleteClaims(c *gin.Context) {
|
||||
// userID := c.Param("user")
|
||||
// workerID := c.Param("worker")
|
||||
// // TODO query database
|
||||
// database.DB.Query()
|
||||
// c.JSON(http.StatusOK, gin.H{"message": "Hello World!"})
|
||||
// }
|
22
internal/application/application.go
Normal file
22
internal/application/application.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package application
|
||||
|
||||
import (
|
||||
"vegan-barcode/internal/database"
|
||||
"vegan-barcode/internal/utils"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Application struct {
|
||||
db database.Database
|
||||
log *logrus.Logger
|
||||
}
|
||||
|
||||
func Start() {
|
||||
application := Application{
|
||||
db: database.InitializeDatabase(),
|
||||
log: utils.InitializeLogger(),
|
||||
}
|
||||
|
||||
application.bindRoutes()
|
||||
}
|
19
internal/application/handlers.go
Normal file
19
internal/application/handlers.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package application
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (a *Application) GetClaimsHandler(c *gin.Context) {
|
||||
system := c.DefaultQuery("system", "upc")
|
||||
barcode := c.Query("barcode")
|
||||
|
||||
productClaims, err := a.GetClaims(system, barcode)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, productClaims)
|
||||
}
|
17
internal/application/routes.go
Normal file
17
internal/application/routes.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package application
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (application *Application) bindRoutes() {
|
||||
|
||||
router := gin.Default()
|
||||
|
||||
router.Group("/claims")
|
||||
{
|
||||
router.GET("/:barcode", application.GetClaimsHandler)
|
||||
}
|
||||
|
||||
router.Run("localhost:8080")
|
||||
}
|
19
internal/application/services.go
Normal file
19
internal/application/services.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package application
|
||||
|
||||
import (
|
||||
"vegan-barcode/internal/models"
|
||||
)
|
||||
|
||||
func (a *Application) GetClaims(system string, barcode string) (*models.ProductClaims, error) {
|
||||
id, err := a.db.FindProductIDByBarcode(system, barcode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
claims, err := a.db.FindClaimsByProductID(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &models.ProductClaims{Id: id, Claims: claims}, nil
|
||||
}
|
54
internal/database/claims.go
Normal file
54
internal/database/claims.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"vegan-barcode/internal/models"
|
||||
)
|
||||
|
||||
func (database *Database) FindClaimsByProductID(product_id int) (claims []models.Claim, err error) {
|
||||
ctx := context.Background()
|
||||
err = database.db.Query(ctx, claims, `SELECT
|
||||
cluster,
|
||||
id,
|
||||
worker_type,
|
||||
evidence_type,
|
||||
evidence,
|
||||
category,
|
||||
polarity,
|
||||
created_at,
|
||||
created_by
|
||||
FROM (
|
||||
SELECT
|
||||
cluster,
|
||||
id,
|
||||
worker_type,
|
||||
evidence_type,
|
||||
evidence,
|
||||
category,
|
||||
polarity,
|
||||
created_at,
|
||||
created_by,
|
||||
ROW_NUMBER() OVER (PARTITION BY category ORDER BY created_at DESC) AS rn
|
||||
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
|
||||
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
|
||||
)
|
||||
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
|
||||
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
|
||||
)
|
||||
)
|
||||
WHERE product_id = ?
|
||||
)
|
||||
WHERE rn = 1
|
||||
ORDER BY created_at;
|
||||
`, product_id)
|
||||
if err != nil {
|
||||
return claims, err
|
||||
}
|
||||
return
|
||||
}
|
|
@ -16,8 +16,13 @@ var UserClaimsTable = ksql.NewTable("user_claims", "id")
|
|||
|
||||
var AutomatedClaimsTable = ksql.NewTable("automated_claims", "id")
|
||||
|
||||
// Struct used in dependency injection can be replaced with a mock for testing.
|
||||
type Database struct {
|
||||
db *ksql.DB
|
||||
}
|
||||
|
||||
// initializeDatabase creates the database and calls createTables.
|
||||
func InitializeDatabase() *ksql.DB {
|
||||
func InitializeDatabase() Database {
|
||||
ctx := context.Background()
|
||||
|
||||
// urlExample := "postgres://username:password@localhost:5432/database_name"
|
||||
|
@ -30,14 +35,18 @@ func InitializeDatabase() *ksql.DB {
|
|||
if err != nil {
|
||||
log.Fatalf("Unable to connect to database: %v\n", err)
|
||||
}
|
||||
createTables(ctx, db)
|
||||
return &db
|
||||
|
||||
database := Database{
|
||||
db: &db,
|
||||
}
|
||||
|
||||
database.createTables(ctx)
|
||||
return database
|
||||
}
|
||||
|
||||
// createTables adds the product, automated_claims, and user_claims tables to the initialized database.
|
||||
func createTables(ctx context.Context, db ksql.DB) {
|
||||
|
||||
_, err := db.Exec(ctx, `
|
||||
func (database *Database) createTables(ctx context.Context) {
|
||||
_, err := database.db.Exec(ctx, `
|
||||
CREATE TABLE IF NOT EXISTS products (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
system TEXT,
|
||||
|
@ -48,7 +57,7 @@ func createTables(ctx context.Context, db ksql.DB) {
|
|||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
_, err = db.Exec(ctx, `
|
||||
_, err = database.db.Exec(ctx, `
|
||||
CREATE TABLE IF NOT EXISTS user_claims (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
product_id INTEGER,
|
||||
|
@ -67,7 +76,7 @@ func createTables(ctx context.Context, db ksql.DB) {
|
|||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
_, err = db.Exec(ctx, `
|
||||
_, err = database.db.Exec(ctx, `
|
||||
CREATE TABLE IF NOT EXISTS automated_claims (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
product_id INTEGER,
|
||||
|
|
|
@ -2,6 +2,7 @@ package database
|
|||
|
||||
import (
|
||||
"time"
|
||||
"vegan-barcode/internal/models"
|
||||
)
|
||||
|
||||
type Product struct {
|
||||
|
@ -11,54 +12,23 @@ type Product struct {
|
|||
Created_at time.Time `ksql:"created_at,timeNowUTC"`
|
||||
}
|
||||
|
||||
type WorkerType int
|
||||
|
||||
const (
|
||||
Barnivore WorkerType = iota
|
||||
)
|
||||
|
||||
type EvidenceType int
|
||||
|
||||
const (
|
||||
ManufactureWebsite EvidenceType = iota
|
||||
IngredientsList
|
||||
)
|
||||
|
||||
type Claim int
|
||||
|
||||
const (
|
||||
ContainsMeat Claim = iota
|
||||
ContainsFish
|
||||
ContainsEggs
|
||||
ContainsMilk
|
||||
ContainsHoney
|
||||
ContainsWax
|
||||
ContainsFur
|
||||
ContainsLeather
|
||||
ContainsAnimalFibers
|
||||
ContainsWool
|
||||
ContainsFeathers
|
||||
AnimalTesting
|
||||
MonkeySlavery
|
||||
)
|
||||
|
||||
type AutomatedClaim struct {
|
||||
id int `ksql:"id"`
|
||||
product_id int `ksql:"product_id"`
|
||||
worker_type WorkerType `ksql:"worker_type"`
|
||||
evidence struct{} `ksql:"evidence,json"`
|
||||
claim Claim `ksql:"claim"`
|
||||
counter_claim Claim `ksql:"counter_claim"`
|
||||
created_at time.Time `ksql:"created_at,timeNowUTC"`
|
||||
id int `ksql:"id"`
|
||||
product_id int `ksql:"product_id"`
|
||||
worker_type models.WorkerType `ksql:"worker_type"`
|
||||
evidence struct{} `ksql:"evidence,json"`
|
||||
claim models.ClaimType `ksql:"claim"`
|
||||
counter_claim models.ClaimType `ksql:"counter_claim"`
|
||||
created_at time.Time `ksql:"created_at,timeNowUTC"`
|
||||
}
|
||||
|
||||
type UserClaim struct {
|
||||
id int `ksql:"id"`
|
||||
product_id int `ksql:"product_id"`
|
||||
evidence_type EvidenceType `ksql:"evidence_type"`
|
||||
evidence struct{} `ksql:"evidence,json"`
|
||||
claim Claim `ksql:"claim"`
|
||||
counter_claim Claim `ksql:"counter_claim"`
|
||||
created_at time.Time `ksql:"created_at,timeNowUTC"`
|
||||
created_by string `ksql:"created_by"`
|
||||
id int `ksql:"id"`
|
||||
product_id int `ksql:"product_id"`
|
||||
evidence_type models.EvidenceType `ksql:"evidence_type"`
|
||||
evidence struct{} `ksql:"evidence,json"`
|
||||
claim models.ClaimType `ksql:"claim"`
|
||||
counter_claim models.ClaimType `ksql:"counter_claim"`
|
||||
created_at time.Time `ksql:"created_at,timeNowUTC"`
|
||||
created_by string `ksql:"created_by"`
|
||||
}
|
||||
|
|
14
internal/database/products.go
Normal file
14
internal/database/products.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
package database
|
||||
|
||||
import "context"
|
||||
|
||||
func (d *Database) FindProductIDByBarcode(system string, barcode string) (id int, err error) {
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
err = d.db.QueryOne(ctx, &id, "SELECT id FROM products WHERE system = ? AND barcode = ?", system, barcode)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return
|
||||
}
|
61
internal/models/models.go
Normal file
61
internal/models/models.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type ClaimType int
|
||||
|
||||
const (
|
||||
ContainsMeat ClaimType = iota
|
||||
ContainsFish
|
||||
ContainsEggs
|
||||
ContainsMilk
|
||||
ContainsHoney
|
||||
ContainsWax
|
||||
ContainsFur
|
||||
ContainsLeather
|
||||
ContainsAnimalFibers
|
||||
ContainsWool
|
||||
ContainsFeathers
|
||||
AnimalTesting
|
||||
MonkeySlavery
|
||||
)
|
||||
|
||||
type WorkerType int
|
||||
|
||||
const (
|
||||
Barnivore WorkerType = iota
|
||||
)
|
||||
|
||||
type EvidenceType int
|
||||
|
||||
const (
|
||||
ManufactureWebsite EvidenceType = iota
|
||||
IngredientsList
|
||||
)
|
||||
|
||||
type ClusterType int
|
||||
|
||||
const (
|
||||
User ClusterType = iota
|
||||
Automated
|
||||
)
|
||||
|
||||
// Generic claim type for combining both automated and user claims.
|
||||
type Claim struct {
|
||||
Id int
|
||||
Worker_type WorkerType
|
||||
Evidence_type EvidenceType
|
||||
Evidence struct{}
|
||||
Category ClaimType
|
||||
Polarity bool
|
||||
Created_at time.Time
|
||||
Created_by string
|
||||
Cluster ClusterType
|
||||
}
|
||||
|
||||
type ProductClaims struct {
|
||||
Id int
|
||||
Claims []Claim
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
package services
|
Loading…
Add table
Add a link
Reference in a new issue