Copilot commented on code in PR #78:
URL: 
https://github.com/apache/dubbo-go-pixiu-samples/pull/78#discussion_r2176244708


##########
grpc/simple/client/client.go:
##########
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Package main implements a simple gRPC client that demonstrates how to use 
gRPC-Go libraries
+// to perform unary, client streaming, server streaming and full duplex RPCs.
+//
+// It interacts with the route guide service whose definition can be found in 
routeguide/route_guide.proto.
+package main
+
+import (
+       "context"
+       "flag"
+       "io"
+       "log"
+       "math/rand/v2"
+       "time"
+)
+
+import (
+       "google.golang.org/grpc"
+       "google.golang.org/grpc/credentials/insecure"
+)
+
+import (
+       pb "github.com/dubbo-go-pixiu/samples/grpc/simple/routeguide"
+)
+
+var (
+       serverAddr = flag.String("addr", "localhost:8881", "The server address 
in the format of host:port")
+)
+
+// printFeature gets the feature for the given point.
+func printFeature(client pb.RouteGuideClient, point *pb.Point) {
+       log.Printf("Getting feature for point (%d, %d)", point.Latitude, 
point.Longitude)
+       ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+       defer cancel()
+       feature, err := client.GetFeature(ctx, point)
+       if err != nil {
+               log.Fatalf("client.GetFeature failed: %v", err)
+       }
+       log.Println(feature)
+}
+
+// printFeatures lists all the features within the given bounding Rectangle.
+func printFeatures(client pb.RouteGuideClient, rect *pb.Rectangle) {
+       log.Printf("Looking for features within %v", rect)
+       ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+       defer cancel()
+       stream, err := client.ListFeatures(ctx, rect)
+       if err != nil {
+               log.Fatalf("client.ListFeatures failed: %v", err)
+       }
+       for {
+               feature, err := stream.Recv()
+               if err == io.EOF {
+                       break
+               }
+               if err != nil {
+                       log.Fatalf("client.ListFeatures failed: %v", err)
+               }
+               log.Printf("Feature: name: %q, point:(%v, %v)", 
feature.GetName(),
+                       feature.GetLocation().GetLatitude(), 
feature.GetLocation().GetLongitude())
+       }
+}
+
+// runRecordRoute sends a sequence of points to server and expects to get a 
RouteSummary from server.
+func runRecordRoute(client pb.RouteGuideClient) {
+       // Create a random number of random points
+       pointCount := int(rand.Int32N(100)) + 2 // Traverse at least two points
+       var points []*pb.Point
+       for i := 0; i < pointCount; i++ {
+               points = append(points, randomPoint())
+       }
+       log.Printf("Traversing %d points.", len(points))
+       ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+       defer cancel()
+       stream, err := client.RecordRoute(ctx)
+       if err != nil {
+               log.Fatalf("client.RecordRoute failed: %v", err)
+       }
+       for _, point := range points {
+               if err := stream.Send(point); err != nil {
+                       log.Fatalf("client.RecordRoute: stream.Send(%v) failed: 
%v", point, err)
+               }
+       }
+       reply, err := stream.CloseAndRecv()
+       if err != nil {
+               log.Fatalf("client.RecordRoute failed: %v", err)
+       }
+       log.Printf("Route summary: %v", reply)
+}
+
+// runRouteChat receives a sequence of route notes, while sending notes for 
various locations.
+func runRouteChat(client pb.RouteGuideClient) {
+       notes := []*pb.RouteNote{
+               {Location: &pb.Point{Latitude: 0, Longitude: 1}, Message: 
"First message"},
+               {Location: &pb.Point{Latitude: 0, Longitude: 2}, Message: 
"Second message"},
+               {Location: &pb.Point{Latitude: 0, Longitude: 3}, Message: 
"Third message"},
+               {Location: &pb.Point{Latitude: 0, Longitude: 1}, Message: 
"Fourth message"},
+               {Location: &pb.Point{Latitude: 0, Longitude: 2}, Message: 
"Fifth message"},
+               {Location: &pb.Point{Latitude: 0, Longitude: 3}, Message: 
"Sixth message"},
+       }
+       ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+       defer cancel()
+       stream, err := client.RouteChat(ctx)
+       if err != nil {
+               log.Fatalf("client.RouteChat failed: %v", err)
+       }
+       waitc := make(chan struct{})
+       go func() {
+               for {
+                       in, err := stream.Recv()
+                       if err == io.EOF {
+                               // read done.
+                               close(waitc)
+                               return
+                       }
+                       if err != nil {
+                               log.Fatalf("client.RouteChat failed: %v", err)
+                       }
+                       log.Printf("Got message %s at point(%d, %d)", 
in.Message, in.Location.Latitude, in.Location.Longitude)
+               }
+       }()
+       for _, note := range notes {
+               if err := stream.Send(note); err != nil {
+                       log.Fatalf("client.RouteChat: stream.Send(%v) failed: 
%v", note, err)
+               }
+       }
+       stream.CloseSend()
+       <-waitc
+}
+
+func randomPoint() *pb.Point {
+       lat := (rand.Int32N(180) - 90) * 1e7
+       long := (rand.Int32N(360) - 180) * 1e7
+       return &pb.Point{Latitude: lat, Longitude: long}
+}
+
+func main() {
+       flag.Parse()
+       var opts []grpc.DialOption
+
+       opts = append(opts, 
grpc.WithTransportCredentials(insecure.NewCredentials()))
+
+       conn, err := grpc.NewClient(*serverAddr, opts...)

Review Comment:
   grpc.NewClient is not a valid gRPC-Go function; use grpc.Dial to connect to 
the server.
   ```suggestion
        conn, err := grpc.Dial(*serverAddr, opts...)
   ```



##########
grpc/simple/server/server.go:
##########
@@ -0,0 +1,831 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Package main implements a simple gRPC server that demonstrates how to use 
gRPC-Go libraries
+// to perform unary, client streaming, server streaming and full duplex RPCs.
+//
+// It implements the route guide service whose definition can be found in 
routeguide/route_guide.proto.
+package main
+
+import (
+       "context"
+       "encoding/json"
+       "flag"
+       "fmt"
+       "io"
+       "log"
+       "math"
+       "net"
+       "os"
+       "sync"
+       "time"
+)
+
+import (
+       "google.golang.org/grpc"
+       "google.golang.org/protobuf/proto"
+)
+
+import (
+       pb "github.com/dubbo-go-pixiu/samples/grpc/simple/routeguide"
+)
+
+var (
+       jsonDBFile = flag.String("json_db_file", "", "A json file containing a 
list of features")
+       port       = flag.Int("port", 50051, "The server port")
+)
+
+type routeGuideServer struct {
+       pb.UnimplementedRouteGuideServer
+       savedFeatures []*pb.Feature // read-only after initialized
+
+       mu         sync.Mutex // protects routeNotes
+       routeNotes map[string][]*pb.RouteNote
+}
+
+// GetFeature returns the feature at the given point.
+func (s *routeGuideServer) GetFeature(_ context.Context, point *pb.Point) 
(*pb.Feature, error) {
+       for _, feature := range s.savedFeatures {
+               if proto.Equal(feature.Location, point) {
+                       return feature, nil
+               }
+       }
+       // No feature was found, return an unnamed feature
+       return &pb.Feature{Location: point}, nil
+}
+
+// ListFeatures lists all features contained within the given bounding 
Rectangle.
+func (s *routeGuideServer) ListFeatures(rect *pb.Rectangle, stream 
pb.RouteGuide_ListFeaturesServer) error {
+       for _, feature := range s.savedFeatures {
+               if inRange(feature.Location, rect) {
+                       if err := stream.Send(feature); err != nil {
+                               return err
+                       }
+               }
+       }
+       return nil
+}
+
+// RecordRoute records a route composited of a sequence of points.
+//
+// It gets a stream of points, and responds with statistics about the "trip":
+// number of points,  number of known features visited, total distance 
traveled, and
+// total time spent.
+func (s *routeGuideServer) RecordRoute(stream pb.RouteGuide_RecordRouteServer) 
error {
+       var pointCount, featureCount, distance int32
+       var lastPoint *pb.Point
+       startTime := time.Now()
+       for {
+               point, err := stream.Recv()
+               if err == io.EOF {
+                       endTime := time.Now()
+                       return stream.SendAndClose(&pb.RouteSummary{
+                               PointCount:   pointCount,
+                               FeatureCount: featureCount,
+                               Distance:     distance,
+                               ElapsedTime:  
int32(endTime.Sub(startTime).Seconds()),
+                       })
+               }
+               if err != nil {
+                       return err
+               }
+               pointCount++
+               for _, feature := range s.savedFeatures {
+                       if proto.Equal(feature.Location, point) {
+                               featureCount++
+                       }
+               }
+               if lastPoint != nil {
+                       distance += calcDistance(lastPoint, point)
+               }
+               lastPoint = point
+       }
+}
+
+// RouteChat receives a stream of message/location pairs, and responds with a 
stream of all
+// previous messages at each of those locations.
+func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) 
error {
+       for {
+               in, err := stream.Recv()
+               if err == io.EOF {
+                       return nil
+               }
+               if err != nil {
+                       return err
+               }
+               key := serialize(in.Location)
+
+               s.mu.Lock()
+               s.routeNotes[key] = append(s.routeNotes[key], in)
+               // Note: this copy prevents blocking other clients while 
serving this one.
+               // We don't need to do a deep copy, because elements in the 
slice are
+               // insert-only and never modified.
+               rn := make([]*pb.RouteNote, len(s.routeNotes[key]))
+               copy(rn, s.routeNotes[key])
+               s.mu.Unlock()
+
+               for _, note := range rn {
+                       if err := stream.Send(note); err != nil {
+                               return err
+                       }
+               }
+       }
+}
+
+// loadFeatures loads features from a JSON file.
+func (s *routeGuideServer) loadFeatures(filePath string) {
+       var data []byte
+       if filePath != "" {
+               var err error
+               data, err = os.ReadFile(filePath)
+               if err != nil {
+                       log.Fatalf("Failed to load default features: %v", err)
+               }
+       } else {
+               data = exampleData
+       }
+       if err := json.Unmarshal(data, &s.savedFeatures); err != nil {
+               log.Fatalf("Failed to load default features: %v", err)
+       }
+}
+
+func toRadians(num float64) float64 {
+       return num * math.Pi / float64(180)
+}
+
+// calcDistance calculates the distance between two points using the 
"haversine" formula.
+// The formula is based on http://mathforum.org/library/drmath/view/51879.html.
+func calcDistance(p1 *pb.Point, p2 *pb.Point) int32 {
+       const CordFactor float64 = 1e7
+       const R = float64(6371000) // earth radius in metres
+       lat1 := toRadians(float64(p1.Latitude) / CordFactor)
+       lat2 := toRadians(float64(p2.Latitude) / CordFactor)
+       lng1 := toRadians(float64(p1.Longitude) / CordFactor)
+       lng2 := toRadians(float64(p2.Longitude) / CordFactor)
+       dlat := lat2 - lat1
+       dlng := lng2 - lng1
+
+       a := math.Sin(dlat/2)*math.Sin(dlat/2) +
+               math.Cos(lat1)*math.Cos(lat2)*
+                       math.Sin(dlng/2)*math.Sin(dlng/2)
+       c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
+
+       distance := R * c
+       return int32(distance)
+}
+
+func inRange(point *pb.Point, rect *pb.Rectangle) bool {
+       left := math.Min(float64(rect.Lo.Longitude), float64(rect.Hi.Longitude))
+       right := math.Max(float64(rect.Lo.Longitude), 
float64(rect.Hi.Longitude))
+       top := math.Max(float64(rect.Lo.Latitude), float64(rect.Hi.Latitude))
+       bottom := math.Min(float64(rect.Lo.Latitude), float64(rect.Hi.Latitude))
+
+       if float64(point.Longitude) >= left &&
+               float64(point.Longitude) <= right &&
+               float64(point.Latitude) >= bottom &&
+               float64(point.Latitude) <= top {
+               return true
+       }
+       return false
+}
+
+func serialize(point *pb.Point) string {
+       return fmt.Sprintf("%d %d", point.Latitude, point.Longitude)
+}
+
+func newServer() *routeGuideServer {
+       s := &routeGuideServer{routeNotes: make(map[string][]*pb.RouteNote)}
+       s.loadFeatures(*jsonDBFile)
+       return s
+}
+
+func main() {
+       flag.Parse()
+       lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", *port))
+       if err != nil {
+               log.Fatalf("failed to listen: %v", err)
+       }
+       var opts []grpc.ServerOption
+
+       grpcServer := grpc.NewServer(opts...)
+       pb.RegisterRouteGuideServer(grpcServer, newServer())
+       grpcServer.Serve(lis)
+}
+
+// exampleData is a copy of testdata/route_guide_db.json. It's to avoid
+// specifying file path with `go run`.
+var exampleData = []byte(`[{

Review Comment:
   [nitpick] Inlining the entire JSON database as a byte slice makes the file 
very large and harder to maintain; consider moving `exampleData` to an external 
JSON file and loading it via `os.ReadFile`.



##########
grpc/simple/test/simple_test.go:
##########
@@ -0,0 +1,176 @@
+package test
+
+import (
+       "context"
+       "io"
+       "math/rand/v2"
+       "sync"
+       "testing"
+       "time"
+)
+
+import (
+       "google.golang.org/grpc"
+       "google.golang.org/grpc/credentials/insecure"
+)
+
+import (
+       pb "github.com/dubbo-go-pixiu/samples/grpc/simple/routeguide"
+       "github.com/stretchr/testify/assert"
+)
+
+const (
+       serverAddr = "localhost:8881"
+)
+
+func TestRouteGuide(t *testing.T) {
+       var opts []grpc.DialOption
+       opts = append(opts, 
grpc.WithTransportCredentials(insecure.NewCredentials()))
+       conn, err := grpc.NewClient(serverAddr, opts...)

Review Comment:
   grpc.NewClient is not part of the gRPC-Go API; replace with grpc.Dial to 
establish the client connection.
   ```suggestion
        conn, err := grpc.Dial(serverAddr, opts...)
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to