From ae4711da2d39d7d0e680ccd804836329042cbb10 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Wed, 23 Jun 2021 11:15:05 +0100 Subject: [PATCH] add the forecast endpoint --- weather/generate.go | 1 + weather/handler/weather.go | 159 ++++++--- weather/main.go | 4 +- weather/proto/weather.pb.go | 544 ++++++++++++++++++++++++++---- weather/proto/weather.pb.micro.go | 17 + weather/proto/weather.proto | 57 ++++ 6 files changed, 673 insertions(+), 109 deletions(-) diff --git a/weather/generate.go b/weather/generate.go index 96f431a..7d9db91 100644 --- a/weather/generate.go +++ b/weather/generate.go @@ -1,2 +1,3 @@ package main + //go:generate make proto diff --git a/weather/handler/weather.go b/weather/handler/weather.go index f2282ee..2255dec 100644 --- a/weather/handler/weather.go +++ b/weather/handler/weather.go @@ -3,76 +3,150 @@ package handler import ( "context" "encoding/json" + "fmt" "io/ioutil" "net/http" "net/url" + "strconv" - "github.com/micro/micro/v3/service/logger" "github.com/micro/micro/v3/service/config" "github.com/micro/micro/v3/service/errors" + "github.com/micro/micro/v3/service/logger" pb "github.com/micro/services/weather/proto" ) -type Weather struct{ +type Weather struct { Api string Key string } func New() *Weather { - // TODO: look for "weather.provider" to determine the handler - v, err := config.Get("weatherapi.api") - if err != nil { - logger.Fatalf("weatherapi.api config not found: %v", err) - } - api := v.String("") - if len(api) == 0 { - logger.Fatal("weatherapi.api config not found") - } - v, err = config.Get("weatherapi.key") - if err != nil { - logger.Fatalf("weatherapi.key config not found: %v", err) - } - key := v.String("") - if len(key) == 0 { - logger.Fatal("weatherapi.key config not found") - } + // TODO: look for "weather.provider" to determine the handler + v, err := config.Get("weatherapi.api") + if err != nil { + logger.Fatalf("weatherapi.api config not found: %v", err) + } + api := v.String("") + if len(api) == 0 { + logger.Fatal("weatherapi.api config not found") + } + v, err = config.Get("weatherapi.key") + if err != nil { + logger.Fatalf("weatherapi.key config not found: %v", err) + } + key := v.String("") + if len(key) == 0 { + logger.Fatal("weatherapi.key config not found") + } - return &Weather{ - Api: api, - Key: key, - } + return &Weather{ + Api: api, + Key: key, + } +} + +func (w *Weather) Forecast(ctx context.Context, req *pb.ForecastRequest, rsp *pb.ForecastResponse) error { + if len(req.Location) <= 0 { + return errors.BadRequest("weather.forecast", "invalid location") + } + if req.Days <= 0 || req.Days > 10 { + req.Days = 1 + } + + vals := url.Values{} + vals.Set("key", w.Key) + vals.Set("aqi", "no") + vals.Set("q", req.Location) + vals.Set("days", fmt.Sprintf("%d", req.Days)) + + resp, err := http.Get(w.Api + "forecast.json?" + vals.Encode()) + if err != nil { + logger.Errorf("Failed to get weather forecast: %v\n", err) + return errors.InternalServerError("weather.forecast", "failed to get weather forecast") + } + defer resp.Body.Close() + + b, _ := ioutil.ReadAll(resp.Body) + + if resp.StatusCode != 200 { + logger.Errorf("Failed to get weather forecast (non 200): %d %v\n", resp.StatusCode, string(b)) + return errors.InternalServerError("weather.forecast", "failed to get weather forecast") + } + + var respBody map[string]interface{} + + if err := json.Unmarshal(b, &respBody); err != nil { + logger.Errorf("Failed to unmarshal forecast: %v\n", err) + return errors.InternalServerError("weather.forecast", "failed to get forecast") + } + + forecast := respBody["forecast"].(map[string]interface{}) + + for _, v := range forecast["forecastday"].([]interface{}) { + fc := v.(map[string]interface{}) + + day := fc["day"].(map[string]interface{}) + + willrain := false + chancerain := int32(0) + if v := day["daily_will_it_rain"].(float64); v == 1.0 { + willrain = true + } + if v, _ := strconv.Atoi(day["daily_chance_of_rain"].(string)); v > 0 { + chancerain = int32(v) + } + + // set the daily forecast + rsp.Forecast = append(rsp.Forecast, &pb.Forecast{ + Date: fc["date"].(string), + MaxTempC: day["maxtemp_c"].(float64), + MinTempC: day["mintemp_c"].(float64), + AvgTempC: day["avgtemp_c"].(float64), + MaxTempF: day["maxtemp_f"].(float64), + MinTempF: day["mintemp_f"].(float64), + AvgTempF: day["avgtemp_f"].(float64), + WillItRain: willrain, + ChanceOfRain: chancerain, + Condition: day["condition"].(map[string]interface{})["text"].(string), + IconUrl: day["condition"].(map[string]interface{})["icon"].(string), + Sunrise: fc["astro"].(map[string]interface{})["sunrise"].(string), + Sunset: fc["astro"].(map[string]interface{})["sunset"].(string), + }) + } + + return nil } func (w *Weather) Now(ctx context.Context, req *pb.NowRequest, rsp *pb.NowResponse) error { - if len(req.Location) <= 0 { - return errors.BadRequest("weather.current", "invalid location") - } + if len(req.Location) <= 0 { + return errors.BadRequest("weather.current", "invalid location") + } vals := url.Values{} vals.Set("key", w.Key) vals.Set("aqi", "no") vals.Set("q", req.Location) - resp, err := http.Get(w.Api + "current.json?" + vals.Encode()) - if err != nil { - logger.Errorf("Failed to get current weather: %v\n", err) - return errors.InternalServerError("weather.current", "failed to get current weather") - } - defer resp.Body.Close() + resp, err := http.Get(w.Api + "current.json?" + vals.Encode()) + if err != nil { + logger.Errorf("Failed to get current weather: %v\n", err) + return errors.InternalServerError("weather.current", "failed to get current weather") + } + defer resp.Body.Close() - b, _ := ioutil.ReadAll(resp.Body) + b, _ := ioutil.ReadAll(resp.Body) - if resp.StatusCode != 200 { - logger.Errorf("Failed to get current weather (non 200): %d %v\n", resp.StatusCode, string(b)) - return errors.InternalServerError("weather.current", "failed to get current weather") - } + if resp.StatusCode != 200 { + logger.Errorf("Failed to get current weather (non 200): %d %v\n", resp.StatusCode, string(b)) + return errors.InternalServerError("weather.current", "failed to get current weather") + } - var respBody map[string]interface{} + var respBody map[string]interface{} - if err := json.Unmarshal(b, &respBody); err != nil { - logger.Errorf("Failed to unmarshal current: %v\n", err) - return errors.InternalServerError("weather.current", "failed to get current") - } + if err := json.Unmarshal(b, &respBody); err != nil { + logger.Errorf("Failed to unmarshal current: %v\n", err) + return errors.InternalServerError("weather.current", "failed to get current") + } location := respBody["location"].(map[string]interface{}) current := respBody["current"].(map[string]interface{}) @@ -106,4 +180,3 @@ func (w *Weather) Now(ctx context.Context, req *pb.NowRequest, rsp *pb.NowRespon return nil } - diff --git a/weather/main.go b/weather/main.go index 6efd116..e25cc09 100644 --- a/weather/main.go +++ b/weather/main.go @@ -1,10 +1,10 @@ package main import ( - "github.com/micro/services/weather/handler" - pb "github.com/micro/services/weather/proto" "github.com/micro/micro/v3/service" "github.com/micro/micro/v3/service/logger" + "github.com/micro/services/weather/handler" + pb "github.com/micro/services/weather/proto" ) func main() { diff --git a/weather/proto/weather.pb.go b/weather/proto/weather.pb.go index 4a57e65..599f088 100644 --- a/weather/proto/weather.pb.go +++ b/weather/proto/weather.pb.go @@ -20,6 +20,331 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type Forecast struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // date of the forecast + Date string `protobuf:"bytes,1,opt,name=date,proto3" json:"date,omitempty"` + // max temp in celsius + MaxTempC float64 `protobuf:"fixed64,2,opt,name=max_temp_c,json=maxTempC,proto3" json:"max_temp_c,omitempty"` + // max temp in fahrenheit + MaxTempF float64 `protobuf:"fixed64,3,opt,name=max_temp_f,json=maxTempF,proto3" json:"max_temp_f,omitempty"` + // minimum temp in celsius + MinTempC float64 `protobuf:"fixed64,4,opt,name=min_temp_c,json=minTempC,proto3" json:"min_temp_c,omitempty"` + // minimum temp in fahrenheit + MinTempF float64 `protobuf:"fixed64,5,opt,name=min_temp_f,json=minTempF,proto3" json:"min_temp_f,omitempty"` + // the average temp in celsius + AvgTempC float64 `protobuf:"fixed64,6,opt,name=avg_temp_c,json=avgTempC,proto3" json:"avg_temp_c,omitempty"` + // the average temp in fahrenheit + AvgTempF float64 `protobuf:"fixed64,7,opt,name=avg_temp_f,json=avgTempF,proto3" json:"avg_temp_f,omitempty"` + // will it rain + WillItRain bool `protobuf:"varint,8,opt,name=will_it_rain,json=willItRain,proto3" json:"will_it_rain,omitempty"` + // chance of rain (percentage) + ChanceOfRain int32 `protobuf:"varint,9,opt,name=chance_of_rain,json=chanceOfRain,proto3" json:"chance_of_rain,omitempty"` + // forecast condition + Condition string `protobuf:"bytes,10,opt,name=condition,proto3" json:"condition,omitempty"` + // forecast condition icon + IconUrl string `protobuf:"bytes,11,opt,name=icon_url,json=iconUrl,proto3" json:"icon_url,omitempty"` + // time of sunrise + Sunrise string `protobuf:"bytes,12,opt,name=sunrise,proto3" json:"sunrise,omitempty"` + // time of sunset + Sunset string `protobuf:"bytes,13,opt,name=sunset,proto3" json:"sunset,omitempty"` +} + +func (x *Forecast) Reset() { + *x = Forecast{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_weather_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Forecast) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Forecast) ProtoMessage() {} + +func (x *Forecast) ProtoReflect() protoreflect.Message { + mi := &file_proto_weather_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 Forecast.ProtoReflect.Descriptor instead. +func (*Forecast) Descriptor() ([]byte, []int) { + return file_proto_weather_proto_rawDescGZIP(), []int{0} +} + +func (x *Forecast) GetDate() string { + if x != nil { + return x.Date + } + return "" +} + +func (x *Forecast) GetMaxTempC() float64 { + if x != nil { + return x.MaxTempC + } + return 0 +} + +func (x *Forecast) GetMaxTempF() float64 { + if x != nil { + return x.MaxTempF + } + return 0 +} + +func (x *Forecast) GetMinTempC() float64 { + if x != nil { + return x.MinTempC + } + return 0 +} + +func (x *Forecast) GetMinTempF() float64 { + if x != nil { + return x.MinTempF + } + return 0 +} + +func (x *Forecast) GetAvgTempC() float64 { + if x != nil { + return x.AvgTempC + } + return 0 +} + +func (x *Forecast) GetAvgTempF() float64 { + if x != nil { + return x.AvgTempF + } + return 0 +} + +func (x *Forecast) GetWillItRain() bool { + if x != nil { + return x.WillItRain + } + return false +} + +func (x *Forecast) GetChanceOfRain() int32 { + if x != nil { + return x.ChanceOfRain + } + return 0 +} + +func (x *Forecast) GetCondition() string { + if x != nil { + return x.Condition + } + return "" +} + +func (x *Forecast) GetIconUrl() string { + if x != nil { + return x.IconUrl + } + return "" +} + +func (x *Forecast) GetSunrise() string { + if x != nil { + return x.Sunrise + } + return "" +} + +func (x *Forecast) GetSunset() string { + if x != nil { + return x.Sunset + } + return "" +} + +// Get the weather forecast for the next 1-10 days +type ForecastRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // location of the forecase + Location string `protobuf:"bytes,1,opt,name=location,proto3" json:"location,omitempty"` + // number of days. default 1, max 10 + Days int32 `protobuf:"varint,2,opt,name=days,proto3" json:"days,omitempty"` +} + +func (x *ForecastRequest) Reset() { + *x = ForecastRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_weather_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ForecastRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ForecastRequest) ProtoMessage() {} + +func (x *ForecastRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_weather_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 ForecastRequest.ProtoReflect.Descriptor instead. +func (*ForecastRequest) Descriptor() ([]byte, []int) { + return file_proto_weather_proto_rawDescGZIP(), []int{1} +} + +func (x *ForecastRequest) GetLocation() string { + if x != nil { + return x.Location + } + return "" +} + +func (x *ForecastRequest) GetDays() int32 { + if x != nil { + return x.Days + } + return 0 +} + +type ForecastResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // location of the request + Location string `protobuf:"bytes,1,opt,name=location,proto3" json:"location,omitempty"` + // region related to the location + Region string `protobuf:"bytes,2,opt,name=region,proto3" json:"region,omitempty"` + // country of the request + Country string `protobuf:"bytes,3,opt,name=country,proto3" json:"country,omitempty"` + // e.g 37.55 + Latitude float64 `protobuf:"fixed64,4,opt,name=latitude,proto3" json:"latitude,omitempty"` + // e.g -77.46 + Longitude float64 `protobuf:"fixed64,5,opt,name=longitude,proto3" json:"longitude,omitempty"` + // timezone of the location + Timezone string `protobuf:"bytes,6,opt,name=timezone,proto3" json:"timezone,omitempty"` + // the local time + LocalTime string `protobuf:"bytes,7,opt,name=local_time,json=localTime,proto3" json:"local_time,omitempty"` + // forecase for the next number of days + Forecast []*Forecast `protobuf:"bytes,8,rep,name=forecast,proto3" json:"forecast,omitempty"` +} + +func (x *ForecastResponse) Reset() { + *x = ForecastResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_weather_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ForecastResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ForecastResponse) ProtoMessage() {} + +func (x *ForecastResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_weather_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 ForecastResponse.ProtoReflect.Descriptor instead. +func (*ForecastResponse) Descriptor() ([]byte, []int) { + return file_proto_weather_proto_rawDescGZIP(), []int{2} +} + +func (x *ForecastResponse) GetLocation() string { + if x != nil { + return x.Location + } + return "" +} + +func (x *ForecastResponse) GetRegion() string { + if x != nil { + return x.Region + } + return "" +} + +func (x *ForecastResponse) GetCountry() string { + if x != nil { + return x.Country + } + return "" +} + +func (x *ForecastResponse) GetLatitude() float64 { + if x != nil { + return x.Latitude + } + return 0 +} + +func (x *ForecastResponse) GetLongitude() float64 { + if x != nil { + return x.Longitude + } + return 0 +} + +func (x *ForecastResponse) GetTimezone() string { + if x != nil { + return x.Timezone + } + return "" +} + +func (x *ForecastResponse) GetLocalTime() string { + if x != nil { + return x.LocalTime + } + return "" +} + +func (x *ForecastResponse) GetForecast() []*Forecast { + if x != nil { + return x.Forecast + } + return nil +} + // Get the current weather report for a location by postcode, city, zip code, ip address type NowRequest struct { state protoimpl.MessageState @@ -33,7 +358,7 @@ type NowRequest struct { func (x *NowRequest) Reset() { *x = NowRequest{} if protoimpl.UnsafeEnabled { - mi := &file_proto_weather_proto_msgTypes[0] + mi := &file_proto_weather_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -46,7 +371,7 @@ func (x *NowRequest) String() string { func (*NowRequest) ProtoMessage() {} func (x *NowRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_weather_proto_msgTypes[0] + mi := &file_proto_weather_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -59,7 +384,7 @@ func (x *NowRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use NowRequest.ProtoReflect.Descriptor instead. func (*NowRequest) Descriptor() ([]byte, []int) { - return file_proto_weather_proto_rawDescGZIP(), []int{0} + return file_proto_weather_proto_rawDescGZIP(), []int{3} } func (x *NowRequest) GetLocation() string { @@ -119,7 +444,7 @@ type NowResponse struct { func (x *NowResponse) Reset() { *x = NowResponse{} if protoimpl.UnsafeEnabled { - mi := &file_proto_weather_proto_msgTypes[1] + mi := &file_proto_weather_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -132,7 +457,7 @@ func (x *NowResponse) String() string { func (*NowResponse) ProtoMessage() {} func (x *NowResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_weather_proto_msgTypes[1] + mi := &file_proto_weather_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -145,7 +470,7 @@ func (x *NowResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use NowResponse.ProtoReflect.Descriptor instead. func (*NowResponse) Descriptor() ([]byte, []int) { - return file_proto_weather_proto_rawDescGZIP(), []int{1} + return file_proto_weather_proto_rawDescGZIP(), []int{4} } func (x *NowResponse) GetLocation() string { @@ -292,52 +617,101 @@ var File_proto_weather_proto protoreflect.FileDescriptor var file_proto_weather_proto_rawDesc = []byte{ 0x0a, 0x13, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x22, 0x28, - 0x0a, 0x0a, 0x4e, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, - 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xc5, 0x04, 0x0a, 0x0b, 0x4e, 0x6f, 0x77, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, - 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, - 0x64, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, - 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x12, 0x1d, 0x0a, 0x0a, - 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x74, - 0x65, 0x6d, 0x70, 0x5f, 0x63, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x74, 0x65, 0x6d, - 0x70, 0x43, 0x12, 0x15, 0x0a, 0x06, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x66, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x01, 0x52, 0x05, 0x74, 0x65, 0x6d, 0x70, 0x46, 0x12, 0x20, 0x0a, 0x0c, 0x66, 0x65, 0x65, - 0x6c, 0x73, 0x5f, 0x6c, 0x69, 0x6b, 0x65, 0x5f, 0x63, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x0a, 0x66, 0x65, 0x65, 0x6c, 0x73, 0x4c, 0x69, 0x6b, 0x65, 0x43, 0x12, 0x20, 0x0a, 0x0c, 0x66, - 0x65, 0x65, 0x6c, 0x73, 0x5f, 0x6c, 0x69, 0x6b, 0x65, 0x5f, 0x66, 0x18, 0x0b, 0x20, 0x01, 0x28, - 0x01, 0x52, 0x0a, 0x66, 0x65, 0x65, 0x6c, 0x73, 0x4c, 0x69, 0x6b, 0x65, 0x46, 0x12, 0x1a, 0x0a, - 0x08, 0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x08, 0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6c, 0x6f, - 0x75, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x12, - 0x18, 0x0a, 0x07, 0x64, 0x61, 0x79, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x07, 0x64, 0x61, 0x79, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, - 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, - 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x63, 0x6f, 0x6e, 0x5f, - 0x75, 0x72, 0x6c, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x63, 0x6f, 0x6e, 0x55, - 0x72, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x6d, 0x70, 0x68, 0x18, 0x11, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x77, 0x69, 0x6e, 0x64, 0x4d, 0x70, 0x68, 0x12, 0x19, 0x0a, - 0x08, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x6b, 0x70, 0x68, 0x18, 0x12, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x07, 0x77, 0x69, 0x6e, 0x64, 0x4b, 0x70, 0x68, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x69, 0x6e, 0x64, - 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0d, 0x77, 0x69, 0x6e, 0x64, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x1f, 0x0a, 0x0b, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x67, 0x72, 0x65, 0x65, 0x18, 0x14, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x77, 0x69, 0x6e, 0x64, 0x44, 0x65, 0x67, 0x72, 0x65, 0x65, - 0x32, 0x3d, 0x0a, 0x07, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x12, 0x32, 0x0a, 0x03, 0x4e, - 0x6f, 0x77, 0x12, 0x13, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x4e, 0x6f, 0x77, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, - 0x72, 0x2e, 0x4e, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, - 0x11, 0x5a, 0x0f, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x77, 0x65, 0x61, 0x74, 0x68, - 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x22, 0x85, + 0x03, 0x0a, 0x08, 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, + 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x65, 0x12, + 0x1c, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x63, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x54, 0x65, 0x6d, 0x70, 0x43, 0x12, 0x1c, 0x0a, + 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x54, 0x65, 0x6d, 0x70, 0x46, 0x12, 0x1c, 0x0a, 0x0a, 0x6d, + 0x69, 0x6e, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x08, 0x6d, 0x69, 0x6e, 0x54, 0x65, 0x6d, 0x70, 0x43, 0x12, 0x1c, 0x0a, 0x0a, 0x6d, 0x69, 0x6e, + 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x66, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x6d, + 0x69, 0x6e, 0x54, 0x65, 0x6d, 0x70, 0x46, 0x12, 0x1c, 0x0a, 0x0a, 0x61, 0x76, 0x67, 0x5f, 0x74, + 0x65, 0x6d, 0x70, 0x5f, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x61, 0x76, 0x67, + 0x54, 0x65, 0x6d, 0x70, 0x43, 0x12, 0x1c, 0x0a, 0x0a, 0x61, 0x76, 0x67, 0x5f, 0x74, 0x65, 0x6d, + 0x70, 0x5f, 0x66, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x61, 0x76, 0x67, 0x54, 0x65, + 0x6d, 0x70, 0x46, 0x12, 0x20, 0x0a, 0x0c, 0x77, 0x69, 0x6c, 0x6c, 0x5f, 0x69, 0x74, 0x5f, 0x72, + 0x61, 0x69, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x77, 0x69, 0x6c, 0x6c, 0x49, + 0x74, 0x52, 0x61, 0x69, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x63, 0x68, 0x61, 0x6e, 0x63, 0x65, 0x5f, + 0x6f, 0x66, 0x5f, 0x72, 0x61, 0x69, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x63, + 0x68, 0x61, 0x6e, 0x63, 0x65, 0x4f, 0x66, 0x52, 0x61, 0x69, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x63, + 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x63, 0x6f, + 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x63, 0x6f, + 0x6e, 0x55, 0x72, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6e, 0x72, 0x69, 0x73, 0x65, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x6e, 0x72, 0x69, 0x73, 0x65, 0x12, 0x16, + 0x0a, 0x06, 0x73, 0x75, 0x6e, 0x73, 0x65, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x73, 0x75, 0x6e, 0x73, 0x65, 0x74, 0x22, 0x41, 0x0a, 0x0f, 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, + 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x79, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x04, 0x64, 0x61, 0x79, 0x73, 0x22, 0x84, 0x02, 0x0a, 0x10, 0x46, 0x6f, + 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, + 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, + 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1a, 0x0a, 0x08, + 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, + 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6c, 0x6f, 0x6e, 0x67, + 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x6c, 0x6f, 0x6e, + 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, + 0x6e, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, + 0x6e, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x54, 0x69, 0x6d, + 0x65, 0x12, 0x2d, 0x0a, 0x08, 0x66, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, 0x18, 0x08, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x46, 0x6f, + 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, 0x52, 0x08, 0x66, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, + 0x22, 0x28, 0x0a, 0x0a, 0x4e, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xc5, 0x04, 0x0a, 0x0b, 0x4e, + 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x18, + 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x74, 0x69, + 0x74, 0x75, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x6c, 0x61, 0x74, 0x69, + 0x74, 0x75, 0x64, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, + 0x64, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x12, 0x1d, + 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x15, 0x0a, + 0x06, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x63, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x74, + 0x65, 0x6d, 0x70, 0x43, 0x12, 0x15, 0x0a, 0x06, 0x74, 0x65, 0x6d, 0x70, 0x5f, 0x66, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x74, 0x65, 0x6d, 0x70, 0x46, 0x12, 0x20, 0x0a, 0x0c, 0x66, + 0x65, 0x65, 0x6c, 0x73, 0x5f, 0x6c, 0x69, 0x6b, 0x65, 0x5f, 0x63, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x0a, 0x66, 0x65, 0x65, 0x6c, 0x73, 0x4c, 0x69, 0x6b, 0x65, 0x43, 0x12, 0x20, 0x0a, + 0x0c, 0x66, 0x65, 0x65, 0x6c, 0x73, 0x5f, 0x6c, 0x69, 0x6b, 0x65, 0x5f, 0x66, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x0a, 0x66, 0x65, 0x65, 0x6c, 0x73, 0x4c, 0x69, 0x6b, 0x65, 0x46, 0x12, + 0x1a, 0x0a, 0x08, 0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x08, 0x68, 0x75, 0x6d, 0x69, 0x64, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x63, + 0x6c, 0x6f, 0x75, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x63, 0x6c, 0x6f, 0x75, + 0x64, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x61, 0x79, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0e, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x07, 0x64, 0x61, 0x79, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, + 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x63, 0x6f, + 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x63, 0x6f, + 0x6e, 0x55, 0x72, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x6d, 0x70, 0x68, + 0x18, 0x11, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x77, 0x69, 0x6e, 0x64, 0x4d, 0x70, 0x68, 0x12, + 0x19, 0x0a, 0x08, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x6b, 0x70, 0x68, 0x18, 0x12, 0x20, 0x01, 0x28, + 0x01, 0x52, 0x07, 0x77, 0x69, 0x6e, 0x64, 0x4b, 0x70, 0x68, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x69, + 0x6e, 0x64, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x13, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x77, 0x69, 0x6e, 0x64, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x67, 0x72, 0x65, 0x65, + 0x18, 0x14, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x77, 0x69, 0x6e, 0x64, 0x44, 0x65, 0x67, 0x72, + 0x65, 0x65, 0x32, 0x80, 0x01, 0x0a, 0x07, 0x57, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x12, 0x32, + 0x0a, 0x03, 0x4e, 0x6f, 0x77, 0x12, 0x13, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, + 0x4e, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x77, 0x65, 0x61, + 0x74, 0x68, 0x65, 0x72, 0x2e, 0x4e, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x41, 0x0a, 0x08, 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, 0x12, 0x18, + 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x77, 0x65, 0x61, 0x74, 0x68, + 0x65, 0x72, 0x2e, 0x46, 0x6f, 0x72, 0x65, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x11, 0x5a, 0x0f, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x3b, 0x77, 0x65, 0x61, 0x74, 0x68, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -352,19 +726,25 @@ func file_proto_weather_proto_rawDescGZIP() []byte { return file_proto_weather_proto_rawDescData } -var file_proto_weather_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_proto_weather_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_proto_weather_proto_goTypes = []interface{}{ - (*NowRequest)(nil), // 0: weather.NowRequest - (*NowResponse)(nil), // 1: weather.NowResponse + (*Forecast)(nil), // 0: weather.Forecast + (*ForecastRequest)(nil), // 1: weather.ForecastRequest + (*ForecastResponse)(nil), // 2: weather.ForecastResponse + (*NowRequest)(nil), // 3: weather.NowRequest + (*NowResponse)(nil), // 4: weather.NowResponse } var file_proto_weather_proto_depIdxs = []int32{ - 0, // 0: weather.Weather.Now:input_type -> weather.NowRequest - 1, // 1: weather.Weather.Now:output_type -> weather.NowResponse - 1, // [1:2] is the sub-list for method output_type - 0, // [0:1] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name + 0, // 0: weather.ForecastResponse.forecast:type_name -> weather.Forecast + 3, // 1: weather.Weather.Now:input_type -> weather.NowRequest + 1, // 2: weather.Weather.Forecast:input_type -> weather.ForecastRequest + 4, // 3: weather.Weather.Now:output_type -> weather.NowResponse + 2, // 4: weather.Weather.Forecast:output_type -> weather.ForecastResponse + 3, // [3:5] is the sub-list for method output_type + 1, // [1:3] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name } func init() { file_proto_weather_proto_init() } @@ -374,7 +754,7 @@ func file_proto_weather_proto_init() { } if !protoimpl.UnsafeEnabled { file_proto_weather_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NowRequest); i { + switch v := v.(*Forecast); i { case 0: return &v.state case 1: @@ -386,6 +766,42 @@ func file_proto_weather_proto_init() { } } file_proto_weather_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ForecastRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_weather_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ForecastResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_weather_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NowRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_weather_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NowResponse); i { case 0: return &v.state @@ -404,7 +820,7 @@ func file_proto_weather_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_weather_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 5, NumExtensions: 0, NumServices: 1, }, diff --git a/weather/proto/weather.pb.micro.go b/weather/proto/weather.pb.micro.go index 28b74df..903a19c 100644 --- a/weather/proto/weather.pb.micro.go +++ b/weather/proto/weather.pb.micro.go @@ -43,6 +43,7 @@ func NewWeatherEndpoints() []*api.Endpoint { type WeatherService interface { Now(ctx context.Context, in *NowRequest, opts ...client.CallOption) (*NowResponse, error) + Forecast(ctx context.Context, in *ForecastRequest, opts ...client.CallOption) (*ForecastResponse, error) } type weatherService struct { @@ -67,15 +68,27 @@ func (c *weatherService) Now(ctx context.Context, in *NowRequest, opts ...client return out, nil } +func (c *weatherService) Forecast(ctx context.Context, in *ForecastRequest, opts ...client.CallOption) (*ForecastResponse, error) { + req := c.c.NewRequest(c.name, "Weather.Forecast", in) + out := new(ForecastResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // Server API for Weather service type WeatherHandler interface { Now(context.Context, *NowRequest, *NowResponse) error + Forecast(context.Context, *ForecastRequest, *ForecastResponse) error } func RegisterWeatherHandler(s server.Server, hdlr WeatherHandler, opts ...server.HandlerOption) error { type weather interface { Now(ctx context.Context, in *NowRequest, out *NowResponse) error + Forecast(ctx context.Context, in *ForecastRequest, out *ForecastResponse) error } type Weather struct { weather @@ -91,3 +104,7 @@ type weatherHandler struct { func (h *weatherHandler) Now(ctx context.Context, in *NowRequest, out *NowResponse) error { return h.WeatherHandler.Now(ctx, in, out) } + +func (h *weatherHandler) Forecast(ctx context.Context, in *ForecastRequest, out *ForecastResponse) error { + return h.WeatherHandler.Forecast(ctx, in, out) +} diff --git a/weather/proto/weather.proto b/weather/proto/weather.proto index 34f3739..ba69a51 100644 --- a/weather/proto/weather.proto +++ b/weather/proto/weather.proto @@ -6,6 +6,63 @@ option go_package = "./proto;weather"; service Weather { rpc Now(NowRequest) returns (NowResponse) {} + rpc Forecast(ForecastRequest) returns (ForecastResponse) {} +} + +message Forecast { + // date of the forecast + string date = 1; + // max temp in celsius + double max_temp_c = 2; + // max temp in fahrenheit + double max_temp_f = 3; + // minimum temp in celsius + double min_temp_c = 4; + // minimum temp in fahrenheit + double min_temp_f = 5; + // the average temp in celsius + double avg_temp_c = 6; + // the average temp in fahrenheit + double avg_temp_f = 7; + // will it rain + bool will_it_rain = 8; + // chance of rain (percentage) + int32 chance_of_rain = 9; + // forecast condition + string condition = 10; + // forecast condition icon + string icon_url = 11; + // time of sunrise + string sunrise = 12; + // time of sunset + string sunset = 13; +} + +// Get the weather forecast for the next 1-10 days +message ForecastRequest { + // location of the forecase + string location = 1; + // number of days. default 1, max 10 + int32 days = 2; +} + +message ForecastResponse { + // location of the request + string location = 1; + // region related to the location + string region = 2; + // country of the request + string country = 3; + // e.g 37.55 + double latitude = 4; + // e.g -77.46 + double longitude = 5; + // timezone of the location + string timezone = 6; + // the local time + string local_time = 7; + // forecase for the next number of days + repeated Forecast forecast = 8; } // Get the current weather report for a location by postcode, city, zip code, ip address