diff --git a/kontor-api-go/cmd/kontor/main.go b/kontor-api-go/cmd/kontor/main.go index bc4b8f1..9f5f96e 100644 --- a/kontor-api-go/cmd/kontor/main.go +++ b/kontor-api-go/cmd/kontor/main.go @@ -23,13 +23,15 @@ func main() { // SigningKey: jwtware.SigningKey{Key: []byte("secret")}, // })) - // app.Use(logger.New()) + //app.Use(logger.New()) + app.Get("/health", handler.GetHealth) app.Post("/login", handler.Login) api := app.Group("/api", logger.New(), jwtware.New(jwtware.Config{ SigningKey: jwtware.SigningKey{Key: []byte("secret")}, })) + api.Use(logger.New()) handler.SetupComicRoutes(api) handler.SetupMediaRoutes(api) // Listen on port 8900 diff --git a/kontor-api-go/go.mod b/kontor-api-go/go.mod index 9ed8e1a..dca5b4e 100644 --- a/kontor-api-go/go.mod +++ b/kontor-api-go/go.mod @@ -7,6 +7,8 @@ require ( github.com/andybalholm/brotli v1.2.0 // indirect github.com/clipperhouse/stringish v0.1.1 // indirect github.com/clipperhouse/uax29/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fatih/color v1.18.0 // indirect github.com/gofiber/contrib/jwt v1.1.2 // indirect github.com/gofiber/fiber/v2 v2.52.10 // indirect github.com/golang-jwt/jwt/v5 v5.3.0 // indirect @@ -17,12 +19,15 @@ require ( github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.19 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect + github.com/stretchr/testify v1.11.1 // indirect github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect github.com/uptrace/bun v1.2.16 // indirect github.com/uptrace/bun/dialect/pgdialect v1.2.16 // indirect github.com/uptrace/bun/driver/pgdriver v1.2.16 // indirect + github.com/uptrace/bun/extra/bundebug v1.2.16 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.68.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect @@ -31,6 +36,7 @@ require ( go.opentelemetry.io/otel v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect golang.org/x/crypto v0.45.0 // indirect - golang.org/x/sys v0.38.0 // indirect + golang.org/x/sys v0.39.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect mellium.im/sasl v0.3.2 // indirect ) diff --git a/kontor-api-go/go.sum b/kontor-api-go/go.sum index ed39e3a..bc100d6 100644 --- a/kontor-api-go/go.sum +++ b/kontor-api-go/go.sum @@ -7,6 +7,10 @@ github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfa github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/gofiber/contrib/jwt v1.1.2 h1:GmWnOqT4A15EkA8IPXwSpvNUXZR4u5SMj+geBmyLAjs= github.com/gofiber/contrib/jwt v1.1.2/go.mod h1:CpIwrkUQ3Q6IP8y9n3f0wP9bOnSKx39EDp2fBVgMFVk= github.com/gofiber/fiber v1.14.6 h1:QRUPvPmr8ijQuGo1MgupHBn8E+wW0IKqiOvIZPtV70o= @@ -38,10 +42,14 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg= github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= github.com/uptrace/bun v1.2.16 h1:QlObi6ZIK5Ao7kAALnh91HWYNZUBbVwye52fmlQM9kc= @@ -50,6 +58,8 @@ github.com/uptrace/bun/dialect/pgdialect v1.2.16 h1:KFNZ0LxAyczKNfK/IJWMyaleO6eI github.com/uptrace/bun/dialect/pgdialect v1.2.16/go.mod h1:IJdMeV4sLfh0LDUZl7TIxLI0LipF1vwTK3hBC7p5qLo= github.com/uptrace/bun/driver/pgdriver v1.2.16 h1:b1kpXKUxtTSGYow5Vlsb+dKV3z0R7aSAJNfMfKp61ZU= github.com/uptrace/bun/driver/pgdriver v1.2.16/go.mod h1:H6lUZ9CBfp1X5Vq62YGSV7q96/v94ja9AYFjKvdoTk0= +github.com/uptrace/bun/extra/bundebug v1.2.16 h1:3OXAfHTU4ydu2+4j05oB1BxPx6+ypdWIVzTugl/7zl0= +github.com/uptrace/bun/extra/bundebug v1.2.16/go.mod h1:vk6R/1i67/S2RvUI5AH/m3P5e67mOkfDCmmCsAPUumo= 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/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA= @@ -78,6 +88,11 @@ golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= mellium.im/sasl v0.3.2 h1:PT6Xp7ccn9XaXAnJ03FcEjmAn7kK1x7aoXV6F+Vmrl0= mellium.im/sasl v0.3.2/go.mod h1:NKXDi1zkr+BlMHLQjY3ofYuU4KSPFxknb8mfEu6SveY= diff --git a/kontor-api-go/pkg/handler/comics.go b/kontor-api-go/pkg/handler/comics.go index 2694363..cc9c978 100644 --- a/kontor-api-go/pkg/handler/comics.go +++ b/kontor-api-go/pkg/handler/comics.go @@ -12,6 +12,8 @@ import ( func SetupComicRoutes(api fiber.Router) { comics := api.Group("/comics") comics.Get("/comics", GetAllComics) + comics.Get("/publishers", GetAllPublishers) + comics.Get("/comicworks", GetAllComicWorks) } func GetAllComics(c *fiber.Ctx) error { @@ -33,3 +35,43 @@ func GetAllComics(c *fiber.Ctx) error { return c.JSON(comics) } + +func GetAllPublishers(c *fiber.Ctx) error { + var publishers []schema.Publisher + var err error + var db *bun.DB + ctx := context.Background() + + db, err = schema.GetDatabase() + if err != nil { + log.Fatal(err) + } + + err = db.NewSelect().Model(&publishers).Relation("ParentPublisher").Scan(ctx) + if err != nil { + log.Fatal(err) + return fiber.NewError(fiber.StatusInternalServerError) + } + + return c.JSON(publishers) +} + +func GetAllComicWorks(c *fiber.Ctx) error { + var comic_works []schema.ComicWork + var err error + var db *bun.DB + ctx := context.Background() + + db, err = schema.GetDatabase() + if err != nil { + log.Fatal(err) + } + + err = db.NewSelect().Model(&comic_works).Relation("Comic").Relation("Artist").Relation("WorkType").Scan(ctx) + if err != nil { + log.Fatal(err) + return fiber.NewError(fiber.StatusInternalServerError) + } + + return c.JSON(comic_works) +} diff --git a/kontor-api-go/pkg/schema/comics.go b/kontor-api-go/pkg/schema/comics.go index e472686..06b1a7e 100644 --- a/kontor-api-go/pkg/schema/comics.go +++ b/kontor-api-go/pkg/schema/comics.go @@ -34,12 +34,107 @@ type Comic struct { Completed bool `bun:"completed"` WebLink string `bun:"weblink"` - PublisherID *string `bun:"publisher_id"` - Publisher *Publisher `bun:"rel:belongs-to,join:publisher_id=id"` - - // Issues []Issue `bun:"rel:has-many,join:id=comic_id"` - // StoryArcs []StoryArc `bun:"rel:has-many,join:id=comic_id"` - // TradePaperbacks []TradePaperback `bun:"rel:has-many,join:id=comic_id"` - // Volumes []Volume `bun:"rel:has-many,join:id=comic_id"` - // ComicWorks []ComicWork `bun:"rel:has-many,join:id=comic_id"` + PublisherID *string `bun:"publisher_id"` + Publisher *Publisher `bun:"rel:belongs-to,join:publisher_id=id"` + Issues []Issue `bun:"rel:has-many,join:id=comic_id"` + StoryArcs []StoryArc `bun:"rel:has-many,join:id=comic_id"` + TradePaperbacks []TradePaperback `bun:"rel:has-many,join:id=comic_id"` + Volumes []Volume `bun:"rel:has-many,join:id=comic_id"` + ComicWorks []ComicWork `bun:"rel:has-many,join:id=comic_id"` +} + +type Artist struct { + bun.BaseModel `bun:"table:artist"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + Name string `bun:"name,unique:title,notnull"` + WebLink string `bun:"weblink"` +} + +type Issue struct { + bun.BaseModel `bun:"table:issue"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + InStock bool `bun:"in_stock"` + IsRead bool `bun:"is_read"` + IssueNumber string `bu:"issue_number"` + Title string `bun:"title"` + PublishedOn time.Time `bun:"published_on"` + ComicID *string `bun:"comic_id"` + Comic *Comic `bun:"rel:belongs-to,join:comic_id=id"` + StoryArcID *string `bun:"story_arc_id"` + StoryArc *StoryArc `bun:"rel:belongs-to,join:story_arc_id=id"` + VolumeID *string `bun:"volume_id"` + Volume *Volume `bun:"rel:belongs-to,join:volume_id=id"` +} + +type StoryArc struct { + bun.BaseModel `bun:"table:story_arc"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + Name string `bun:"name,unique:name,notnull"` + ComicID *string `bun:"comic_id"` + Comic *Comic `bun:"rel:belongs-to,join:comic_id=id"` + VolumeID *string `bun:"volume_id"` + Volume *Volume `bun:"rel:belongs-to,join:volume_id=id"` +} + +type TradePaperback struct { + bun.BaseModel `bun:"table:trade_paperback"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + Name string `bun:"name,unique:name,notnull"` + IssueStart int `bun:"issue_start"` + IssueEnd int `bun:"issue_end"` + ComicID *string `bun:"comic_id"` + Comic *Comic `bun:"rel:belongs-to,join:comic_id=id"` +} + +type Volume struct { + bun.BaseModel `bun:"table:volume"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + Name string `bun:"name,unique:name,notnull"` + ComicID *string `bun:"comic_id"` + Comic *Comic `bun:"rel:belongs-to,join:comic_id=id"` +} + +type WorkType struct { + bun.BaseModel `bun:"table:worktype"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + Name string `bun:"name,unique:name,notnull"` +} + +type ComicWork struct { + bun.BaseModel `bun:"table:comic_work"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + ArtistID *string `bun:"artist_id"` + Artist *Artist `bun:"rel:belongs-to,join:artist_id=id"` + ComicID *string `bun:"comic_id"` + Comic *Comic `bun:"rel:belongs-to,join:comic_id=id"` + WorkTypeID *string `bun:"work_type_id"` + WorkType *WorkType `bun:"rel:belongs-to,join:work_type_id=id"` } diff --git a/kontor-api-go/pkg/schema/comics_test.go b/kontor-api-go/pkg/schema/comics_test.go new file mode 100644 index 0000000..ea2505e --- /dev/null +++ b/kontor-api-go/pkg/schema/comics_test.go @@ -0,0 +1,101 @@ +package schema + +import ( + "context" + "log" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "database/sql" + + "github.com/uptrace/bun" + "github.com/uptrace/bun/extra/bundebug" + + "github.com/uptrace/bun/dialect/pgdialect" + "github.com/uptrace/bun/driver/pgdriver" +) + +func GetTestDatabase() (*bun.DB, error) { + var err error + + dsn := "postgres://kontor:kontor@localhost:5432/kontor?sslmode=disable" + sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(dsn))) + + sqldb.SetMaxOpenConns(4) + sqldb.SetMaxIdleConns(4) + + DB := bun.NewDB(sqldb, pgdialect.New()) + DB.AddQueryHook(bundebug.NewQueryHook(bundebug.WithVerbose(true))) + + if err = DB.Ping(); err != nil { + return nil, err + } + + log.Println("Returned Database Connection") + return DB, nil +} + +func TestMain(m *testing.M) { + log.Println("Setup Test") + exitCode := m.Run() + os.Exit(exitCode) +} + +func TestSelectComics(t *testing.T) { + var comics []Comic + var err error + var db *bun.DB + ctx := context.Background() + + db, err = GetTestDatabase() + require.NoError(t, err) + + err = db.NewSelect().Model(&comics).Relation("Publisher").Scan(ctx) + if err != nil { + log.Fatal(err) + } + + require.NoError(t, err) + assert.Equal(t, 168, len(comics)) +} + +func TestSelectPublishers(t *testing.T) { + var publishers []Publisher + var err error + var db *bun.DB + ctx := context.Background() + + db, err = GetTestDatabase() + require.NoError(t, err) + + err = db.NewSelect().Model(&publishers).Relation("ParentPublisher").Scan(ctx) + if err != nil { + log.Fatal(err) + } + + require.NoError(t, err) + assert.Equal(t, 19, len(publishers)) +} + +func TestSelectWorkTypes(t *testing.T) { + var comic_works []ComicWork + var err error + var db *bun.DB + ctx := context.Background() + + db, err = GetTestDatabase() + if err != nil { + log.Fatal(err) + } + + err = db.NewSelect().Model(&comic_works).Relation("Comic").Relation("Artist").Relation("WorkType").Scan(ctx) + if err != nil { + log.Fatal(err) + } + + require.NoError(t, err) + assert.Equal(t, 59, len(comic_works)) +}