Skip to content

Instantly share code, notes, and snippets.

// createCmd represents the create command
var createCmd = &cobra.Command{
Use: "create",
Short: "Create a new blog post",
Long: `Create a new blogpost on the server through gRPC.
A blog post requires an AuthorId, Title and Content.`,
RunE: func(cmd *cobra.Command, args []string) error {
// Get the data from our flags
author, err := cmd.Flags().GetString("author")
func init() {
createCmd.Flags().StringP("author", "a", "", "Add an author")
createCmd.Flags().StringP("title", "t", "", "A title for the blog")
createCmd.Flags().StringP("content", "c", "", "The content for the blog")
createCmd.MarkFlagRequired("author")
createCmd.MarkFlagRequired("title")
createCmd.MarkFlagRequired("content")
rootCmd.AddCommand(createCmd)
}
var cfgFile string
// Client and context global vars for the cmd package
// So they can be used by our subcommands
var client blogpb.BlogServiceClient
var requestCtx context.Context
var requestOpts grpc.DialOption
func init() {
// initConfig reads in config file and ENV variables
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "blogclient",
Short: "a gRPC client to communicate with the BlogService server",
Long: `a gRPC client to communicate with the BlogService server.
You can use this client to create and read blogs.`,
}
func (s *BlogServiceServer) ListBlogs(req *blogpb.ListBlogsReq, stream blogpb.BlogService_ListBlogsServer) error {
// Initiate a BlogItem type to write decoded data to
data := &BlogItem{}
// collection.Find returns a cursor for our (empty) query
cursor, err := blogdb.Find(context.Background(), bson.M{})
if err != nil {
return status.Errorf(codes.Internal, fmt.Sprintf("Unknown internal error: %v", err))
}
// An expression with defer will be called at the end of the function
defer cursor.Close(context.Background())
func (s *BlogServiceServer) UpdateBlog(ctx context.Context, req *blogpb.UpdateBlogReq) (*blogpb.UpdateBlogRes, error) {
// Get the blog data from the request
blog := req.GetBlog()
// Convert the Id string to a MongoDB ObjectId
oid, err := primitive.ObjectIDFromHex(blog.GetId())
if err != nil {
return nil, status.Errorf(
codes.InvalidArgument,
fmt.Sprintf("Could not convert the supplied blog id to a MongoDB ObjectId: %v", err),
func (s *BlogServiceServer) DeleteBlog(ctx context.Context, req *blogpb.DeleteBlogReq) (*blogpb.DeleteBlogRes, error) {
// Get the ID (string) from the request message and convert it to an Object ID
oid, err := primitive.ObjectIDFromHex(req.GetId())
// Check for errors
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("Could not convert to ObjectId: %v", err))
}
// DeleteOne returns DeleteResult which is a struct containing the amount of deleted docs (in this case only 1 always)
// So we return a boolean instead
_, err = blogdb.DeleteOne(ctx, bson.M{"_id": oid})
func (s *BlogServiceServer) ReadBlog(ctx context.Context, req *blogpb.ReadBlogReq) (*blogpb.ReadBlogRes, error) {
// convert string id (from proto) to mongoDB ObjectId
oid, err := primitive.ObjectIDFromHex(req.GetId())
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("Could not convert to ObjectId: %v", err))
}
result := blogdb.FindOne(ctx, bson.M{"_id": oid})
// Create an empty BlogItem to write our decode result to
data := BlogItem{}
// decode and write to data
func (s *BlogServiceServer) CreateBlog(ctx context.Context, req *blogpb.CreateBlogReq) (*blogpb.CreateBlogRes, error) {
// Essentially doing req.Blog to access the struct with a nil check
blog := req.GetBlog()
// Now we have to convert this into a BlogItem type to convert into BSON
data := BlogItem{
// ID: Empty, so it gets omitted and MongoDB generates a unique Object ID upon insertion.
AuthorID: blog.GetAuthorId(),
Title: blog.GetTitle(),
Content: blog.GetContent(),
}
type BlogItem struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
AuthorID string `bson:"author_id"`
Content string `bson:"content"`
Title string `bson:"title"`
}
func main() { ... }