diff --git a/go.mod b/go.mod index a455363..4edd6d1 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/golang/protobuf v1.4.3 github.com/google/uuid v1.1.2 github.com/gosimple/slug v1.9.0 + github.com/hailocab/go-geoindex v0.0.0-20160127134810-64631bfe9711 github.com/micro/dev v0.0.0-20201111162228-80c2b20de2db github.com/micro/go-micro/v2 v2.9.1 // indirect github.com/micro/micro/v3 v3.0.0-beta.7 diff --git a/go.sum b/go.sum index 197bc83..c780381 100644 --- a/go.sum +++ b/go.sum @@ -260,6 +260,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +github.com/hailocab/go-geoindex v0.0.0-20160127134810-64631bfe9711 h1:Oi8hPOZX0gaM2sPVXse2bMpfOjP47a7O61YuB6Z4sGk= +github.com/hailocab/go-geoindex v0.0.0-20160127134810-64631bfe9711/go.mod h1:+v2qJ3UZe4q2GfgZO4od004F/cMgJbmPSs7dD/ZMUkY= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= diff --git a/location/.gitignore b/location/.gitignore new file mode 100644 index 0000000..0dd7a8a --- /dev/null +++ b/location/.gitignore @@ -0,0 +1,2 @@ + +location diff --git a/location/Dockerfile b/location/Dockerfile new file mode 100644 index 0000000..a323d1e --- /dev/null +++ b/location/Dockerfile @@ -0,0 +1,3 @@ +FROM alpine +ADD location /location +ENTRYPOINT [ "/location" ] diff --git a/location/Makefile b/location/Makefile new file mode 100644 index 0000000..dd68854 --- /dev/null +++ b/location/Makefile @@ -0,0 +1,22 @@ + +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 --proto_path=. --micro_out=. --go_out=:. proto/location.proto + +.PHONY: build +build: + go build -o location *.go + +.PHONY: test +test: + go test -v ./... -cover + +.PHONY: docker +docker: + docker build . -t location:latest diff --git a/location/README.md b/location/README.md new file mode 100644 index 0000000..9e10228 --- /dev/null +++ b/location/README.md @@ -0,0 +1,23 @@ +# Location Service + +A realtime GPS location tracking and search service + +Generated with + +``` +micro new location +``` + +## Usage + +Generate the proto code + +``` +make proto +``` + +Run the service + +``` +micro run . +``` diff --git a/location/domain/domain.go b/location/domain/domain.go new file mode 100644 index 0000000..03a1e93 --- /dev/null +++ b/location/domain/domain.go @@ -0,0 +1,104 @@ +package domain + +import ( + "sync" + + geo "github.com/hailocab/go-geoindex" + "github.com/micro/micro/v3/service/errors" + common "github.com/micro/services/location/proto" +) + +var ( + mtx sync.RWMutex + defaultIndex = geo.NewPointsIndex(geo.Km(0.5)) +) + +type Entity struct { + ID string + Type string + Latitude float64 + Longitude float64 + Timestamp int64 +} + +func (e *Entity) Id() string { + return e.ID +} + +func (e *Entity) Lat() float64 { + return e.Latitude +} + +func (e *Entity) Lon() float64 { + return e.Longitude +} + +func (e *Entity) ToProto() *common.Entity { + return &common.Entity{ + Id: e.ID, + Type: e.Type, + Location: &common.Point{ + Latitude: e.Latitude, + Longitude: e.Longitude, + Timestamp: e.Timestamp, + }, + } +} + +func ProtoToEntity(e *common.Entity) *Entity { + return &Entity{ + ID: e.Id, + Type: e.Type, + Latitude: e.Location.Latitude, + Longitude: e.Location.Longitude, + Timestamp: e.Location.Timestamp, + } +} + +func Read(id string) (*Entity, error) { + mtx.RLock() + defer mtx.RUnlock() + + p := defaultIndex.Get(id) + if p == nil { + return nil, errors.NotFound("location.read", "Not found") + } + + entity, ok := p.(*Entity) + if !ok { + return nil, errors.InternalServerError("location.read", "Error reading entity") + } + + return entity, nil +} + +func Save(e *Entity) { + mtx.Lock() + defaultIndex.Add(e) + mtx.Unlock() +} + +func Search(typ string, entity *Entity, radius float64, numEntities int) []*Entity { + mtx.RLock() + defer mtx.RUnlock() + + points := defaultIndex.KNearest(entity, numEntities, geo.Meters(radius), func(p geo.Point) bool { + e, ok := p.(*Entity) + if !ok || e.Type != typ { + return false + } + return true + }) + + var entities []*Entity + + for _, point := range points { + e, ok := point.(*Entity) + if !ok { + continue + } + entities = append(entities, e) + } + + return entities +} diff --git a/location/generate.go b/location/generate.go new file mode 100644 index 0000000..7d9db91 --- /dev/null +++ b/location/generate.go @@ -0,0 +1,3 @@ +package main + +//go:generate make proto diff --git a/location/handler/handler.go b/location/handler/handler.go new file mode 100644 index 0000000..aff4bdf --- /dev/null +++ b/location/handler/handler.go @@ -0,0 +1,67 @@ +package handler + +import ( + "context" + "log" + + "github.com/micro/micro/v3/service" + "github.com/micro/micro/v3/service/errors" + "github.com/micro/services/location/domain" + loc "github.com/micro/services/location/proto" + "github.com/micro/services/location/subscriber" +) + +type Location struct{} + +func (l *Location) Read(ctx context.Context, req *loc.ReadRequest, rsp *loc.ReadResponse) error { + log.Print("Received Location.Read request") + + id := req.Id + + if len(id) == 0 { + return errors.BadRequest("location.read", "Require Id") + } + + entity, err := domain.Read(id) + if err != nil { + return err + } + + rsp.Entity = entity.ToProto() + + return nil +} + +func (l *Location) Save(ctx context.Context, req *loc.SaveRequest, rsp *loc.SaveResponse) error { + log.Print("Received Location.Save request") + + entity := req.GetEntity() + + if entity.GetLocation() == nil { + return errors.BadRequest("location.save", "Require location") + } + + p := service.NewEvent(subscriber.Topic) + if err := p.Publish(ctx, entity); err != nil { + return errors.InternalServerError("location.save", err.Error()) + } + + return nil +} + +func (l *Location) Search(ctx context.Context, req *loc.SearchRequest, rsp *loc.SearchResponse) error { + log.Print("Received Location.Search request") + + entity := &domain.Entity{ + Latitude: req.Center.Latitude, + Longitude: req.Center.Longitude, + } + + entities := domain.Search(req.Type, entity, req.Radius, int(req.NumEntities)) + + for _, e := range entities { + rsp.Entities = append(rsp.Entities, e.ToProto()) + } + + return nil +} diff --git a/location/main.go b/location/main.go new file mode 100644 index 0000000..d4f6756 --- /dev/null +++ b/location/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "log" + + "github.com/micro/micro/v3/service" + "github.com/micro/services/location/handler" + pb "github.com/micro/services/location/proto" + "github.com/micro/services/location/subscriber" +) + +func main() { + location := service.New( + service.Name("location"), + ) + + pb.RegisterLocationHandler(location.Server(), new(handler.Location)) + + service.Subscribe(subscriber.Topic, new(subscriber.Location)) + + if err := location.Run(); err != nil { + log.Fatal(err) + } +} diff --git a/location/micro.mu b/location/micro.mu new file mode 100644 index 0000000..a77bf2f --- /dev/null +++ b/location/micro.mu @@ -0,0 +1 @@ +service location diff --git a/location/proto/location.pb.go b/location/proto/location.pb.go new file mode 100644 index 0000000..ffbe67f --- /dev/null +++ b/location/proto/location.pb.go @@ -0,0 +1,421 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: proto/location.proto + +package location + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// 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 + +type Point struct { + Latitude float64 `protobuf:"fixed64,1,opt,name=latitude,proto3" json:"latitude,omitempty"` + Longitude float64 `protobuf:"fixed64,2,opt,name=longitude,proto3" json:"longitude,omitempty"` + Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Point) Reset() { *m = Point{} } +func (m *Point) String() string { return proto.CompactTextString(m) } +func (*Point) ProtoMessage() {} +func (*Point) Descriptor() ([]byte, []int) { + return fileDescriptor_1d9f8c6d814f264b, []int{0} +} + +func (m *Point) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Point.Unmarshal(m, b) +} +func (m *Point) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Point.Marshal(b, m, deterministic) +} +func (m *Point) XXX_Merge(src proto.Message) { + xxx_messageInfo_Point.Merge(m, src) +} +func (m *Point) XXX_Size() int { + return xxx_messageInfo_Point.Size(m) +} +func (m *Point) XXX_DiscardUnknown() { + xxx_messageInfo_Point.DiscardUnknown(m) +} + +var xxx_messageInfo_Point proto.InternalMessageInfo + +func (m *Point) GetLatitude() float64 { + if m != nil { + return m.Latitude + } + return 0 +} + +func (m *Point) GetLongitude() float64 { + if m != nil { + return m.Longitude + } + return 0 +} + +func (m *Point) GetTimestamp() int64 { + if m != nil { + return m.Timestamp + } + return 0 +} + +type Entity struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` + Location *Point `protobuf:"bytes,3,opt,name=location,proto3" json:"location,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Entity) Reset() { *m = Entity{} } +func (m *Entity) String() string { return proto.CompactTextString(m) } +func (*Entity) ProtoMessage() {} +func (*Entity) Descriptor() ([]byte, []int) { + return fileDescriptor_1d9f8c6d814f264b, []int{1} +} + +func (m *Entity) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Entity.Unmarshal(m, b) +} +func (m *Entity) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Entity.Marshal(b, m, deterministic) +} +func (m *Entity) XXX_Merge(src proto.Message) { + xxx_messageInfo_Entity.Merge(m, src) +} +func (m *Entity) XXX_Size() int { + return xxx_messageInfo_Entity.Size(m) +} +func (m *Entity) XXX_DiscardUnknown() { + xxx_messageInfo_Entity.DiscardUnknown(m) +} + +var xxx_messageInfo_Entity proto.InternalMessageInfo + +func (m *Entity) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *Entity) GetType() string { + if m != nil { + return m.Type + } + return "" +} + +func (m *Entity) GetLocation() *Point { + if m != nil { + return m.Location + } + return nil +} + +type ReadRequest struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ReadRequest) Reset() { *m = ReadRequest{} } +func (m *ReadRequest) String() string { return proto.CompactTextString(m) } +func (*ReadRequest) ProtoMessage() {} +func (*ReadRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_1d9f8c6d814f264b, []int{2} +} + +func (m *ReadRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ReadRequest.Unmarshal(m, b) +} +func (m *ReadRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ReadRequest.Marshal(b, m, deterministic) +} +func (m *ReadRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReadRequest.Merge(m, src) +} +func (m *ReadRequest) XXX_Size() int { + return xxx_messageInfo_ReadRequest.Size(m) +} +func (m *ReadRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ReadRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ReadRequest proto.InternalMessageInfo + +func (m *ReadRequest) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +type ReadResponse struct { + Entity *Entity `protobuf:"bytes,1,opt,name=entity,proto3" json:"entity,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ReadResponse) Reset() { *m = ReadResponse{} } +func (m *ReadResponse) String() string { return proto.CompactTextString(m) } +func (*ReadResponse) ProtoMessage() {} +func (*ReadResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1d9f8c6d814f264b, []int{3} +} + +func (m *ReadResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ReadResponse.Unmarshal(m, b) +} +func (m *ReadResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ReadResponse.Marshal(b, m, deterministic) +} +func (m *ReadResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ReadResponse.Merge(m, src) +} +func (m *ReadResponse) XXX_Size() int { + return xxx_messageInfo_ReadResponse.Size(m) +} +func (m *ReadResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ReadResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ReadResponse proto.InternalMessageInfo + +func (m *ReadResponse) GetEntity() *Entity { + if m != nil { + return m.Entity + } + return nil +} + +type SaveRequest struct { + Entity *Entity `protobuf:"bytes,1,opt,name=entity,proto3" json:"entity,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SaveRequest) Reset() { *m = SaveRequest{} } +func (m *SaveRequest) String() string { return proto.CompactTextString(m) } +func (*SaveRequest) ProtoMessage() {} +func (*SaveRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_1d9f8c6d814f264b, []int{4} +} + +func (m *SaveRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SaveRequest.Unmarshal(m, b) +} +func (m *SaveRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SaveRequest.Marshal(b, m, deterministic) +} +func (m *SaveRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SaveRequest.Merge(m, src) +} +func (m *SaveRequest) XXX_Size() int { + return xxx_messageInfo_SaveRequest.Size(m) +} +func (m *SaveRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SaveRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SaveRequest proto.InternalMessageInfo + +func (m *SaveRequest) GetEntity() *Entity { + if m != nil { + return m.Entity + } + return nil +} + +type SaveResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SaveResponse) Reset() { *m = SaveResponse{} } +func (m *SaveResponse) String() string { return proto.CompactTextString(m) } +func (*SaveResponse) ProtoMessage() {} +func (*SaveResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1d9f8c6d814f264b, []int{5} +} + +func (m *SaveResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SaveResponse.Unmarshal(m, b) +} +func (m *SaveResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SaveResponse.Marshal(b, m, deterministic) +} +func (m *SaveResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SaveResponse.Merge(m, src) +} +func (m *SaveResponse) XXX_Size() int { + return xxx_messageInfo_SaveResponse.Size(m) +} +func (m *SaveResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SaveResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SaveResponse proto.InternalMessageInfo + +type SearchRequest struct { + Center *Point `protobuf:"bytes,1,opt,name=center,proto3" json:"center,omitempty"` + Radius float64 `protobuf:"fixed64,2,opt,name=radius,proto3" json:"radius,omitempty"` + Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` + NumEntities int64 `protobuf:"varint,4,opt,name=numEntities,proto3" json:"numEntities,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SearchRequest) Reset() { *m = SearchRequest{} } +func (m *SearchRequest) String() string { return proto.CompactTextString(m) } +func (*SearchRequest) ProtoMessage() {} +func (*SearchRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_1d9f8c6d814f264b, []int{6} +} + +func (m *SearchRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SearchRequest.Unmarshal(m, b) +} +func (m *SearchRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SearchRequest.Marshal(b, m, deterministic) +} +func (m *SearchRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SearchRequest.Merge(m, src) +} +func (m *SearchRequest) XXX_Size() int { + return xxx_messageInfo_SearchRequest.Size(m) +} +func (m *SearchRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SearchRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SearchRequest proto.InternalMessageInfo + +func (m *SearchRequest) GetCenter() *Point { + if m != nil { + return m.Center + } + return nil +} + +func (m *SearchRequest) GetRadius() float64 { + if m != nil { + return m.Radius + } + return 0 +} + +func (m *SearchRequest) GetType() string { + if m != nil { + return m.Type + } + return "" +} + +func (m *SearchRequest) GetNumEntities() int64 { + if m != nil { + return m.NumEntities + } + return 0 +} + +type SearchResponse struct { + Entities []*Entity `protobuf:"bytes,1,rep,name=entities,proto3" json:"entities,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SearchResponse) Reset() { *m = SearchResponse{} } +func (m *SearchResponse) String() string { return proto.CompactTextString(m) } +func (*SearchResponse) ProtoMessage() {} +func (*SearchResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_1d9f8c6d814f264b, []int{7} +} + +func (m *SearchResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SearchResponse.Unmarshal(m, b) +} +func (m *SearchResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SearchResponse.Marshal(b, m, deterministic) +} +func (m *SearchResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SearchResponse.Merge(m, src) +} +func (m *SearchResponse) XXX_Size() int { + return xxx_messageInfo_SearchResponse.Size(m) +} +func (m *SearchResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SearchResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SearchResponse proto.InternalMessageInfo + +func (m *SearchResponse) GetEntities() []*Entity { + if m != nil { + return m.Entities + } + return nil +} + +func init() { + proto.RegisterType((*Point)(nil), "location.Point") + proto.RegisterType((*Entity)(nil), "location.Entity") + proto.RegisterType((*ReadRequest)(nil), "location.ReadRequest") + proto.RegisterType((*ReadResponse)(nil), "location.ReadResponse") + proto.RegisterType((*SaveRequest)(nil), "location.SaveRequest") + proto.RegisterType((*SaveResponse)(nil), "location.SaveResponse") + proto.RegisterType((*SearchRequest)(nil), "location.SearchRequest") + proto.RegisterType((*SearchResponse)(nil), "location.SearchResponse") +} + +func init() { proto.RegisterFile("proto/location.proto", fileDescriptor_1d9f8c6d814f264b) } + +var fileDescriptor_1d9f8c6d814f264b = []byte{ + // 366 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xb1, 0x4e, 0xf3, 0x30, + 0x14, 0x85, 0xeb, 0xa6, 0x7f, 0x94, 0xde, 0xf4, 0x2f, 0xc8, 0x82, 0x12, 0x55, 0x20, 0x45, 0x59, + 0xa8, 0x04, 0x2a, 0x52, 0x19, 0xca, 0x02, 0x1b, 0x1b, 0x03, 0x72, 0x27, 0x26, 0x64, 0x1a, 0x0b, + 0x2c, 0x35, 0x76, 0x48, 0x1c, 0xa4, 0xbe, 0x00, 0x2f, 0xc5, 0xcb, 0xa1, 0xd8, 0x4e, 0x62, 0x5a, + 0x06, 0xb6, 0xde, 0x7b, 0x7c, 0x4e, 0x8f, 0x3f, 0x07, 0x8e, 0xf2, 0x42, 0x2a, 0x79, 0xb5, 0x91, + 0x6b, 0xaa, 0xb8, 0x14, 0x73, 0x3d, 0xe2, 0xa0, 0x99, 0x93, 0x67, 0xf8, 0xf7, 0x28, 0xb9, 0x50, + 0x78, 0x0a, 0xc1, 0x86, 0x2a, 0xae, 0xaa, 0x94, 0x45, 0x28, 0x46, 0x33, 0x44, 0xda, 0x19, 0x9f, + 0xc2, 0x70, 0x23, 0xc5, 0xab, 0x11, 0xfb, 0x5a, 0xec, 0x16, 0xb5, 0xaa, 0x78, 0xc6, 0x4a, 0x45, + 0xb3, 0x3c, 0xf2, 0x62, 0x34, 0xf3, 0x48, 0xb7, 0x48, 0x9e, 0xc0, 0xbf, 0x17, 0x8a, 0xab, 0x2d, + 0x1e, 0x43, 0x9f, 0xa7, 0x3a, 0x7b, 0x48, 0xfa, 0x3c, 0xc5, 0x18, 0x06, 0x6a, 0x9b, 0x9b, 0xc0, + 0x21, 0xd1, 0xbf, 0xf1, 0x05, 0xb4, 0xd5, 0x74, 0x54, 0xb8, 0x38, 0x98, 0xb7, 0xdd, 0x75, 0x51, + 0xd2, 0x75, 0x3f, 0x83, 0x90, 0x30, 0x9a, 0x12, 0xf6, 0x5e, 0xb1, 0x52, 0xed, 0xe6, 0x27, 0x37, + 0x30, 0x32, 0x72, 0x99, 0x4b, 0x51, 0x32, 0x3c, 0x03, 0x9f, 0xe9, 0x26, 0xfa, 0x4c, 0xb8, 0x38, + 0xec, 0x92, 0x4d, 0x43, 0x62, 0xf5, 0x64, 0x09, 0xe1, 0x8a, 0x7e, 0xb0, 0x26, 0xf8, 0xef, 0xc6, + 0x31, 0x8c, 0x8c, 0xd1, 0xfc, 0x65, 0xf2, 0x89, 0xe0, 0xff, 0x8a, 0xd1, 0x62, 0xfd, 0xd6, 0x64, + 0x9d, 0x83, 0xbf, 0x66, 0x42, 0xb1, 0xc2, 0x66, 0xed, 0x5d, 0xcf, 0xca, 0x78, 0x02, 0x7e, 0x41, + 0x53, 0x5e, 0x95, 0x16, 0xb8, 0x9d, 0x5a, 0x6a, 0x9e, 0x43, 0x2d, 0x86, 0x50, 0x54, 0x99, 0xee, + 0xc2, 0x59, 0x19, 0x0d, 0xf4, 0x1b, 0xb8, 0xab, 0xe4, 0x0e, 0xc6, 0x4d, 0x0f, 0x4b, 0xe3, 0x12, + 0x02, 0xd6, 0x18, 0x50, 0xec, 0xfd, 0x7a, 0xad, 0xf6, 0xc4, 0xe2, 0x0b, 0x41, 0xf0, 0x60, 0x55, + 0xbc, 0x84, 0x41, 0x0d, 0x16, 0x1f, 0x77, 0x06, 0xe7, 0x1d, 0xa6, 0x93, 0xdd, 0xb5, 0x85, 0xd1, + 0xab, 0x8d, 0x35, 0x1e, 0xd7, 0xe8, 0x70, 0x76, 0x8d, 0x3f, 0x28, 0xf6, 0xf0, 0x2d, 0xf8, 0xa6, + 0x3e, 0x3e, 0x71, 0xce, 0xb8, 0x60, 0xa7, 0xd1, 0xbe, 0xd0, 0xd8, 0x5f, 0x7c, 0xfd, 0xd5, 0x5f, + 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0x84, 0x35, 0x27, 0xbf, 0x0d, 0x03, 0x00, 0x00, +} diff --git a/location/proto/location.pb.micro.go b/location/proto/location.pb.micro.go new file mode 100644 index 0000000..06efedd --- /dev/null +++ b/location/proto/location.pb.micro.go @@ -0,0 +1,127 @@ +// Code generated by protoc-gen-micro. DO NOT EDIT. +// source: proto/location.proto + +package location + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + 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 Location service + +func NewLocationEndpoints() []*api.Endpoint { + return []*api.Endpoint{} +} + +// Client API for Location service + +type LocationService interface { + Read(ctx context.Context, in *ReadRequest, opts ...client.CallOption) (*ReadResponse, error) + Save(ctx context.Context, in *SaveRequest, opts ...client.CallOption) (*SaveResponse, error) + Search(ctx context.Context, in *SearchRequest, opts ...client.CallOption) (*SearchResponse, error) +} + +type locationService struct { + c client.Client + name string +} + +func NewLocationService(name string, c client.Client) LocationService { + return &locationService{ + c: c, + name: name, + } +} + +func (c *locationService) Read(ctx context.Context, in *ReadRequest, opts ...client.CallOption) (*ReadResponse, error) { + req := c.c.NewRequest(c.name, "Location.Read", in) + out := new(ReadResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *locationService) Save(ctx context.Context, in *SaveRequest, opts ...client.CallOption) (*SaveResponse, error) { + req := c.c.NewRequest(c.name, "Location.Save", in) + out := new(SaveResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *locationService) Search(ctx context.Context, in *SearchRequest, opts ...client.CallOption) (*SearchResponse, error) { + req := c.c.NewRequest(c.name, "Location.Search", in) + out := new(SearchResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for Location service + +type LocationHandler interface { + Read(context.Context, *ReadRequest, *ReadResponse) error + Save(context.Context, *SaveRequest, *SaveResponse) error + Search(context.Context, *SearchRequest, *SearchResponse) error +} + +func RegisterLocationHandler(s server.Server, hdlr LocationHandler, opts ...server.HandlerOption) error { + type location interface { + Read(ctx context.Context, in *ReadRequest, out *ReadResponse) error + Save(ctx context.Context, in *SaveRequest, out *SaveResponse) error + Search(ctx context.Context, in *SearchRequest, out *SearchResponse) error + } + type Location struct { + location + } + h := &locationHandler{hdlr} + return s.Handle(s.NewHandler(&Location{h}, opts...)) +} + +type locationHandler struct { + LocationHandler +} + +func (h *locationHandler) Read(ctx context.Context, in *ReadRequest, out *ReadResponse) error { + return h.LocationHandler.Read(ctx, in, out) +} + +func (h *locationHandler) Save(ctx context.Context, in *SaveRequest, out *SaveResponse) error { + return h.LocationHandler.Save(ctx, in, out) +} + +func (h *locationHandler) Search(ctx context.Context, in *SearchRequest, out *SearchResponse) error { + return h.LocationHandler.Search(ctx, in, out) +} diff --git a/location/proto/location.proto b/location/proto/location.proto new file mode 100644 index 0000000..392aa0b --- /dev/null +++ b/location/proto/location.proto @@ -0,0 +1,48 @@ +syntax = "proto3"; + +package location; + +service Location { + rpc Read(ReadRequest) returns (ReadResponse) {} + rpc Save(SaveRequest) returns (SaveResponse) {} + rpc Search(SearchRequest) returns (SearchResponse) {} +} + + +message Point { + double latitude = 1; + double longitude = 2; + int64 timestamp = 3; +} + +message Entity { + string id = 1; + string type = 2; + Point location = 3; +} + +message ReadRequest { + string id = 1; +} + +message ReadResponse { + Entity entity = 1; +} + +message SaveRequest { + Entity entity = 1; +} + +message SaveResponse { +} + +message SearchRequest { + Point center = 1; + double radius = 2; // in meters + string type = 3; + int64 numEntities = 4; +} + +message SearchResponse { + repeated Entity entities = 1; +} diff --git a/location/subscriber/subscriber.go b/location/subscriber/subscriber.go new file mode 100644 index 0000000..dc14a7a --- /dev/null +++ b/location/subscriber/subscriber.go @@ -0,0 +1,21 @@ +package subscriber + +import ( + "context" + "log" + + "github.com/micro/services/location/domain" + proto "github.com/micro/services/location/proto" +) + +var ( + Topic = "location" +) + +type Location struct{} + +func (g *Location) Handle(ctx context.Context, e *proto.Entity) error { + log.Printf("Saving entity ID %s", e.Id) + domain.Save(domain.ProtoToEntity(e)) + return nil +}