proxying.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. package routing
  2. import (
  3. "context"
  4. "encoding/json"
  5. "net/http"
  6. "net/url"
  7. "time"
  8. "github.com/go-kit/kit/circuitbreaker"
  9. "github.com/go-kit/kit/endpoint"
  10. kithttp "github.com/go-kit/kit/transport/http"
  11. shipping "github.com/longjoy/micro-go-course/section19/cargo/model"
  12. )
  13. type proxyService struct {
  14. context.Context
  15. FetchRoutesEndpoint endpoint.Endpoint
  16. shipping.RoutingService
  17. }
  18. func (s proxyService) FetchRoutesForSpecification(rs shipping.RouteSpecification) []shipping.Itinerary {
  19. response, err := s.FetchRoutesEndpoint(s.Context, fetchRoutesRequest{
  20. From: string(rs.Origin),
  21. To: string(rs.Destination),
  22. })
  23. if err != nil {
  24. return []shipping.Itinerary{}
  25. }
  26. resp := response.(fetchRoutesResponse)
  27. var itineraries []shipping.Itinerary
  28. for _, r := range resp.Paths {
  29. var legs []shipping.Leg
  30. for _, e := range r.Edges {
  31. legs = append(legs, shipping.Leg{
  32. VoyageNumber: shipping.VoyageNumber(e.Voyage),
  33. LoadLocation: shipping.UNLocode(e.Origin),
  34. UnloadLocation: shipping.UNLocode(e.Destination),
  35. LoadTime: e.Departure,
  36. UnloadTime: e.Arrival,
  37. })
  38. }
  39. itineraries = append(itineraries, shipping.Itinerary{Legs: legs})
  40. }
  41. return itineraries
  42. }
  43. // ServiceMiddleware defines a middleware for a routing service.
  44. type ServiceMiddleware func(shipping.RoutingService) shipping.RoutingService
  45. // NewProxyingMiddleware returns a new instance of a proxying middleware.
  46. func NewProxyingMiddleware(ctx context.Context, proxyURL string) ServiceMiddleware {
  47. return func(next shipping.RoutingService) shipping.RoutingService {
  48. var e endpoint.Endpoint
  49. e = makeFetchRoutesEndpoint(ctx, proxyURL)
  50. e = circuitbreaker.Hystrix("fetch-routes")(e)
  51. return proxyService{ctx, e, next}
  52. }
  53. }
  54. type fetchRoutesRequest struct {
  55. From string
  56. To string
  57. }
  58. type fetchRoutesResponse struct {
  59. Paths []struct {
  60. Edges []struct {
  61. Origin string `json:"origin"`
  62. Destination string `json:"destination"`
  63. Voyage string `json:"voyage"`
  64. Departure time.Time `json:"departure"`
  65. Arrival time.Time `json:"arrival"`
  66. } `json:"edges"`
  67. } `json:"paths"`
  68. }
  69. func makeFetchRoutesEndpoint(ctx context.Context, instance string) endpoint.Endpoint {
  70. u, err := url.Parse(instance)
  71. if err != nil {
  72. panic(err)
  73. }
  74. if u.Path == "" {
  75. u.Path = "/paths"
  76. }
  77. return kithttp.NewClient(
  78. "GET", u,
  79. encodeFetchRoutesRequest,
  80. decodeFetchRoutesResponse,
  81. ).Endpoint()
  82. }
  83. func decodeFetchRoutesResponse(_ context.Context, resp *http.Response) (interface{}, error) {
  84. var response fetchRoutesResponse
  85. if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
  86. return nil, err
  87. }
  88. return response, nil
  89. }
  90. func encodeFetchRoutesRequest(_ context.Context, r *http.Request, request interface{}) error {
  91. req := request.(fetchRoutesRequest)
  92. vals := r.URL.Query()
  93. vals.Add("from", req.From)
  94. vals.Add("to", req.To)
  95. r.URL.RawQuery = vals.Encode()
  96. return nil
  97. }