tb/main #1
33
README.md
33
README.md
|
@ -3,6 +3,12 @@
|
||||||
The goal of this project is to be a crowd sourced resource to find out if a product is vegan
|
The goal of this project is to be a crowd sourced resource to find out if a product is vegan
|
||||||
|
|
||||||
|
|
||||||
|
# Project organization
|
||||||
|
|
||||||
|
- handlers: gets query parameters to call service and responds with service output
|
||||||
|
- services: takes parameters and performs business logic, calls functions to query database
|
||||||
|
- user_claims.go in database package: queries database
|
||||||
|
|
||||||
## Task list
|
## Task list
|
||||||
- figure out how we want to handle database migrations
|
- figure out how we want to handle database migrations
|
||||||
- database object models should be separated out from database migration function
|
- database object models should be separated out from database migration function
|
||||||
|
@ -16,7 +22,19 @@ The goal of this project is to be a crowd sourced resource to find out if a prod
|
||||||
- moderation tooling?
|
- moderation tooling?
|
||||||
|
|
||||||
|
|
||||||
Troubleshooting:
|
|
||||||
|
## How to start database
|
||||||
|
|
||||||
|
- Run `brew services start postgresql`
|
||||||
|
- Create database
|
||||||
|
- Setting environment variables:
|
||||||
|
- PGHOST=localhost
|
||||||
|
- DB_NAME=veganDB (Can be arbitrary)
|
||||||
|
- Run `createdb veganDB`
|
||||||
|
(To delete in future just run dropdb <name>)
|
||||||
|
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
Error:
|
Error:
|
||||||
`2025/04/17 16:21:21 ERROR: relation "idx_user_claims_product_id" already exists (SQLSTATE 42P07)`
|
`2025/04/17 16:21:21 ERROR: relation "idx_user_claims_product_id" already exists (SQLSTATE 42P07)`
|
||||||
|
@ -29,16 +47,3 @@ Error:
|
||||||
Explanation: Postgres automatically tryes to connect to a database with the same name as the user. Specify user and database:
|
Explanation: Postgres automatically tryes to connect to a database with the same name as the user. Specify user and database:
|
||||||
`psql -U username databaseName `
|
`psql -U username databaseName `
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## How to start database
|
|
||||||
|
|
||||||
- Run `brew services start postgresql`
|
|
||||||
- Create database
|
|
||||||
- Setting environment variables:
|
|
||||||
- PGHOST=localhost
|
|
||||||
- DB_NAME=veganDB (Can be arbitrary)
|
|
||||||
- Run `createdb veganDB`
|
|
||||||
(To delete in future just run dropdb <name>)
|
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import "vegan-barcode/internal/api"
|
||||||
"vegan-barcode/internal/api"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
api.BindRoutes()
|
api.Start()
|
||||||
}
|
}
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -4,7 +4,6 @@ go 1.24.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gin-gonic/gin v1.10.0
|
github.com/gin-gonic/gin v1.10.0
|
||||||
github.com/labstack/gommon v0.4.2
|
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,7 +31,6 @@ require (
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
@ -41,8 +39,6 @@ require (
|
||||||
github.com/stretchr/testify v1.9.0 // indirect
|
github.com/stretchr/testify v1.9.0 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
|
||||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
|
||||||
golang.org/x/arch v0.8.0 // indirect
|
golang.org/x/arch v0.8.0 // indirect
|
||||||
golang.org/x/net v0.25.0 // indirect
|
golang.org/x/net v0.25.0 // indirect
|
||||||
golang.org/x/sys v0.31.0 // indirect
|
golang.org/x/sys v0.31.0 // indirect
|
||||||
|
|
10
go.sum
10
go.sum
|
@ -154,8 +154,6 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
|
||||||
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v0.0.0-20180327071824-d34b9ff171c2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
@ -166,12 +164,9 @@ github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
|
||||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
|
||||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||||
|
@ -245,10 +240,6 @@ github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2
|
||||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
|
||||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
|
||||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
|
||||||
github.com/vingarcia/ksql v1.12.3 h1:1LVRGW39XPaYltPHNQsvHms+bWHp8e99sxQx+aEXDMQ=
|
github.com/vingarcia/ksql v1.12.3 h1:1LVRGW39XPaYltPHNQsvHms+bWHp8e99sxQx+aEXDMQ=
|
||||||
github.com/vingarcia/ksql v1.12.3/go.mod h1:DHp/nhVu1nHpBBXH/FRw6JLgIcvcM3+uo2+PfUNdo0g=
|
github.com/vingarcia/ksql v1.12.3/go.mod h1:DHp/nhVu1nHpBBXH/FRw6JLgIcvcM3+uo2+PfUNdo0g=
|
||||||
github.com/vingarcia/ksql/adapters/kpgx v1.12.3 h1:vzH0vMw1NTCang2DcZI9sV975BFUg5ONQjEZ27tuCng=
|
github.com/vingarcia/ksql/adapters/kpgx v1.12.3 h1:vzH0vMw1NTCang2DcZI9sV975BFUg5ONQjEZ27tuCng=
|
||||||
|
@ -351,7 +342,6 @@ golang.org/x/sys v0.0.0-20220405210540-1e041c57c461/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
|
|
@ -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")
|
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.
|
// initializeDatabase creates the database and calls createTables.
|
||||||
func InitializeDatabase() *ksql.DB {
|
func InitializeDatabase() Database {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
// urlExample := "postgres://username:password@localhost:5432/database_name"
|
// urlExample := "postgres://username:password@localhost:5432/database_name"
|
||||||
|
@ -30,14 +35,18 @@ func InitializeDatabase() *ksql.DB {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Unable to connect to database: %v\n", err)
|
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.
|
// createTables adds the product, automated_claims, and user_claims tables to the initialized database.
|
||||||
func createTables(ctx context.Context, db ksql.DB) {
|
func (database *Database) createTables(ctx context.Context) {
|
||||||
|
_, err := database.db.Exec(ctx, `
|
||||||
_, err := db.Exec(ctx, `
|
|
||||||
CREATE TABLE IF NOT EXISTS products (
|
CREATE TABLE IF NOT EXISTS products (
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
system TEXT,
|
system TEXT,
|
||||||
|
@ -48,7 +57,7 @@ func createTables(ctx context.Context, db ksql.DB) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = db.Exec(ctx, `
|
_, err = database.db.Exec(ctx, `
|
||||||
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,
|
||||||
|
@ -67,7 +76,7 @@ func createTables(ctx context.Context, db ksql.DB) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = db.Exec(ctx, `
|
_, err = database.db.Exec(ctx, `
|
||||||
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,
|
||||||
|
|
|
@ -2,6 +2,7 @@ package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
"vegan-barcode/internal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Product struct {
|
type Product struct {
|
||||||
|
@ -11,54 +12,23 @@ type Product struct {
|
||||||
Created_at time.Time `ksql:"created_at,timeNowUTC"`
|
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 {
|
type AutomatedClaim struct {
|
||||||
id int `ksql:"id"`
|
id int `ksql:"id"`
|
||||||
product_id int `ksql:"product_id"`
|
product_id int `ksql:"product_id"`
|
||||||
worker_type WorkerType `ksql:"worker_type"`
|
worker_type models.WorkerType `ksql:"worker_type"`
|
||||||
evidence struct{} `ksql:"evidence,json"`
|
evidence struct{} `ksql:"evidence,json"`
|
||||||
claim Claim `ksql:"claim"`
|
claim models.ClaimType `ksql:"claim"`
|
||||||
counter_claim Claim `ksql:"counter_claim"`
|
counter_claim models.ClaimType `ksql:"counter_claim"`
|
||||||
created_at time.Time `ksql:"created_at,timeNowUTC"`
|
created_at time.Time `ksql:"created_at,timeNowUTC"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserClaim struct {
|
type UserClaim struct {
|
||||||
id int `ksql:"id"`
|
id int `ksql:"id"`
|
||||||
product_id int `ksql:"product_id"`
|
product_id int `ksql:"product_id"`
|
||||||
evidence_type EvidenceType `ksql:"evidence_type"`
|
evidence_type models.EvidenceType `ksql:"evidence_type"`
|
||||||
evidence struct{} `ksql:"evidence,json"`
|
evidence struct{} `ksql:"evidence,json"`
|
||||||
claim Claim `ksql:"claim"`
|
claim models.ClaimType `ksql:"claim"`
|
||||||
counter_claim Claim `ksql:"counter_claim"`
|
counter_claim models.ClaimType `ksql:"counter_claim"`
|
||||||
created_at time.Time `ksql:"created_at,timeNowUTC"`
|
created_at time.Time `ksql:"created_at,timeNowUTC"`
|
||||||
created_by string `ksql:"created_by"`
|
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
|
|
|
@ -1,7 +1,7 @@
|
||||||
SELECT
|
SELECT
|
||||||
cluster,
|
cluster,
|
||||||
id,
|
id,
|
||||||
worker,
|
worker_type,
|
||||||
evidence_type,
|
evidence_type,
|
||||||
evidence,
|
evidence,
|
||||||
category,
|
category,
|
||||||
|
@ -12,7 +12,7 @@ FROM (
|
||||||
SELECT
|
SELECT
|
||||||
cluster,
|
cluster,
|
||||||
id,
|
id,
|
||||||
worker,
|
worker_type,
|
||||||
evidence_type,
|
evidence_type,
|
||||||
evidence,
|
evidence,
|
||||||
category,
|
category,
|
||||||
|
@ -22,15 +22,15 @@ FROM (
|
||||||
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, 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(claim) as category, true as polarity, created_at, created_by FROM user_claims
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT "automated" as cluster, id, product_id, worker, 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(claim) 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, 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(counter_claim) as category, false as polarity, created_at, created_by FROM user_claims
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT "automated" as cluster, id, product_id, worker, 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(counter_claim) as category, false as polarity, created_at, null as created_by FROM automated_claims
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
WHERE product_id = ???
|
WHERE product_id = ???
|
||||||
|
|
Loading…
Reference in a new issue