diff --git a/places/.gitignore b/places/.gitignore deleted file mode 100644 index affd360..0000000 --- a/places/.gitignore +++ /dev/null @@ -1 +0,0 @@ -locations \ No newline at end of file diff --git a/places/Makefile b/places/Makefile deleted file mode 100644 index d0ff316..0000000 --- a/places/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -GOPATH:=$(shell go env GOPATH) -.PHONY: init -init: - go get -u github.com/golang/protobuf/proto - go get -u github.com/golang/protobuf/protoc-gen-go - go get github.com/micro/micro/v3/cmd/protoc-gen-micro - -.PHONY: proto -proto: - protoc --openapi_out=. --proto_path=. --micro_out=. --go_out=:. proto/places.proto - -.PHONY: docs -docs: - protoc --openapi_out=. --proto_path=. --micro_out=. --go_out=:. proto/places.proto - @redoc-cli bundle api-places.json - -.PHONY: build -build: - go build -o places *.go - -.PHONY: test -test: - go test -v ./... -cover diff --git a/places/README.md b/places/README.md deleted file mode 100644 index cada39e..0000000 --- a/places/README.md +++ /dev/null @@ -1,6 +0,0 @@ -Store and search for points of interest - -# Places Service - -The places API stores points of interest and enables you to search for places nearby or last visited. - diff --git a/places/generate.go b/places/generate.go deleted file mode 100644 index 7d9db91..0000000 --- a/places/generate.go +++ /dev/null @@ -1,3 +0,0 @@ -package main - -//go:generate make proto diff --git a/places/handler/places.go b/places/handler/places.go deleted file mode 100644 index 37281b0..0000000 --- a/places/handler/places.go +++ /dev/null @@ -1,177 +0,0 @@ -package handler - -import ( - "context" - "sync" - "time" - - "github.com/google/uuid" - geo "github.com/hailocab/go-geoindex" - "google.golang.org/protobuf/types/known/timestamppb" - "google.golang.org/protobuf/types/known/wrapperspb" - "gorm.io/gorm" - - "github.com/micro/micro/v3/service/errors" - "github.com/micro/micro/v3/service/logger" - "github.com/micro/services/places/model" - pb "github.com/micro/services/places/proto" -) - -var ( - ErrMissingPlaces = errors.BadRequest("MISSING_LOCATIONS", "One or more places are required") - ErrMissingLatitude = errors.BadRequest("MISSING_LATITUDE", "Latitude is required") - ErrMissingLongitude = errors.BadRequest("MISSING_LONGITUDE", "Longitude is required") - ErrMissingID = errors.BadRequest("MISSING_ID", "Place ID is required") - ErrMissingIDs = errors.BadRequest("MISSING_IDS", "One or more Place IDs are required") - ErrMissingBefore = errors.BadRequest("MISSING_BEFORE", "Before timestamp is required") - ErrMissingAfter = errors.BadRequest("MISSING_AFTER", "After timestamp is required") - ErrMissingRadius = errors.BadRequest("MISSING_RADIUS", "Radius is required") -) - -type Places struct { - sync.RWMutex - - Geoindex *geo.PointsIndex - DB *gorm.DB -} - -// Save a set of places -func (l *Places) Save(ctx context.Context, req *pb.SaveRequest, rsp *pb.SaveResponse) error { - // validate the request - if len(req.Places) == 0 { - return ErrMissingPlaces - } - for _, l := range req.Places { - if l.Latitude == nil { - return ErrMissingLatitude - } - if l.Longitude == nil { - return ErrMissingLongitude - } - if len(l.Id) == 0 { - return ErrMissingID - } - } - - // construct the database objects - ls := make([]*model.Location, len(req.Places)) - for i, lc := range req.Places { - loc := &model.Location{ - ID: uuid.New().String(), - PlaceID: lc.Id, - Latitude: lc.Latitude.Value, - Longitude: lc.Longitude.Value, - } - if lc.Timestamp != nil { - loc.Timestamp = lc.Timestamp.AsTime() - } else { - loc.Timestamp = time.Now() - } - ls[i] = loc - } - - // write to the database - if err := l.DB.Create(ls).Error; err != nil { - logger.Errorf("Error writing to the database: %v", err) - return errors.InternalServerError("DATABASE_ERROR", "Error writing to the database") - } - - // write to the geoindex - l.Lock() - defer l.Unlock() - for _, lc := range ls { - l.Geoindex.Add(lc) - } - return nil -} - -// Last places for a set of users -func (l *Places) Last(ctx context.Context, req *pb.LastRequest, rsp *pb.ListResponse) error { - // validate the request - if req.Ids == nil { - return ErrMissingIDs - } - - // query the database - q := l.DB.Raw("SELECT DISTINCT ON (place_id) place_id, timestamp, latitude, longitude FROM locations WHERE place_id IN (?) ORDER BY place_id, timestamp DESC", req.Ids) - var locs []*model.Location - if err := q.Find(&locs).Error; err != nil { - logger.Errorf("Error reading from the database: %v", err) - return errors.InternalServerError("DATABASE_ERROR", "Error reading from the database") - } - - // serialize the result - rsp.Places = serializePlaces(locs) - return nil -} - -// Near returns the places near a point -func (l *Places) Near(ctx context.Context, req *pb.NearRequest, rsp *pb.ListResponse) error { - // validate the request - if req.Latitude == nil { - return ErrMissingLatitude - } - if req.Longitude == nil { - return ErrMissingLongitude - } - if req.Radius == nil { - return ErrMissingRadius - } - - // query the geoindex - l.RLock() - p := geo.NewGeoPoint("query", req.Latitude.Value, req.Longitude.Value) - result := l.Geoindex.PointsWithin(p, geo.Km(req.Radius.Value), func(p geo.Point) bool { - return true - }) - l.RUnlock() - - // serialize the result - locs := make([]*model.Location, len(result)) - for i, r := range result { - locs[i] = r.(*model.Location) - } - rsp.Places = serializePlaces(locs) - return nil -} - -// Read places for a group of users between two points in time -func (l *Places) Read(ctx context.Context, req *pb.ReadRequest, rsp *pb.ListResponse) error { - // validate the request - if len(req.Ids) == 0 { - return ErrMissingIDs - } - if req.Before == nil { - return ErrMissingBefore - } - if req.After == nil { - return ErrMissingAfter - } - - // construct the request - q := l.DB.Model(&model.Location{}) - q = q.Order("timestamp ASC") - q = q.Where("place_id IN (?) AND timestamp > ? AND timestamp < ?", req.Ids, req.After.AsTime(), req.Before.AsTime()) - var locs []*model.Location - if err := q.Find(&locs).Error; err != nil { - logger.Errorf("Error reading from the database: %v", err) - return errors.InternalServerError("DATABASE_ERROR", "Error reading from the database") - } - - // serialize the result - rsp.Places = serializePlaces(locs) - return nil -} - -func serializePlaces(locs []*model.Location) []*pb.Location { - rsp := make([]*pb.Location, len(locs)) - for i, l := range locs { - rsp[i] = &pb.Location{ - Id: l.PlaceID, - Latitude: &wrapperspb.DoubleValue{Value: l.Latitude}, - Longitude: &wrapperspb.DoubleValue{Value: l.Longitude}, - Timestamp: timestamppb.New(l.Timestamp), - } - } - return rsp -} diff --git a/places/handler/places_test.go b/places/handler/places_test.go deleted file mode 100644 index ee993f3..0000000 --- a/places/handler/places_test.go +++ /dev/null @@ -1,533 +0,0 @@ -package handler_test - -import ( - "context" - "os" - "sort" - "testing" - "time" - - "github.com/golang/protobuf/ptypes/timestamp" - "github.com/google/uuid" - geo "github.com/hailocab/go-geoindex" - "github.com/stretchr/testify/assert" - "google.golang.org/protobuf/types/known/timestamppb" - "google.golang.org/protobuf/types/known/wrapperspb" - "gorm.io/driver/postgres" - "gorm.io/gorm" - - "github.com/micro/services/places/handler" - "github.com/micro/services/places/model" - pb "github.com/micro/services/places/proto" -) - -func testHandler(t *testing.T) pb.PlacesHandler { - // connect to the database - addr := os.Getenv("POSTGRES_URL") - if len(addr) == 0 { - addr = "postgresql://postgres@localhost:5432/postgres?sslmode=disable" - } - db, err := gorm.Open(postgres.Open(addr), &gorm.Config{}) - if err != nil { - t.Fatalf("Error connecting to database: %v", err) - } - - // clean any data from a previous run - if err := db.Exec("DROP TABLE IF EXISTS locations CASCADE").Error; err != nil { - t.Fatalf("Error cleaning database: %v", err) - } - - // migrate the database - if err := db.AutoMigrate(&model.Location{}); err != nil { - t.Fatalf("Error migrating database: %v", err) - } - - return &handler.Places{DB: db, Geoindex: geo.NewPointsIndex(geo.Km(0.1))} -} - -func TestSave(t *testing.T) { - tt := []struct { - Name string - Places []*pb.Location - Error error - }{ - { - Name: "NoPlaces", - Error: handler.ErrMissingPlaces, - }, - { - Name: "NoLatitude", - Places: []*pb.Location{ - { - Longitude: &wrapperspb.DoubleValue{Value: -0.1246}, - Id: uuid.New().String(), - }, - }, - Error: handler.ErrMissingLatitude, - }, - { - Name: "NoLongitude", - Places: []*pb.Location{ - { - Latitude: &wrapperspb.DoubleValue{Value: -0.1246}, - Id: uuid.New().String(), - }, - }, - Error: handler.ErrMissingLongitude, - }, - { - Name: "OneLocation", - Places: []*pb.Location{ - { - Latitude: &wrapperspb.DoubleValue{Value: 51.5007}, - Longitude: &wrapperspb.DoubleValue{Value: 0.1246}, - Timestamp: timestamppb.New(time.Now()), - Id: uuid.New().String(), - }, - }, - }, - { - Name: "ManyPlaces", - Places: []*pb.Location{ - { - Latitude: &wrapperspb.DoubleValue{Value: 51.5007}, - Longitude: &wrapperspb.DoubleValue{Value: 0.1246}, - Timestamp: timestamppb.New(time.Now()), - Id: uuid.New().String(), - }, - { - Latitude: &wrapperspb.DoubleValue{Value: 51.003}, - Longitude: &wrapperspb.DoubleValue{Value: -0.1246}, - Id: uuid.New().String(), - }, - }, - }, - } - - h := testHandler(t) - - for _, tc := range tt { - t.Run(tc.Name, func(t *testing.T) { - err := h.Save(context.Background(), &pb.SaveRequest{ - Places: tc.Places, - }, &pb.SaveResponse{}) - assert.Equal(t, tc.Error, err) - }) - } -} - -func TestLast(t *testing.T) { - h := testHandler(t) - - t.Run("MissingIDs", func(t *testing.T) { - err := h.Last(context.Background(), &pb.LastRequest{}, &pb.ListResponse{}) - assert.Equal(t, handler.ErrMissingIDs, err) - }) - - t.Run("NoMatches", func(t *testing.T) { - var rsp pb.ListResponse - err := h.Last(context.Background(), &pb.LastRequest{ - Ids: []string{uuid.New().String()}, - }, &rsp) - assert.NoError(t, err) - assert.Empty(t, rsp.Places) - }) - tn := time.Now() - - // generate some example data to work with - loc1 := &pb.Location{ - Latitude: &wrapperspb.DoubleValue{Value: 51.5007}, - Longitude: &wrapperspb.DoubleValue{Value: 0.1246}, - Timestamp: timestamppb.New(tn), - Id: "a", - } - loc2 := &pb.Location{ - Latitude: &wrapperspb.DoubleValue{Value: 51.6007}, - Longitude: &wrapperspb.DoubleValue{Value: 0.1546}, - Timestamp: timestamppb.New(tn.Add(1 * time.Microsecond)), - Id: "b", - } - loc3 := &pb.Location{ - Latitude: &wrapperspb.DoubleValue{Value: 52.6007}, - Longitude: &wrapperspb.DoubleValue{Value: 0.2546}, - Timestamp: timestamppb.New(tn.Add(2 * time.Microsecond)), - Id: loc2.Id, - } - err := h.Save(context.TODO(), &pb.SaveRequest{ - Places: []*pb.Location{loc1, loc2, loc3}, - }, &pb.SaveResponse{}) - assert.NoError(t, err) - - t.Run("OneUser", func(t *testing.T) { - var rsp pb.ListResponse - err := h.Last(context.Background(), &pb.LastRequest{ - Ids: []string{loc3.Id}, - }, &rsp) - assert.NoError(t, err) - - if len(rsp.Places) != 1 { - t.Fatalf("One location should be returned") - } - assert.Equal(t, loc3.Id, rsp.Places[0].Id) - assert.Equal(t, loc3.Latitude.Value, rsp.Places[0].Latitude.Value) - assert.Equal(t, loc3.Longitude.Value, rsp.Places[0].Longitude.Value) - assert.Equal(t, microSecondTime(loc3.Timestamp), microSecondTime(rsp.Places[0].Timestamp)) - }) - - t.Run("ManyUser", func(t *testing.T) { - var rsp pb.ListResponse - err := h.Last(context.Background(), &pb.LastRequest{ - Ids: []string{loc1.Id, loc2.Id}, - }, &rsp) - assert.NoError(t, err) - - if len(rsp.Places) != 2 { - t.Fatalf("Two places should be returned") - } - - // sort using user_id so we can hardcode the index - sort.Slice(rsp.Places, func(i, j int) bool { - return rsp.Places[i].Id > rsp.Places[j].Id - }) - - assert.Equal(t, loc1.Id, rsp.Places[1].Id) - assert.Equal(t, loc1.Latitude.Value, rsp.Places[1].Latitude.Value) - assert.Equal(t, loc1.Longitude.Value, rsp.Places[1].Longitude.Value) - assert.Equal(t, microSecondTime(loc1.Timestamp), microSecondTime(rsp.Places[1].Timestamp)) - - assert.Equal(t, loc3.Id, rsp.Places[0].Id) - assert.Equal(t, loc3.Latitude.Value, rsp.Places[0].Latitude.Value) - assert.Equal(t, loc3.Longitude.Value, rsp.Places[0].Longitude.Value) - assert.Equal(t, microSecondTime(loc3.Timestamp), microSecondTime(rsp.Places[0].Timestamp)) - }) -} - -func TestNear(t *testing.T) { - lat := &wrapperspb.DoubleValue{Value: 51.510357} - lng := &wrapperspb.DoubleValue{Value: -0.116773} - rad := &wrapperspb.DoubleValue{Value: 2.0} - - inBoundsLat := &wrapperspb.DoubleValue{Value: 51.5110} - inBoundsLng := &wrapperspb.DoubleValue{Value: -0.1142} - - outOfBoundsLat := &wrapperspb.DoubleValue{Value: 51.5415} - outOfBoundsLng := &wrapperspb.DoubleValue{Value: -0.0028} - - tt := []struct { - Name string - Places []*pb.Location - Results []*pb.Location - QueryLatitude *wrapperspb.DoubleValue - QueryLongitude *wrapperspb.DoubleValue - QueryRadius *wrapperspb.DoubleValue - Error error - }{ - { - Name: "MissingLatitude", - QueryLongitude: lng, - QueryRadius: rad, - Error: handler.ErrMissingLatitude, - }, - { - Name: "MissingLongitude", - QueryLatitude: lat, - QueryRadius: rad, - Error: handler.ErrMissingLongitude, - }, - { - Name: "MissingRadius", - QueryLatitude: lat, - QueryLongitude: lng, - Error: handler.ErrMissingRadius, - }, - { - Name: "NoPlaces", - QueryLatitude: lat, - QueryLongitude: lng, - QueryRadius: rad, - }, - { - Name: "OneWithinRadius", - QueryLatitude: lat, - QueryLongitude: lng, - QueryRadius: rad, - Places: []*pb.Location{ - &pb.Location{ - Latitude: inBoundsLat, - Longitude: inBoundsLng, - Id: "in", - }, - &pb.Location{ - Latitude: outOfBoundsLat, - Longitude: outOfBoundsLng, - Id: "out", - }, - }, - Results: []*pb.Location{ - &pb.Location{ - Latitude: inBoundsLat, - Longitude: inBoundsLng, - Id: "in", - }, - }, - }, - { - Name: "NoneWithinRadius", - QueryLatitude: lat, - QueryLongitude: lng, - QueryRadius: &wrapperspb.DoubleValue{Value: 0.01}, - Places: []*pb.Location{ - &pb.Location{ - Latitude: inBoundsLat, - Longitude: inBoundsLng, - Id: "in", - }, - &pb.Location{ - Latitude: outOfBoundsLat, - Longitude: outOfBoundsLng, - Id: "out", - }, - }, - }, - { - Name: "TwoPlacesForUser", - QueryLatitude: lat, - QueryLongitude: lng, - QueryRadius: rad, - Places: []*pb.Location{ - &pb.Location{ - Latitude: inBoundsLat, - Longitude: inBoundsLng, - Id: "in", - }, - &pb.Location{ - Latitude: outOfBoundsLat, - Longitude: outOfBoundsLng, - Id: "out", - }, - &pb.Location{ - Latitude: inBoundsLat, - Longitude: inBoundsLng, - Id: "out", - }, - }, - Results: []*pb.Location{ - &pb.Location{ - Latitude: inBoundsLat, - Longitude: inBoundsLng, - Id: "in", - }, - &pb.Location{ - Latitude: inBoundsLat, - Longitude: inBoundsLng, - Id: "out", - }, - }, - }, - { - Name: "ManyWithinRadius", - QueryLatitude: lat, - QueryLongitude: lng, - QueryRadius: &wrapperspb.DoubleValue{Value: 20}, - Places: []*pb.Location{ - &pb.Location{ - Latitude: inBoundsLat, - Longitude: inBoundsLng, - Id: "in", - }, - &pb.Location{ - Latitude: outOfBoundsLat, - Longitude: outOfBoundsLng, - Id: "out", - }, - }, - Results: []*pb.Location{ - &pb.Location{ - Latitude: inBoundsLat, - Longitude: inBoundsLng, - Id: "in", - }, - &pb.Location{ - Latitude: outOfBoundsLat, - Longitude: outOfBoundsLng, - Id: "out", - }, - }, - }, - } - - for _, tc := range tt { - t.Run(tc.Name, func(t *testing.T) { - h := testHandler(t) - - // create the places - if len(tc.Places) > 0 { - err := h.Save(context.TODO(), &pb.SaveRequest{Places: tc.Places}, &pb.SaveResponse{}) - assert.NoError(t, err) - } - - // find near places - var rsp pb.ListResponse - err := h.Near(context.TODO(), &pb.NearRequest{ - Latitude: tc.QueryLatitude, - Longitude: tc.QueryLongitude, - Radius: tc.QueryRadius, - }, &rsp) - assert.Equal(t, tc.Error, err) - - // check the count of the results matches - if len(tc.Results) != len(rsp.Places) { - t.Errorf("Incorrect number of results returned. Expected %v, got %v", len(tc.Results), len(rsp.Places)) - } - - // validate the results match - sort.Slice(rsp.Places, func(i, j int) bool { - return rsp.Places[i].Id > rsp.Places[j].Id - }) - sort.Slice(tc.Results, func(i, j int) bool { - return tc.Results[i].Id > tc.Results[j].Id - }) - for i, r := range tc.Results { - l := rsp.Places[i] - assert.Equal(t, r.Id, l.Id) - assert.Equal(t, r.Latitude.Value, l.Latitude.Value) - assert.Equal(t, r.Longitude.Value, l.Longitude.Value) - } - }) - } -} - -func TestRead(t *testing.T) { - h := testHandler(t) - - baseTime := time.Now().Add(time.Hour * -24) - - t.Run("MissingIDs", func(t *testing.T) { - err := h.Read(context.Background(), &pb.ReadRequest{ - After: timestamppb.New(baseTime), - Before: timestamppb.New(baseTime), - }, &pb.ListResponse{}) - assert.Equal(t, handler.ErrMissingIDs, err) - }) - - t.Run("MissingAfter", func(t *testing.T) { - err := h.Read(context.Background(), &pb.ReadRequest{ - Ids: []string{uuid.New().String()}, - Before: timestamppb.New(baseTime), - }, &pb.ListResponse{}) - assert.Equal(t, handler.ErrMissingAfter, err) - }) - - t.Run("MissingBefore", func(t *testing.T) { - err := h.Read(context.Background(), &pb.ReadRequest{ - Ids: []string{uuid.New().String()}, - After: timestamppb.New(baseTime), - }, &pb.ListResponse{}) - assert.Equal(t, handler.ErrMissingBefore, err) - }) - - // generate some example data to work with - loc1 := &pb.Location{ - Latitude: &wrapperspb.DoubleValue{Value: 51.5007}, - Longitude: &wrapperspb.DoubleValue{Value: 0.1246}, - Timestamp: timestamppb.New(baseTime.Add(time.Minute * 10)), - Id: "a", - } - loc2 := &pb.Location{ - Latitude: &wrapperspb.DoubleValue{Value: 51.6007}, - Longitude: &wrapperspb.DoubleValue{Value: 0.1546}, - Timestamp: timestamppb.New(baseTime.Add(time.Minute * 20)), - Id: "b", - } - loc3 := &pb.Location{ - Latitude: &wrapperspb.DoubleValue{Value: 52.6007}, - Longitude: &wrapperspb.DoubleValue{Value: 0.2546}, - Timestamp: timestamppb.New(baseTime.Add(time.Minute * 40)), - Id: loc2.Id, - } - err := h.Save(context.TODO(), &pb.SaveRequest{ - Places: []*pb.Location{loc1, loc2, loc3}, - }, &pb.SaveResponse{}) - assert.NoError(t, err) - - t.Run("NoMatches", func(t *testing.T) { - var rsp pb.ListResponse - err := h.Read(context.Background(), &pb.ReadRequest{ - Ids: []string{uuid.New().String()}, - After: timestamppb.New(baseTime), - Before: timestamppb.New(baseTime.Add(time.Hour)), - }, &rsp) - assert.NoError(t, err) - assert.Empty(t, rsp.Places) - }) - - t.Run("OnePlaceID", func(t *testing.T) { - var rsp pb.ListResponse - err := h.Read(context.Background(), &pb.ReadRequest{ - Ids: []string{loc2.Id}, - After: timestamppb.New(baseTime), - Before: timestamppb.New(baseTime.Add(time.Hour)), - }, &rsp) - assert.NoError(t, err) - - if len(rsp.Places) != 2 { - t.Fatalf("Two places should be returned") - } - assert.Equal(t, loc2.Id, rsp.Places[0].Id) - assert.Equal(t, loc2.Latitude.Value, rsp.Places[0].Latitude.Value) - assert.Equal(t, loc2.Longitude.Value, rsp.Places[0].Longitude.Value) - assert.Equal(t, microSecondTime(loc2.Timestamp), microSecondTime(rsp.Places[0].Timestamp)) - - assert.Equal(t, loc3.Id, rsp.Places[1].Id) - assert.Equal(t, loc3.Latitude.Value, rsp.Places[1].Latitude.Value) - assert.Equal(t, loc3.Longitude.Value, rsp.Places[1].Longitude.Value) - assert.Equal(t, microSecondTime(loc3.Timestamp), microSecondTime(rsp.Places[1].Timestamp)) - }) - - t.Run("OnePlaceIDReducedTime", func(t *testing.T) { - var rsp pb.ListResponse - err := h.Read(context.Background(), &pb.ReadRequest{ - Ids: []string{loc2.Id}, - After: timestamppb.New(baseTime), - Before: timestamppb.New(baseTime.Add(time.Minute * 30)), - }, &rsp) - assert.NoError(t, err) - - if len(rsp.Places) != 1 { - t.Fatalf("One location should be returned") - } - assert.Equal(t, loc2.Id, rsp.Places[0].Id) - assert.Equal(t, loc2.Latitude.Value, rsp.Places[0].Latitude.Value) - assert.Equal(t, loc2.Longitude.Value, rsp.Places[0].Longitude.Value) - assert.Equal(t, microSecondTime(loc2.Timestamp), microSecondTime(rsp.Places[0].Timestamp)) - }) - - t.Run("TwoPlaceIDs", func(t *testing.T) { - var rsp pb.ListResponse - err := h.Read(context.Background(), &pb.ReadRequest{ - Ids: []string{loc1.Id, loc2.Id}, - After: timestamppb.New(baseTime), - Before: timestamppb.New(baseTime.Add(time.Minute * 30)), - }, &rsp) - assert.NoError(t, err) - - if len(rsp.Places) != 2 { - t.Fatalf("Two places should be returned") - } - assert.Equal(t, loc1.Id, rsp.Places[0].Id) - assert.Equal(t, loc1.Latitude.Value, rsp.Places[0].Latitude.Value) - assert.Equal(t, loc1.Longitude.Value, rsp.Places[0].Longitude.Value) - assert.Equal(t, microSecondTime(loc1.Timestamp), microSecondTime(rsp.Places[0].Timestamp)) - - assert.Equal(t, loc2.Id, rsp.Places[1].Id) - assert.Equal(t, loc2.Latitude.Value, rsp.Places[1].Latitude.Value) - assert.Equal(t, loc2.Longitude.Value, rsp.Places[1].Longitude.Value) - assert.Equal(t, microSecondTime(loc2.Timestamp), microSecondTime(rsp.Places[1].Timestamp)) - }) -} - -// postgres has a resolution of 100microseconds so just test that it's accurate to the second -func microSecondTime(t *timestamp.Timestamp) time.Time { - tt := t.AsTime() - return time.Unix(tt.Unix(), int64(tt.Nanosecond()-tt.Nanosecond()%1000)) -} diff --git a/places/main.go b/places/main.go deleted file mode 100644 index 76c1288..0000000 --- a/places/main.go +++ /dev/null @@ -1,51 +0,0 @@ -package main - -import ( - geo "github.com/hailocab/go-geoindex" - "gorm.io/driver/postgres" - "gorm.io/gorm" - - "github.com/micro/services/places/handler" - "github.com/micro/services/places/model" - pb "github.com/micro/services/places/proto" - - "github.com/micro/micro/v3/service" - "github.com/micro/micro/v3/service/config" - "github.com/micro/micro/v3/service/logger" -) - -var dbAddress = "postgresql://postgres:postgres@localhost:5432/places?sslmode=disable" - -func main() { - // Create service - srv := service.New( - service.Name("places"), - service.Version("latest"), - ) - - // Connect to the database - cfg, err := config.Get("places.database") - if err != nil { - logger.Fatalf("Error loading config: %v", err) - } - addr := cfg.String(dbAddress) - db, err := gorm.Open(postgres.Open(addr), &gorm.Config{}) - if err != nil { - logger.Fatalf("Error connecting to database: %v", err) - } - - // Migrate the database - if err := db.AutoMigrate(&model.Location{}); err != nil { - logger.Fatalf("Error migrating database: %v", err) - } - - // Register handler - pb.RegisterPlacesHandler(srv.Server(), &handler.Places{ - DB: db, Geoindex: geo.NewPointsIndex(geo.Km(0.1)), - }) - - // Run service - if err := srv.Run(); err != nil { - logger.Fatal(err) - } -} diff --git a/places/micro.mu b/places/micro.mu deleted file mode 100644 index a88f81d..0000000 --- a/places/micro.mu +++ /dev/null @@ -1 +0,0 @@ -service places diff --git a/places/model/location.go b/places/model/location.go deleted file mode 100644 index 35c8a55..0000000 --- a/places/model/location.go +++ /dev/null @@ -1,26 +0,0 @@ -package model - -import ( - "time" -) - -type Location struct { - ID string - PlaceID string `gorm:"index"` - Latitude float64 - Longitude float64 - Timestamp time.Time -} - -// use the place id for the geoindex so only one result is returned per place -func (l *Location) Id() string { - return l.PlaceID -} - -func (l *Location) Lat() float64 { - return l.Latitude -} - -func (l *Location) Lon() float64 { - return l.Longitude -} diff --git a/places/proto/places.pb.go b/places/proto/places.pb.go deleted file mode 100644 index 3bec013..0000000 --- a/places/proto/places.pb.go +++ /dev/null @@ -1,658 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.26.0 -// protoc v3.15.5 -// source: proto/places.proto - -package places - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - timestamppb "google.golang.org/protobuf/types/known/timestamppb" - wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type Location struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Metadata map[string]string `protobuf:"bytes,3,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Timestamp *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - Latitude *wrapperspb.DoubleValue `protobuf:"bytes,5,opt,name=latitude,proto3" json:"latitude,omitempty"` - Longitude *wrapperspb.DoubleValue `protobuf:"bytes,6,opt,name=longitude,proto3" json:"longitude,omitempty"` -} - -func (x *Location) Reset() { - *x = Location{} - if protoimpl.UnsafeEnabled { - mi := &file_proto_places_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Location) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Location) ProtoMessage() {} - -func (x *Location) ProtoReflect() protoreflect.Message { - mi := &file_proto_places_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Location.ProtoReflect.Descriptor instead. -func (*Location) Descriptor() ([]byte, []int) { - return file_proto_places_proto_rawDescGZIP(), []int{0} -} - -func (x *Location) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *Location) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *Location) GetMetadata() map[string]string { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *Location) GetTimestamp() *timestamppb.Timestamp { - if x != nil { - return x.Timestamp - } - return nil -} - -func (x *Location) GetLatitude() *wrapperspb.DoubleValue { - if x != nil { - return x.Latitude - } - return nil -} - -func (x *Location) GetLongitude() *wrapperspb.DoubleValue { - if x != nil { - return x.Longitude - } - return nil -} - -type SaveRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Places []*Location `protobuf:"bytes,1,rep,name=places,proto3" json:"places,omitempty"` -} - -func (x *SaveRequest) Reset() { - *x = SaveRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_proto_places_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SaveRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SaveRequest) ProtoMessage() {} - -func (x *SaveRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_places_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SaveRequest.ProtoReflect.Descriptor instead. -func (*SaveRequest) Descriptor() ([]byte, []int) { - return file_proto_places_proto_rawDescGZIP(), []int{1} -} - -func (x *SaveRequest) GetPlaces() []*Location { - if x != nil { - return x.Places - } - return nil -} - -type SaveResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *SaveResponse) Reset() { - *x = SaveResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_proto_places_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SaveResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SaveResponse) ProtoMessage() {} - -func (x *SaveResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_places_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SaveResponse.ProtoReflect.Descriptor instead. -func (*SaveResponse) Descriptor() ([]byte, []int) { - return file_proto_places_proto_rawDescGZIP(), []int{2} -} - -type LastRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Ids []string `protobuf:"bytes,1,rep,name=ids,proto3" json:"ids,omitempty"` -} - -func (x *LastRequest) Reset() { - *x = LastRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_proto_places_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *LastRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LastRequest) ProtoMessage() {} - -func (x *LastRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_places_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LastRequest.ProtoReflect.Descriptor instead. -func (*LastRequest) Descriptor() ([]byte, []int) { - return file_proto_places_proto_rawDescGZIP(), []int{3} -} - -func (x *LastRequest) GetIds() []string { - if x != nil { - return x.Ids - } - return nil -} - -type ListResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Places []*Location `protobuf:"bytes,1,rep,name=places,proto3" json:"places,omitempty"` -} - -func (x *ListResponse) Reset() { - *x = ListResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_proto_places_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ListResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListResponse) ProtoMessage() {} - -func (x *ListResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_places_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListResponse.ProtoReflect.Descriptor instead. -func (*ListResponse) Descriptor() ([]byte, []int) { - return file_proto_places_proto_rawDescGZIP(), []int{4} -} - -func (x *ListResponse) GetPlaces() []*Location { - if x != nil { - return x.Places - } - return nil -} - -type NearRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Latitude *wrapperspb.DoubleValue `protobuf:"bytes,1,opt,name=latitude,proto3" json:"latitude,omitempty"` - Longitude *wrapperspb.DoubleValue `protobuf:"bytes,2,opt,name=longitude,proto3" json:"longitude,omitempty"` - // radius to search within, units km - Radius *wrapperspb.DoubleValue `protobuf:"bytes,3,opt,name=radius,proto3" json:"radius,omitempty"` -} - -func (x *NearRequest) Reset() { - *x = NearRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_proto_places_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *NearRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*NearRequest) ProtoMessage() {} - -func (x *NearRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_places_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use NearRequest.ProtoReflect.Descriptor instead. -func (*NearRequest) Descriptor() ([]byte, []int) { - return file_proto_places_proto_rawDescGZIP(), []int{5} -} - -func (x *NearRequest) GetLatitude() *wrapperspb.DoubleValue { - if x != nil { - return x.Latitude - } - return nil -} - -func (x *NearRequest) GetLongitude() *wrapperspb.DoubleValue { - if x != nil { - return x.Longitude - } - return nil -} - -func (x *NearRequest) GetRadius() *wrapperspb.DoubleValue { - if x != nil { - return x.Radius - } - return nil -} - -type ReadRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Ids []string `protobuf:"bytes,1,rep,name=ids,proto3" json:"ids,omitempty"` - After *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=after,proto3" json:"after,omitempty"` - Before *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=before,proto3" json:"before,omitempty"` -} - -func (x *ReadRequest) Reset() { - *x = ReadRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_proto_places_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ReadRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReadRequest) ProtoMessage() {} - -func (x *ReadRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_places_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReadRequest.ProtoReflect.Descriptor instead. -func (*ReadRequest) Descriptor() ([]byte, []int) { - return file_proto_places_proto_rawDescGZIP(), []int{6} -} - -func (x *ReadRequest) GetIds() []string { - if x != nil { - return x.Ids - } - return nil -} - -func (x *ReadRequest) GetAfter() *timestamppb.Timestamp { - if x != nil { - return x.After - } - return nil -} - -func (x *ReadRequest) GetBefore() *timestamppb.Timestamp { - if x != nil { - return x.Before - } - return nil -} - -var File_proto_places_proto protoreflect.FileDescriptor - -var file_proto_places_proto_rawDesc = []byte{ - 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x1a, 0x1f, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, - 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd7, 0x02, - 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3a, - 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1e, 0x2e, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x12, 0x38, 0x0a, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x12, 0x3a, - 0x0a, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x37, 0x0a, 0x0b, 0x53, 0x61, 0x76, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x06, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x2e, - 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, - 0x22, 0x0e, 0x0a, 0x0c, 0x53, 0x61, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1f, 0x0a, 0x0b, 0x4c, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64, - 0x73, 0x22, 0x38, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x28, 0x0a, 0x06, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x22, 0xb9, 0x01, 0x0a, 0x0b, - 0x4e, 0x65, 0x61, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x08, 0x6c, - 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x6c, 0x61, 0x74, - 0x69, 0x74, 0x75, 0x64, 0x65, 0x12, 0x3a, 0x0a, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, - 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x6f, 0x75, 0x62, 0x6c, - 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, - 0x65, 0x12, 0x34, 0x0a, 0x06, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x06, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x22, 0x85, 0x01, 0x0a, 0x0b, 0x52, 0x65, 0x61, 0x64, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x64, 0x73, 0x12, 0x30, 0x0a, 0x05, 0x61, 0x66, 0x74, - 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x05, 0x61, 0x66, 0x74, 0x65, 0x72, 0x12, 0x32, 0x0a, 0x06, 0x62, - 0x65, 0x66, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x06, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x32, - 0xdc, 0x01, 0x0a, 0x06, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x04, 0x53, 0x61, - 0x76, 0x65, 0x12, 0x13, 0x2e, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x53, 0x61, 0x76, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, - 0x2e, 0x53, 0x61, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x33, 0x0a, 0x04, 0x4c, 0x61, 0x73, 0x74, 0x12, 0x13, 0x2e, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, - 0x2e, 0x4c, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x70, - 0x6c, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x04, 0x4e, 0x65, 0x61, 0x72, 0x12, 0x13, 0x2e, 0x70, - 0x6c, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4e, 0x65, 0x61, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x14, 0x2e, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x04, 0x52, 0x65, 0x61, - 0x64, 0x12, 0x13, 0x2e, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x10, - 0x5a, 0x0e, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_proto_places_proto_rawDescOnce sync.Once - file_proto_places_proto_rawDescData = file_proto_places_proto_rawDesc -) - -func file_proto_places_proto_rawDescGZIP() []byte { - file_proto_places_proto_rawDescOnce.Do(func() { - file_proto_places_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_places_proto_rawDescData) - }) - return file_proto_places_proto_rawDescData -} - -var file_proto_places_proto_msgTypes = make([]protoimpl.MessageInfo, 8) -var file_proto_places_proto_goTypes = []interface{}{ - (*Location)(nil), // 0: places.Location - (*SaveRequest)(nil), // 1: places.SaveRequest - (*SaveResponse)(nil), // 2: places.SaveResponse - (*LastRequest)(nil), // 3: places.LastRequest - (*ListResponse)(nil), // 4: places.ListResponse - (*NearRequest)(nil), // 5: places.NearRequest - (*ReadRequest)(nil), // 6: places.ReadRequest - nil, // 7: places.Location.MetadataEntry - (*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp - (*wrapperspb.DoubleValue)(nil), // 9: google.protobuf.DoubleValue -} -var file_proto_places_proto_depIdxs = []int32{ - 7, // 0: places.Location.metadata:type_name -> places.Location.MetadataEntry - 8, // 1: places.Location.timestamp:type_name -> google.protobuf.Timestamp - 9, // 2: places.Location.latitude:type_name -> google.protobuf.DoubleValue - 9, // 3: places.Location.longitude:type_name -> google.protobuf.DoubleValue - 0, // 4: places.SaveRequest.places:type_name -> places.Location - 0, // 5: places.ListResponse.places:type_name -> places.Location - 9, // 6: places.NearRequest.latitude:type_name -> google.protobuf.DoubleValue - 9, // 7: places.NearRequest.longitude:type_name -> google.protobuf.DoubleValue - 9, // 8: places.NearRequest.radius:type_name -> google.protobuf.DoubleValue - 8, // 9: places.ReadRequest.after:type_name -> google.protobuf.Timestamp - 8, // 10: places.ReadRequest.before:type_name -> google.protobuf.Timestamp - 1, // 11: places.Places.Save:input_type -> places.SaveRequest - 3, // 12: places.Places.Last:input_type -> places.LastRequest - 5, // 13: places.Places.Near:input_type -> places.NearRequest - 6, // 14: places.Places.Read:input_type -> places.ReadRequest - 2, // 15: places.Places.Save:output_type -> places.SaveResponse - 4, // 16: places.Places.Last:output_type -> places.ListResponse - 4, // 17: places.Places.Near:output_type -> places.ListResponse - 4, // 18: places.Places.Read:output_type -> places.ListResponse - 15, // [15:19] is the sub-list for method output_type - 11, // [11:15] is the sub-list for method input_type - 11, // [11:11] is the sub-list for extension type_name - 11, // [11:11] is the sub-list for extension extendee - 0, // [0:11] is the sub-list for field type_name -} - -func init() { file_proto_places_proto_init() } -func file_proto_places_proto_init() { - if File_proto_places_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_proto_places_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Location); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_proto_places_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SaveRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_proto_places_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SaveResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_proto_places_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LastRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_proto_places_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_proto_places_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NearRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_proto_places_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReadRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_proto_places_proto_rawDesc, - NumEnums: 0, - NumMessages: 8, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_proto_places_proto_goTypes, - DependencyIndexes: file_proto_places_proto_depIdxs, - MessageInfos: file_proto_places_proto_msgTypes, - }.Build() - File_proto_places_proto = out.File - file_proto_places_proto_rawDesc = nil - file_proto_places_proto_goTypes = nil - file_proto_places_proto_depIdxs = nil -} diff --git a/places/proto/places.pb.micro.go b/places/proto/places.pb.micro.go deleted file mode 100644 index c11af97..0000000 --- a/places/proto/places.pb.micro.go +++ /dev/null @@ -1,154 +0,0 @@ -// Code generated by protoc-gen-micro. DO NOT EDIT. -// source: proto/places.proto - -package places - -import ( - fmt "fmt" - proto "github.com/golang/protobuf/proto" - _ "google.golang.org/protobuf/types/known/timestamppb" - _ "google.golang.org/protobuf/types/known/wrapperspb" - math "math" -) - -import ( - context "context" - api "github.com/micro/micro/v3/service/api" - client "github.com/micro/micro/v3/service/client" - server "github.com/micro/micro/v3/service/server" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -// Reference imports to suppress errors if they are not otherwise used. -var _ api.Endpoint -var _ context.Context -var _ client.Option -var _ server.Option - -// Api Endpoints for Places service - -func NewPlacesEndpoints() []*api.Endpoint { - return []*api.Endpoint{} -} - -// Client API for Places service - -type PlacesService interface { - // Save a set of places - Save(ctx context.Context, in *SaveRequest, opts ...client.CallOption) (*SaveResponse, error) - // Last places for a set of users - Last(ctx context.Context, in *LastRequest, opts ...client.CallOption) (*ListResponse, error) - // Near returns the places near a point at a given time - Near(ctx context.Context, in *NearRequest, opts ...client.CallOption) (*ListResponse, error) - // Read places for a group of users between two points in time - Read(ctx context.Context, in *ReadRequest, opts ...client.CallOption) (*ListResponse, error) -} - -type placesService struct { - c client.Client - name string -} - -func NewPlacesService(name string, c client.Client) PlacesService { - return &placesService{ - c: c, - name: name, - } -} - -func (c *placesService) Save(ctx context.Context, in *SaveRequest, opts ...client.CallOption) (*SaveResponse, error) { - req := c.c.NewRequest(c.name, "Places.Save", in) - out := new(SaveResponse) - err := c.c.Call(ctx, req, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *placesService) Last(ctx context.Context, in *LastRequest, opts ...client.CallOption) (*ListResponse, error) { - req := c.c.NewRequest(c.name, "Places.Last", in) - out := new(ListResponse) - err := c.c.Call(ctx, req, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *placesService) Near(ctx context.Context, in *NearRequest, opts ...client.CallOption) (*ListResponse, error) { - req := c.c.NewRequest(c.name, "Places.Near", in) - out := new(ListResponse) - err := c.c.Call(ctx, req, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *placesService) Read(ctx context.Context, in *ReadRequest, opts ...client.CallOption) (*ListResponse, error) { - req := c.c.NewRequest(c.name, "Places.Read", in) - out := new(ListResponse) - err := c.c.Call(ctx, req, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// Server API for Places service - -type PlacesHandler interface { - // Save a set of places - Save(context.Context, *SaveRequest, *SaveResponse) error - // Last places for a set of users - Last(context.Context, *LastRequest, *ListResponse) error - // Near returns the places near a point at a given time - Near(context.Context, *NearRequest, *ListResponse) error - // Read places for a group of users between two points in time - Read(context.Context, *ReadRequest, *ListResponse) error -} - -func RegisterPlacesHandler(s server.Server, hdlr PlacesHandler, opts ...server.HandlerOption) error { - type places interface { - Save(ctx context.Context, in *SaveRequest, out *SaveResponse) error - Last(ctx context.Context, in *LastRequest, out *ListResponse) error - Near(ctx context.Context, in *NearRequest, out *ListResponse) error - Read(ctx context.Context, in *ReadRequest, out *ListResponse) error - } - type Places struct { - places - } - h := &placesHandler{hdlr} - return s.Handle(s.NewHandler(&Places{h}, opts...)) -} - -type placesHandler struct { - PlacesHandler -} - -func (h *placesHandler) Save(ctx context.Context, in *SaveRequest, out *SaveResponse) error { - return h.PlacesHandler.Save(ctx, in, out) -} - -func (h *placesHandler) Last(ctx context.Context, in *LastRequest, out *ListResponse) error { - return h.PlacesHandler.Last(ctx, in, out) -} - -func (h *placesHandler) Near(ctx context.Context, in *NearRequest, out *ListResponse) error { - return h.PlacesHandler.Near(ctx, in, out) -} - -func (h *placesHandler) Read(ctx context.Context, in *ReadRequest, out *ListResponse) error { - return h.PlacesHandler.Read(ctx, in, out) -} diff --git a/places/proto/places.proto b/places/proto/places.proto deleted file mode 100644 index 1f28bed..0000000 --- a/places/proto/places.proto +++ /dev/null @@ -1,54 +0,0 @@ -syntax = "proto3"; - -package places; -option go_package = "./proto;places"; - -import "google/protobuf/timestamp.proto"; -import "google/protobuf/wrappers.proto"; - -service Places { - // Save a set of places - rpc Save(SaveRequest) returns (SaveResponse) {} - // Last places for a set of users - rpc Last(LastRequest) returns (ListResponse) {} - // Near returns the places near a point at a given time - rpc Near(NearRequest) returns (ListResponse) {} - // Read places for a group of users between two points in time - rpc Read(ReadRequest) returns (ListResponse) {} -} - -message Location { - string id = 1; - string name = 2; - map metadata = 3; - google.protobuf.Timestamp timestamp = 4; - google.protobuf.DoubleValue latitude = 5; - google.protobuf.DoubleValue longitude = 6; -} - -message SaveRequest { - repeated Location places = 1; -} - -message SaveResponse {} - -message LastRequest { - repeated string ids = 1; -} - -message ListResponse { - repeated Location places = 1; -} - -message NearRequest { - google.protobuf.DoubleValue latitude = 1; - google.protobuf.DoubleValue longitude = 2; - // radius to search within, units km - google.protobuf.DoubleValue radius = 3; -} - -message ReadRequest { - repeated string ids = 1; - google.protobuf.Timestamp after = 2; - google.protobuf.Timestamp before = 3; -} diff --git a/places/usage.md b/places/usage.md deleted file mode 100644 index ac4703b..0000000 --- a/places/usage.md +++ /dev/null @@ -1,20 +0,0 @@ -Store and search for points of interest - -# Places Service - -The places API stores points of interest and enables you to search for places nearby or last visited. - - -## Usage - -Places makes use of postgres. Set the config for the database - -``` -micro user config set places.database "postgresql://postgres@localhost:5432/locations?sslmode=disable" -``` - -Run the service - -``` -micro run . -```