[WIP] mongodb service

This commit is contained in:
Alexander Kiryukhin 2022-02-08 02:14:57 +03:00
parent 4966c8fbcc
commit b2ea87f9ba
No known key found for this signature in database
GPG key ID: 6DF7A2910D0699E9
14 changed files with 380 additions and 32 deletions

58
example/cmd/app/main.go Normal file
View file

@ -0,0 +1,58 @@
package main
import (
"context"
"flag"
"log"
"net/http"
"os"
"os/signal"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"github.com/neonxp/geezer"
"github.com/neonxp/geezer/services/mongodb"
"github.com/neonxp/geezer/example/services/hello"
)
var (
listen string
mongoURI string
mongoDB string
)
func main() {
flag.StringVar(&listen, "listen", ":3000", "Host and port to listen (ex: '0.0.0.0:3000')")
flag.StringVar(&mongoURI, "mongo", "mongodb://localhost:27017/", "MongoDB connection uri (ex: 'mongodb://user:pass@sample.host:27017/')")
flag.StringVar(&mongoDB, "mongo_db", "geezer", "Database name")
flag.Parse()
ctx, cancel := signal.NotifyContext(context.Background(), os.Kill, os.Interrupt)
defer cancel()
// MongoDB connection
client, err := mongo.Connect(context.Background(), options.Client().ApplyURI(mongoURI))
if err != nil {
log.Fatalln(err)
}
defer client.Disconnect(context.Background())
db := client.Database(mongoDB)
app := geezer.NewHttpKernel()
hello.RegisterHooks(app) // Register hooks
_ = app.Register(hello.ServiceName, &hello.Service{}) // Register service as external handler
_ = app.Register("test", mongodb.New[Product](db.Collection("test"))) // Register mongodb crud service
log.Printf("Started on %s\n", listen)
srv := http.Server{Addr: listen, Handler: app}
go func() {
<-ctx.Done()
srv.Close()
}()
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalln(err)
}
}

View file

@ -0,0 +1,11 @@
package main
import "go.mongodb.org/mongo-driver/bson/primitive"
type Product struct {
ID primitive.ObjectID `json:"-" bson:"_id,omitempty"`
ProductID int `json:"productId"`
ProductName string `json:"productName"`
Price int `json:"price"`
Tags []string `json:"tags"`
}

View file

@ -0,0 +1,18 @@
package hello
import (
"log"
"github.com/neonxp/geezer"
)
func RegisterHooks(app geezer.AppKernel) {
app.Hook(ServiceName, geezer.HookBefore, geezer.HookFind, func(ctx *geezer.HookContext) error {
log.Printf("Hook before find")
return nil
})
app.Hook(ServiceName, geezer.HookAfter, geezer.HookFind, func(ctx *geezer.HookContext) error {
log.Printf("Hook after find")
return nil
})
}

View file

@ -0,0 +1,56 @@
package hello
import (
"context"
"log"
"github.com/neonxp/geezer"
"github.com/neonxp/geezer/render"
)
const ServiceName = "hello"
type Service struct {
}
func (s Service) Find(ctx context.Context, params geezer.Params) (render.Renderer, error) {
//TODO implement me
log.Printf("Find params=%+v", params)
return render.Text("text/html", "Hello, world!"), nil
}
func (s Service) Get(ctx context.Context, id string, params geezer.Params) (render.Renderer, error) {
//TODO implement me
log.Printf("Get id=%s params=%+v", id, params)
return nil, geezer.ErrMethodNotFound
}
func (s Service) Create(ctx context.Context, data geezer.Data, params geezer.Params) (render.Renderer, error) {
//TODO implement me
log.Printf("Create data=%s params=%+v", data, params)
return nil, geezer.ErrMethodNotFound
}
func (s Service) Update(ctx context.Context, id string, data geezer.Data, params geezer.Params) (render.Renderer, error) {
//TODO implement me
log.Printf("Update id=%s data=%s params=%+v", id, data, params)
return nil, geezer.ErrMethodNotFound
}
func (s Service) Patch(ctx context.Context, id string, data geezer.Data, params geezer.Params) (render.Renderer, error) {
//TODO implement me
log.Printf("Patch id=%s data=%s params=%+v", id, data, params)
return nil, geezer.ErrMethodNotFound
}
func (s Service) Remove(ctx context.Context, id string, params geezer.Params) error {
//TODO implement me
log.Printf("Remove id=%s params=%+v", id, params)
return geezer.ErrMethodNotFound
}
func (s Service) Setup(app geezer.AppKernel, path string) error {
//TODO implement me
log.Printf("Setup path=%s", path)
return nil
}

19
example/test.http Normal file
View file

@ -0,0 +1,19 @@
POST http://localhost:3000/test
Content-Type: application/json
{
"productId": 123,
"productName": "test",
"price": 123,
"tags": [
"one", "two", "three"
]
}
###
GET http://localhost:3000/test/62017d3fbfd5a64b46077ece
###
GET http://localhost:3000/test?price=123

21
go.mod
View file

@ -1,3 +1,24 @@
module github.com/neonxp/geezer
go 1.18
require (
github.com/xeipuuv/gojsonschema v1.2.0
go.mongodb.org/mongo-driver v1.8.3
)
require (
github.com/go-stack/stack v1.8.0 // indirect
github.com/golang/snappy v0.0.1 // indirect
github.com/klauspost/compress v1.13.6 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.0.2 // indirect
github.com/xdg-go/stringprep v1.0.2 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f // indirect
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
golang.org/x/text v0.3.5 // indirect
)

63
go.sum Normal file
View file

@ -0,0 +1,63 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w=
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc=
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
go.mongodb.org/mongo-driver v1.8.3 h1:TDKlTkGDKm9kkJVUOAXDK5/fkqKHJVwYQSpoRfB43R4=
go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f h1:aZp0e2vLN4MToVqnjNEYEtrEA8RH8U8FN1CU7JgqsPU=
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -33,7 +33,7 @@ var hookTypeFromMethod = map[Method]HookType{
}
type HookContext struct {
App Kernel
App AppKernel
Path []string
Method Method
Type HookLifecycle

View file

@ -8,12 +8,12 @@ import (
)
type HttpKernel struct {
Kernel
AppKernel
}
func NewHttpKernel() *HttpKernel {
return &HttpKernel{
Kernel: newKernel(),
AppKernel: newKernel(),
}
}
@ -57,7 +57,6 @@ func (s *HttpKernel) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
params := Params{
Ctx: ctx,
Path: parts,
Query: Values(u.Query()),
Headers: Values(r.Header),
@ -72,7 +71,7 @@ func (s *HttpKernel) ServeHTTP(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
data := Data(b)
result, err := s.Call(method, name, id, data, params)
result, err := s.Call(ctx, method, name, id, data, params)
if err != nil {
if err == ErrMethodNotFound {
w.WriteHeader(http.StatusNotFound)

View file

@ -1,6 +1,7 @@
package geezer
import (
"context"
"errors"
"strings"
@ -12,19 +13,19 @@ var (
ErrMethodNotFound = errors.New("method not found")
)
type defaultKernel struct {
type kernel struct {
routes map[string]Service
hooks map[string]map[HookLifecycle]map[HookType][]Hook
}
func newKernel() *defaultKernel {
return &defaultKernel{
func newKernel() *kernel {
return &kernel{
routes: map[string]Service{},
hooks: map[string]map[HookLifecycle]map[HookType][]Hook{},
}
}
func (s *defaultKernel) Register(name string, service Service) error {
func (s *kernel) Register(name string, service Service) error {
name = strings.ToLower(name)
s.routes[name] = service
if _, exist := s.hooks[name]; !exist {
@ -36,7 +37,7 @@ func (s *defaultKernel) Register(name string, service Service) error {
return nil
}
func (s *defaultKernel) Hook(service string, lifecycle HookLifecycle, hookType HookType, hook Hook) {
func (s *kernel) Hook(service string, lifecycle HookLifecycle, hookType HookType, hook Hook) {
service = strings.ToLower(service)
if _, exist := s.hooks[service]; !exist {
s.hooks[service] = map[HookLifecycle]map[HookType][]Hook{}
@ -50,14 +51,14 @@ func (s *defaultKernel) Hook(service string, lifecycle HookLifecycle, hookType H
s.hooks[service][lifecycle][hookType] = append(s.hooks[service][lifecycle][hookType], hook)
}
func (s *defaultKernel) Service(name string) Service {
func (s *kernel) Service(name string) Service {
if service, exist := s.routes[name]; exist {
return service
}
return nil
}
func (s *defaultKernel) Call(method Method, name, id string, data Data, params Params) (render.Renderer, error) {
func (s *kernel) Call(ctx context.Context, method Method, name, id string, data Data, params Params) (render.Renderer, error) {
name = strings.ToLower(name)
service := s.Service(name)
if service == nil {
@ -70,24 +71,24 @@ func (s *defaultKernel) Call(method Method, name, id string, data Data, params P
}
switch hookCtx.Method {
case MethodFind:
result, err = service.Find(hookCtx.Params)
result, err = service.Find(ctx, hookCtx.Params)
case MethodGet:
result, err = service.Get(hookCtx.ID, hookCtx.Params)
result, err = service.Get(ctx, hookCtx.ID, hookCtx.Params)
case MethodCreate:
result, err = service.Create(hookCtx.Data, hookCtx.Params)
result, err = service.Create(ctx, hookCtx.Data, hookCtx.Params)
case MethodUpdate:
result, err = service.Update(hookCtx.ID, hookCtx.Data, hookCtx.Params)
result, err = service.Update(ctx, hookCtx.ID, hookCtx.Data, hookCtx.Params)
case MethodPatch:
result, err = service.Patch(hookCtx.ID, hookCtx.Data, hookCtx.Params)
result, err = service.Patch(ctx, hookCtx.ID, hookCtx.Data, hookCtx.Params)
case MethodRemove:
err = service.Remove(hookCtx.ID, hookCtx.Params)
err = service.Remove(ctx, hookCtx.ID, hookCtx.Params)
default:
return nil, ErrMethodNotFound
}
return s.callAfterHooks(method, name, hookCtx, result, err)
}
func (s *defaultKernel) callBeforeHooks(method Method, name string, id string, data Data, params Params) (*HookContext, render.Renderer, error) {
func (s *kernel) callBeforeHooks(method Method, name string, id string, data Data, params Params) (*HookContext, render.Renderer, error) {
var beforeHooks []Hook
if hooks, ok := s.hooks[name][HookBefore]; ok {
if allHooks, ok := hooks[HookAll]; ok {
@ -117,7 +118,7 @@ func (s *defaultKernel) callBeforeHooks(method Method, name string, id string, d
return hookCtx, nil, nil
}
func (s *defaultKernel) callAfterHooks(method Method, name string, hookCtx *HookContext, result render.Renderer, err error) (render.Renderer, error) {
func (s *kernel) callAfterHooks(method Method, name string, hookCtx *HookContext, result render.Renderer, err error) (render.Renderer, error) {
var afterHooks []Hook
if hooks, ok := s.hooks[name][HookAfter]; ok {
if allHooks, ok := hooks[HookAll]; ok {
@ -154,9 +155,9 @@ func (s *defaultKernel) callAfterHooks(method Method, name string, hookCtx *Hook
return hookCtx.Result, hookCtx.Err
}
type Kernel interface {
type AppKernel interface {
Register(name string, service Service) error
Hook(service string, lifecycle HookLifecycle, hookType HookType, hook Hook)
Service(name string) Service
Call(method Method, name, id string, data Data, params Params) (render.Renderer, error)
Call(ctx context.Context, method Method, name, id string, data Data, params Params) (render.Renderer, error)
}

View file

@ -1,9 +1,6 @@
package geezer
import "context"
type Params struct {
Ctx context.Context
Path []string
Query Values
Headers Values

View file

@ -1,19 +1,20 @@
package geezer
import (
"context"
"encoding/json"
"github.com/neonxp/geezer/render"
)
type Service interface {
Find(params Params) (render.Renderer, error)
Get(id string, params Params) (render.Renderer, error)
Create(data Data, params Params) (render.Renderer, error)
Update(id string, data Data, params Params) (render.Renderer, error)
Patch(id string, data Data, params Params) (render.Renderer, error)
Remove(id string, params Params) error
Setup(app Kernel, path string) error
Find(ctx context.Context, params Params) (render.Renderer, error)
Get(ctx context.Context, id string, params Params) (render.Renderer, error)
Create(ctx context.Context, data Data, params Params) (render.Renderer, error)
Update(ctx context.Context, id string, data Data, params Params) (render.Renderer, error)
Patch(ctx context.Context, id string, data Data, params Params) (render.Renderer, error)
Remove(ctx context.Context, id string, params Params) error
Setup(app AppKernel, path string) error
}
type Method int

View file

@ -0,0 +1,6 @@
package mongodb
type InsertResult struct {
ID any `json:"id"`
Item any `json:"item"`
}

View file

@ -0,0 +1,98 @@
package mongodb
import (
"context"
"encoding/json"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"github.com/neonxp/geezer"
"github.com/neonxp/geezer/render"
)
type Service[T any] struct {
collection *mongo.Collection
}
func New[T any](collection *mongo.Collection) *Service[T] {
return &Service[T]{collection: collection}
}
func (s Service[T]) Find(ctx context.Context, params geezer.Params) (render.Renderer, error) {
var model []*T
where := bson.D{}
for k, v := range params.Query {
where = append(where, bson.E{Key: k, Value: v[0]})
}
cursor, err := s.collection.Find(ctx, where)
if err != nil {
return nil, err
}
for cursor.Next(ctx) {
r := new(T)
if err := cursor.Decode(r); err != nil {
return nil, err
}
model = append(model, r)
}
return render.JSON(model), nil
}
func (s Service[T]) Get(ctx context.Context, id string, params geezer.Params) (render.Renderer, error) {
var model T
oid, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, err
}
sr := s.collection.FindOne(ctx, bson.M{"_id": oid})
if err := sr.Err(); err != nil {
return nil, err
}
if err := sr.Decode(&model); err != nil {
return nil, err
}
return render.JSON(model), nil
}
func (s Service[T]) Create(ctx context.Context, data geezer.Data, params geezer.Params) (render.Renderer, error) {
var model T
if err := json.Unmarshal(data, &model); err != nil {
return nil, err
}
ir, err := s.collection.InsertOne(ctx, model)
if err != nil {
return nil, err
}
return render.JSON(InsertResult{
ID: ir.InsertedID,
Item: model,
}), nil
}
func (s Service[T]) Update(ctx context.Context, id string, data geezer.Data, params geezer.Params) (render.Renderer, error) {
//TODO implement me
panic("implement me")
}
func (s Service[T]) Patch(ctx context.Context, id string, data geezer.Data, params geezer.Params) (render.Renderer, error) {
//TODO implement me
panic("implement me")
}
func (s Service[T]) Remove(ctx context.Context, id string, params geezer.Params) error {
oid, err := primitive.ObjectIDFromHex(id)
if err != nil {
return err
}
_, err = s.collection.DeleteOne(ctx, bson.M{"_id": oid})
return err
}
func (s Service[T]) Setup(app geezer.AppKernel, path string) error {
return nil
}