OSDN Git Service

new repo
[bytom/vapor.git] / vendor / github.com / go-kit / kit / examples / shipping / tracking / service.go
1 // Package tracking provides the use-case of tracking a cargo. Used by views
2 // facing the end-user.
3 package tracking
4
5 import (
6         "errors"
7         "fmt"
8         "strings"
9         "time"
10
11         "github.com/go-kit/kit/examples/shipping/cargo"
12 )
13
14 // ErrInvalidArgument is returned when one or more arguments are invalid.
15 var ErrInvalidArgument = errors.New("invalid argument")
16
17 // Service is the interface that provides the basic Track method.
18 type Service interface {
19         // Track returns a cargo matching a tracking ID.
20         Track(id string) (Cargo, error)
21 }
22
23 type service struct {
24         cargos         cargo.Repository
25         handlingEvents cargo.HandlingEventRepository
26 }
27
28 func (s *service) Track(id string) (Cargo, error) {
29         if id == "" {
30                 return Cargo{}, ErrInvalidArgument
31         }
32         c, err := s.cargos.Find(cargo.TrackingID(id))
33         if err != nil {
34                 return Cargo{}, err
35         }
36         return assemble(c, s.handlingEvents), nil
37 }
38
39 // NewService returns a new instance of the default Service.
40 func NewService(cargos cargo.Repository, events cargo.HandlingEventRepository) Service {
41         return &service{
42                 cargos:         cargos,
43                 handlingEvents: events,
44         }
45 }
46
47 // Cargo is a read model for tracking views.
48 type Cargo struct {
49         TrackingID           string    `json:"tracking_id"`
50         StatusText           string    `json:"status_text"`
51         Origin               string    `json:"origin"`
52         Destination          string    `json:"destination"`
53         ETA                  time.Time `json:"eta"`
54         NextExpectedActivity string    `json:"next_expected_activity"`
55         ArrivalDeadline      time.Time `json:"arrival_deadline"`
56         Events               []Event   `json:"events"`
57 }
58
59 // Leg is a read model for booking views.
60 type Leg struct {
61         VoyageNumber string    `json:"voyage_number"`
62         From         string    `json:"from"`
63         To           string    `json:"to"`
64         LoadTime     time.Time `json:"load_time"`
65         UnloadTime   time.Time `json:"unload_time"`
66 }
67
68 // Event is a read model for tracking views.
69 type Event struct {
70         Description string `json:"description"`
71         Expected    bool   `json:"expected"`
72 }
73
74 func assemble(c *cargo.Cargo, events cargo.HandlingEventRepository) Cargo {
75         return Cargo{
76                 TrackingID:           string(c.TrackingID),
77                 Origin:               string(c.Origin),
78                 Destination:          string(c.RouteSpecification.Destination),
79                 ETA:                  c.Delivery.ETA,
80                 NextExpectedActivity: nextExpectedActivity(c),
81                 ArrivalDeadline:      c.RouteSpecification.ArrivalDeadline,
82                 StatusText:           assembleStatusText(c),
83                 Events:               assembleEvents(c, events),
84         }
85 }
86
87 func assembleLegs(c cargo.Cargo) []Leg {
88         var legs []Leg
89         for _, l := range c.Itinerary.Legs {
90                 legs = append(legs, Leg{
91                         VoyageNumber: string(l.VoyageNumber),
92                         From:         string(l.LoadLocation),
93                         To:           string(l.UnloadLocation),
94                         LoadTime:     l.LoadTime,
95                         UnloadTime:   l.UnloadTime,
96                 })
97         }
98         return legs
99 }
100
101 func nextExpectedActivity(c *cargo.Cargo) string {
102         a := c.Delivery.NextExpectedActivity
103         prefix := "Next expected activity is to"
104
105         switch a.Type {
106         case cargo.Load:
107                 return fmt.Sprintf("%s %s cargo onto voyage %s in %s.", prefix, strings.ToLower(a.Type.String()), a.VoyageNumber, a.Location)
108         case cargo.Unload:
109                 return fmt.Sprintf("%s %s cargo off of voyage %s in %s.", prefix, strings.ToLower(a.Type.String()), a.VoyageNumber, a.Location)
110         case cargo.NotHandled:
111                 return "There are currently no expected activities for this cargo."
112         }
113
114         return fmt.Sprintf("%s %s cargo in %s.", prefix, strings.ToLower(a.Type.String()), a.Location)
115 }
116
117 func assembleStatusText(c *cargo.Cargo) string {
118         switch c.Delivery.TransportStatus {
119         case cargo.NotReceived:
120                 return "Not received"
121         case cargo.InPort:
122                 return fmt.Sprintf("In port %s", c.Delivery.LastKnownLocation)
123         case cargo.OnboardCarrier:
124                 return fmt.Sprintf("Onboard voyage %s", c.Delivery.CurrentVoyage)
125         case cargo.Claimed:
126                 return "Claimed"
127         default:
128                 return "Unknown"
129         }
130 }
131
132 func assembleEvents(c *cargo.Cargo, handlingEvents cargo.HandlingEventRepository) []Event {
133         h := handlingEvents.QueryHandlingHistory(c.TrackingID)
134
135         var events []Event
136         for _, e := range h.HandlingEvents {
137                 var description string
138
139                 switch e.Activity.Type {
140                 case cargo.NotHandled:
141                         description = "Cargo has not yet been received."
142                 case cargo.Receive:
143                         description = fmt.Sprintf("Received in %s, at %s", e.Activity.Location, time.Now().Format(time.RFC3339))
144                 case cargo.Load:
145                         description = fmt.Sprintf("Loaded onto voyage %s in %s, at %s.", e.Activity.VoyageNumber, e.Activity.Location, time.Now().Format(time.RFC3339))
146                 case cargo.Unload:
147                         description = fmt.Sprintf("Unloaded off voyage %s in %s, at %s.", e.Activity.VoyageNumber, e.Activity.Location, time.Now().Format(time.RFC3339))
148                 case cargo.Claim:
149                         description = fmt.Sprintf("Claimed in %s, at %s.", e.Activity.Location, time.Now().Format(time.RFC3339))
150                 case cargo.Customs:
151                         description = fmt.Sprintf("Cleared customs in %s, at %s.", e.Activity.Location, time.Now().Format(time.RFC3339))
152                 default:
153                         description = "[Unknown status]"
154                 }
155
156                 events = append(events, Event{
157                         Description: description,
158                         Expected:    c.Itinerary.IsExpected(e),
159                 })
160         }
161
162         return events
163 }