From 03c94cb465d6b64f5a4d97751ecee596a4bc43ac Mon Sep 17 00:00:00 2001 From: ragadyazji Date: Fri, 31 Jan 2025 19:28:04 -0500 Subject: [PATCH] fix(cookbook): update twitter cookbook mongodb driver The old driver is deprecated and the example doesn't work. Fixes #128 --- cookbook/twitter/handler/handler.go | 6 ++-- cookbook/twitter/handler/post.go | 43 +++++++++++++-------------- cookbook/twitter/handler/user.go | 45 +++++++++++++++-------------- cookbook/twitter/model/post.go | 6 ++-- cookbook/twitter/model/user.go | 6 ++-- cookbook/twitter/server.go | 24 ++++++++++----- go.mod | 13 ++++++++- go.sum | 17 +++++++++++ 8 files changed, 97 insertions(+), 63 deletions(-) diff --git a/cookbook/twitter/handler/handler.go b/cookbook/twitter/handler/handler.go index dc730c8e..742ee7fa 100644 --- a/cookbook/twitter/handler/handler.go +++ b/cookbook/twitter/handler/handler.go @@ -1,12 +1,10 @@ package handler -import ( - "gopkg.in/mgo.v2" -) +import "go.mongodb.org/mongo-driver/v2/mongo" type ( Handler struct { - DB *mgo.Session + Client *mongo.Client } ) diff --git a/cookbook/twitter/handler/post.go b/cookbook/twitter/handler/post.go index 57509e64..9d26964f 100644 --- a/cookbook/twitter/handler/post.go +++ b/cookbook/twitter/handler/post.go @@ -1,21 +1,26 @@ package handler import ( + "context" "net/http" "strconv" "github.com/labstack/echo/v4" "github.com/labstack/echox/cookbook/twitter/model" - "gopkg.in/mgo.v2" - "gopkg.in/mgo.v2/bson" + "go.mongodb.org/mongo-driver/v2/bson" + "go.mongodb.org/mongo-driver/v2/mongo/options" ) func (h *Handler) CreatePost(c echo.Context) (err error) { + userID, err := bson.ObjectIDFromHex(userIDFromToken(c)) + if err != nil { + return err + } u := &model.User{ - ID: bson.ObjectIdHex(userIDFromToken(c)), + ID: userID, } p := &model.Post{ - ID: bson.NewObjectId(), + ID: bson.NewObjectID(), From: u.ID.Hex(), } if err = c.Bind(p); err != nil { @@ -27,19 +32,14 @@ func (h *Handler) CreatePost(c echo.Context) (err error) { return &echo.HTTPError{Code: http.StatusBadRequest, Message: "invalid to or message fields"} } - // Find user from database - db := h.DB.Clone() - defer db.Close() - if err = db.DB("twitter").C("users").FindId(u.ID).One(u); err != nil { - if err == mgo.ErrNotFound { - return echo.ErrNotFound - } - return + // Find user from database by id + if err = h.Client.Database("twitter").Collection("users").FindOne(context.TODO(), bson.M{"_id": u.ID}).Decode(u); err != nil { + return err } // Save post in database - if err = db.DB("twitter").C("posts").Insert(p); err != nil { - return + if _, err = h.Client.Database("twitter").Collection("posts").InsertOne(context.TODO(), p); err != nil { + return err } return c.JSON(http.StatusCreated, p) } @@ -59,15 +59,16 @@ func (h *Handler) FetchPost(c echo.Context) (err error) { // Retrieve posts from database posts := []*model.Post{} - db := h.DB.Clone() - if err = db.DB("twitter").C("posts"). - Find(bson.M{"to": userID}). - Skip((page - 1) * limit). - Limit(limit). - All(&posts); err != nil { + cur, err := h.Client.Database("twitter").Collection("posts"). + Find(context.TODO(), bson.M{"to": userID}, options.Find().SetSkip(int64((page-1)*limit)).SetLimit(int64(limit))) + if err != nil { + return + } + defer cur.Close(context.TODO()) + + if err = cur.All(context.TODO(), &posts); err != nil { return } - defer db.Close() return c.JSON(http.StatusOK, posts) } diff --git a/cookbook/twitter/handler/user.go b/cookbook/twitter/handler/user.go index 8e5f9b2c..83112e09 100644 --- a/cookbook/twitter/handler/user.go +++ b/cookbook/twitter/handler/user.go @@ -1,19 +1,19 @@ package handler import ( - "github.com/golang-jwt/jwt/v5" "net/http" "time" + "github.com/golang-jwt/jwt/v5" + "github.com/labstack/echo/v4" "github.com/labstack/echox/cookbook/twitter/model" - "gopkg.in/mgo.v2" - "gopkg.in/mgo.v2/bson" + "go.mongodb.org/mongo-driver/v2/bson" ) func (h *Handler) Signup(c echo.Context) (err error) { // Bind - u := &model.User{ID: bson.NewObjectId()} + u := &model.User{ID: bson.NewObjectID()} if err = c.Bind(u); err != nil { return } @@ -24,9 +24,7 @@ func (h *Handler) Signup(c echo.Context) (err error) { } // Save user - db := h.DB.Clone() - defer db.Close() - if err = db.DB("twitter").C("users").Insert(u); err != nil { + if _, err = h.Client.Database("twitter").Collection("users").InsertOne(c.Request().Context(), u); err != nil { return } @@ -41,14 +39,18 @@ func (h *Handler) Login(c echo.Context) (err error) { } // Find user - db := h.DB.Clone() - defer db.Close() - if err = db.DB("twitter").C("users"). - Find(bson.M{"email": u.Email, "password": u.Password}).One(u); err != nil { - if err == mgo.ErrNotFound { - return &echo.HTTPError{Code: http.StatusUnauthorized, Message: "invalid email or password"} + cursor, err := h.Client.Database("twitter").Collection("users"). + Find(c.Request().Context(), bson.M{"email": u.Email, "password": u.Password}) + if err != nil { + return &echo.HTTPError{Code: http.StatusUnauthorized, Message: "invalid email or password"} + } + defer cursor.Close(c.Request().Context()) + if cursor.Next(c.Request().Context()) { + if err = cursor.Decode(u); err != nil { + return err } - return + } else { + return &echo.HTTPError{Code: http.StatusUnauthorized, Message: "invalid email or password"} } //----- @@ -78,13 +80,14 @@ func (h *Handler) Follow(c echo.Context) (err error) { id := c.Param("id") // Add a follower to user - db := h.DB.Clone() - defer db.Close() - if err = db.DB("twitter").C("users"). - UpdateId(bson.ObjectIdHex(id), bson.M{"$addToSet": bson.M{"followers": userID}}); err != nil { - if err == mgo.ErrNotFound { - return echo.ErrNotFound - } + targetUserID, err := bson.ObjectIDFromHex(id) + if err != nil { + return err + } + if _, err = h.Client.Database("twitter"). + Collection("users"). + UpdateOne(c.Request().Context(), bson.M{"_id": targetUserID}, bson.M{"$addToSet": bson.M{"followers": userID}}); err != nil { + return err } return diff --git a/cookbook/twitter/model/post.go b/cookbook/twitter/model/post.go index 5b5333d6..8efae34b 100644 --- a/cookbook/twitter/model/post.go +++ b/cookbook/twitter/model/post.go @@ -1,12 +1,10 @@ package model -import ( - "gopkg.in/mgo.v2/bson" -) +import "go.mongodb.org/mongo-driver/v2/bson" type ( Post struct { - ID bson.ObjectId `json:"id" bson:"_id,omitempty"` + ID bson.ObjectID `json:"id" bson:"_id,omitempty"` To string `json:"to" bson:"to"` From string `json:"from" bson:"from"` Message string `json:"message" bson:"message"` diff --git a/cookbook/twitter/model/user.go b/cookbook/twitter/model/user.go index 62a4b11a..c3f85c61 100644 --- a/cookbook/twitter/model/user.go +++ b/cookbook/twitter/model/user.go @@ -1,12 +1,10 @@ package model -import ( - "gopkg.in/mgo.v2/bson" -) +import "go.mongodb.org/mongo-driver/v2/bson" type ( User struct { - ID bson.ObjectId `json:"id" bson:"_id,omitempty"` + ID bson.ObjectID `json:"id" bson:"_id,omitempty"` Email string `json:"email" bson:"email"` Password string `json:"password,omitempty" bson:"password"` Token string `json:"token,omitempty" bson:"-"` diff --git a/cookbook/twitter/server.go b/cookbook/twitter/server.go index e5fad7c5..a020c41a 100644 --- a/cookbook/twitter/server.go +++ b/cookbook/twitter/server.go @@ -2,11 +2,15 @@ package main import ( echojwt "github.com/labstack/echo-jwt/v4" - "github.com/labstack/echo/v4" + echo "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" "github.com/labstack/echox/cookbook/twitter/handler" "github.com/labstack/gommon/log" - "gopkg.in/mgo.v2" + "golang.org/x/net/context" + + "go.mongodb.org/mongo-driver/v2/bson" + "go.mongodb.org/mongo-driver/v2/mongo" + "go.mongodb.org/mongo-driver/v2/mongo/options" ) func main() { @@ -24,22 +28,26 @@ func main() { }, })) - // Database connection - db, err := mgo.Dial("localhost") + // Mongo Database connection + // Use the SetServerAPIOptions() method to set the Stable API version to 1 + serverAPI := options.ServerAPI(options.ServerAPIVersion1) + opts := options.Client().ApplyURI("mongoDBURI").SetServerAPIOptions(serverAPI) + // Create a new client and connect to the server + client, err := mongo.Connect(opts) if err != nil { e.Logger.Fatal(err) } // Create indices - if err = db.Copy().DB("twitter").C("users").EnsureIndex(mgo.Index{ - Key: []string{"email"}, - Unique: true, + if _, err = client.Database("twitter").Collection("users").Indexes().CreateOne(context.TODO(), mongo.IndexModel{ + Keys: bson.D{{Key: "email", Value: 1}}, // Create an index on the "email" field + Options: options.Index().SetUnique(true), // Make the index unique }); err != nil { log.Fatal(err) } // Initialize handler - h := &handler.Handler{DB: db} + h := &handler.Handler{Client: client} // Routes e.POST("/signup", h.Signup) diff --git a/go.mod b/go.mod index e9afd31c..1f0d1fa4 100644 --- a/go.mod +++ b/go.mod @@ -19,12 +19,22 @@ require ( gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 ) +require ( + github.com/golang/snappy v0.0.4 // indirect + github.com/klauspost/compress v1.16.7 // indirect + github.com/xdg-go/pbkdf2 v1.0.0 // indirect + github.com/xdg-go/scram v1.1.2 // indirect + github.com/xdg-go/stringprep v1.0.4 // indirect + github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect + golang.org/x/sync v0.10.0 // indirect +) + require ( github.com/daaku/go.zipexe v1.0.2 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/goccy/go-json v0.10.4 // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect github.com/lestrrat-go/blackmagic v1.0.2 // indirect @@ -37,6 +47,7 @@ require ( github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect + go.mongodb.org/mongo-driver/v2 v2.0.0 golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.8.0 // indirect diff --git a/go.sum b/go.sum index 5ee650ba..9c176635 100644 --- a/go.sum +++ b/go.sum @@ -18,12 +18,17 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -77,7 +82,17 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= 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/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.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.mongodb.org/mongo-driver/v2 v2.0.0 h1:Jfd7XpdZa9yk3eY774bO7SWVb30noLSirL9nKTpavhI= +go.mongodb.org/mongo-driver/v2 v2.0.0/go.mod h1:nSjmNq4JUstE8IRZKTktLgMHM4F1fccL6HGX1yh+8RA= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= @@ -91,6 +106,8 @@ golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=