shipping: Update with upstream changes
Marcus Olsson
7 years ago
15 | 15 | |
16 | 16 | There are also a few pure domain packages that contain some intricate business-logic. They provide domain objects and services that are used by each application service to provide interesting use-cases for the user. |
17 | 17 | |
18 | `repository` contains in-memory implementations for the repositories found in the domain packages. | |
18 | `inmem` contains in-memory implementations for the repositories found in the domain packages. | |
19 | 19 | |
20 | 20 | The `routing` package provides a _domain service_ that is used to query an external application for possible routes. |
21 | 21 |
5 | 5 | "golang.org/x/net/context" |
6 | 6 | |
7 | 7 | "github.com/go-kit/kit/endpoint" |
8 | ||
8 | 9 | "github.com/go-kit/kit/examples/shipping/cargo" |
9 | 10 | "github.com/go-kit/kit/examples/shipping/location" |
10 | 11 | ) |
15 | 15 | } |
16 | 16 | |
17 | 17 | // NewInstrumentingService returns an instance of an instrumenting Service. |
18 | func NewInstrumentingService(requestCount metrics.Counter, requestLatency metrics.Histogram, s Service) Service { | |
18 | func NewInstrumentingService(counter metrics.Counter, latency metrics.Histogram, s Service) Service { | |
19 | 19 | return &instrumentingService{ |
20 | requestCount: requestCount, | |
21 | requestLatency: requestLatency, | |
20 | requestCount: counter, | |
21 | requestLatency: latency, | |
22 | 22 | Service: s, |
23 | 23 | } |
24 | 24 | } |
25 | 25 | |
26 | func (s *instrumentingService) BookNewCargo(origin, destination location.UNLocode, arrivalDeadline time.Time) (cargo.TrackingID, error) { | |
26 | func (s *instrumentingService) BookNewCargo(origin, destination location.UNLocode, deadline time.Time) (cargo.TrackingID, error) { | |
27 | 27 | defer func(begin time.Time) { |
28 | 28 | s.requestCount.With("method", "book").Add(1) |
29 | 29 | s.requestLatency.With("method", "book").Observe(time.Since(begin).Seconds()) |
30 | 30 | }(time.Now()) |
31 | 31 | |
32 | return s.Service.BookNewCargo(origin, destination, arrivalDeadline) | |
32 | return s.Service.BookNewCargo(origin, destination, deadline) | |
33 | 33 | } |
34 | 34 | |
35 | 35 | func (s *instrumentingService) LoadCargo(id cargo.TrackingID) (c Cargo, err error) { |
2 | 2 | import ( |
3 | 3 | "time" |
4 | 4 | |
5 | "github.com/go-kit/kit/log" | |
6 | ||
5 | 7 | "github.com/go-kit/kit/examples/shipping/cargo" |
6 | 8 | "github.com/go-kit/kit/examples/shipping/location" |
7 | "github.com/go-kit/kit/log" | |
8 | 9 | ) |
9 | 10 | |
10 | 11 | type loggingService struct { |
17 | 18 | return &loggingService{logger, s} |
18 | 19 | } |
19 | 20 | |
20 | func (s *loggingService) BookNewCargo(origin location.UNLocode, destination location.UNLocode, arrivalDeadline time.Time) (id cargo.TrackingID, err error) { | |
21 | func (s *loggingService) BookNewCargo(origin location.UNLocode, destination location.UNLocode, deadline time.Time) (id cargo.TrackingID, err error) { | |
21 | 22 | defer func(begin time.Time) { |
22 | 23 | s.logger.Log( |
23 | 24 | "method", "book", |
24 | 25 | "origin", origin, |
25 | 26 | "destination", destination, |
26 | "arrival_deadline", arrivalDeadline, | |
27 | "arrival_deadline", deadline, | |
27 | 28 | "took", time.Since(begin), |
28 | 29 | "err", err, |
29 | 30 | ) |
30 | 31 | }(time.Now()) |
31 | return s.Service.BookNewCargo(origin, destination, arrivalDeadline) | |
32 | return s.Service.BookNewCargo(origin, destination, deadline) | |
32 | 33 | } |
33 | 34 | |
34 | 35 | func (s *loggingService) LoadCargo(id cargo.TrackingID) (c Cargo, err error) { |
17 | 17 | type Service interface { |
18 | 18 | // BookNewCargo registers a new cargo in the tracking system, not yet |
19 | 19 | // routed. |
20 | BookNewCargo(origin location.UNLocode, destination location.UNLocode, arrivalDeadline time.Time) (cargo.TrackingID, error) | |
20 | BookNewCargo(origin location.UNLocode, destination location.UNLocode, deadline time.Time) (cargo.TrackingID, error) | |
21 | 21 | |
22 | 22 | // LoadCargo returns a read model of a cargo. |
23 | LoadCargo(trackingID cargo.TrackingID) (Cargo, error) | |
23 | LoadCargo(id cargo.TrackingID) (Cargo, error) | |
24 | 24 | |
25 | 25 | // RequestPossibleRoutesForCargo requests a list of itineraries describing |
26 | 26 | // possible routes for this cargo. |
27 | RequestPossibleRoutesForCargo(trackingID cargo.TrackingID) []cargo.Itinerary | |
27 | RequestPossibleRoutesForCargo(id cargo.TrackingID) []cargo.Itinerary | |
28 | 28 | |
29 | 29 | // AssignCargoToRoute assigns a cargo to the route specified by the |
30 | 30 | // itinerary. |
31 | AssignCargoToRoute(trackingID cargo.TrackingID, itinerary cargo.Itinerary) error | |
31 | AssignCargoToRoute(id cargo.TrackingID, itinerary cargo.Itinerary) error | |
32 | 32 | |
33 | 33 | // ChangeDestination changes the destination of a cargo. |
34 | ChangeDestination(trackingID cargo.TrackingID, unLocode location.UNLocode) error | |
34 | ChangeDestination(id cargo.TrackingID, destination location.UNLocode) error | |
35 | 35 | |
36 | 36 | // Cargos returns a list of all cargos that have been booked. |
37 | 37 | Cargos() []Cargo |
41 | 41 | } |
42 | 42 | |
43 | 43 | type service struct { |
44 | cargoRepository cargo.Repository | |
45 | locationRepository location.Repository | |
46 | routingService routing.Service | |
47 | handlingEventRepository cargo.HandlingEventRepository | |
44 | cargos cargo.Repository | |
45 | locations location.Repository | |
46 | handlingEvents cargo.HandlingEventRepository | |
47 | routingService routing.Service | |
48 | 48 | } |
49 | 49 | |
50 | 50 | func (s *service) AssignCargoToRoute(id cargo.TrackingID, itinerary cargo.Itinerary) error { |
52 | 52 | return ErrInvalidArgument |
53 | 53 | } |
54 | 54 | |
55 | c, err := s.cargoRepository.Find(id) | |
55 | c, err := s.cargos.Find(id) | |
56 | 56 | if err != nil { |
57 | 57 | return err |
58 | 58 | } |
59 | 59 | |
60 | 60 | c.AssignToRoute(itinerary) |
61 | 61 | |
62 | if err := s.cargoRepository.Store(c); err != nil { | |
63 | return err | |
64 | } | |
65 | ||
66 | return nil | |
62 | return s.cargos.Store(c) | |
67 | 63 | } |
68 | 64 | |
69 | func (s *service) BookNewCargo(origin, destination location.UNLocode, arrivalDeadline time.Time) (cargo.TrackingID, error) { | |
70 | if origin == "" || destination == "" || arrivalDeadline.IsZero() { | |
65 | func (s *service) BookNewCargo(origin, destination location.UNLocode, deadline time.Time) (cargo.TrackingID, error) { | |
66 | if origin == "" || destination == "" || deadline.IsZero() { | |
71 | 67 | return "", ErrInvalidArgument |
72 | 68 | } |
73 | 69 | |
75 | 71 | rs := cargo.RouteSpecification{ |
76 | 72 | Origin: origin, |
77 | 73 | Destination: destination, |
78 | ArrivalDeadline: arrivalDeadline, | |
74 | ArrivalDeadline: deadline, | |
79 | 75 | } |
80 | 76 | |
81 | 77 | c := cargo.New(id, rs) |
82 | 78 | |
83 | if err := s.cargoRepository.Store(c); err != nil { | |
79 | if err := s.cargos.Store(c); err != nil { | |
84 | 80 | return "", err |
85 | 81 | } |
86 | 82 | |
87 | 83 | return c.TrackingID, nil |
88 | 84 | } |
89 | 85 | |
90 | func (s *service) LoadCargo(trackingID cargo.TrackingID) (Cargo, error) { | |
91 | if trackingID == "" { | |
86 | func (s *service) LoadCargo(id cargo.TrackingID) (Cargo, error) { | |
87 | if id == "" { | |
92 | 88 | return Cargo{}, ErrInvalidArgument |
93 | 89 | } |
94 | 90 | |
95 | c, err := s.cargoRepository.Find(trackingID) | |
91 | c, err := s.cargos.Find(id) | |
96 | 92 | if err != nil { |
97 | 93 | return Cargo{}, err |
98 | 94 | } |
99 | 95 | |
100 | return assemble(c, s.handlingEventRepository), nil | |
96 | return assemble(c, s.handlingEvents), nil | |
101 | 97 | } |
102 | 98 | |
103 | 99 | func (s *service) ChangeDestination(id cargo.TrackingID, destination location.UNLocode) error { |
105 | 101 | return ErrInvalidArgument |
106 | 102 | } |
107 | 103 | |
108 | c, err := s.cargoRepository.Find(id) | |
104 | c, err := s.cargos.Find(id) | |
109 | 105 | if err != nil { |
110 | 106 | return err |
111 | 107 | } |
112 | 108 | |
113 | l, err := s.locationRepository.Find(destination) | |
109 | l, err := s.locations.Find(destination) | |
114 | 110 | if err != nil { |
115 | 111 | return err |
116 | 112 | } |
121 | 117 | ArrivalDeadline: c.RouteSpecification.ArrivalDeadline, |
122 | 118 | }) |
123 | 119 | |
124 | if err := s.cargoRepository.Store(c); err != nil { | |
120 | if err := s.cargos.Store(c); err != nil { | |
125 | 121 | return err |
126 | 122 | } |
127 | 123 | |
133 | 129 | return nil |
134 | 130 | } |
135 | 131 | |
136 | c, err := s.cargoRepository.Find(id) | |
132 | c, err := s.cargos.Find(id) | |
137 | 133 | if err != nil { |
138 | 134 | return []cargo.Itinerary{} |
139 | 135 | } |
143 | 139 | |
144 | 140 | func (s *service) Cargos() []Cargo { |
145 | 141 | var result []Cargo |
146 | for _, c := range s.cargoRepository.FindAll() { | |
147 | result = append(result, assemble(c, s.handlingEventRepository)) | |
142 | for _, c := range s.cargos.FindAll() { | |
143 | result = append(result, assemble(c, s.handlingEvents)) | |
148 | 144 | } |
149 | 145 | return result |
150 | 146 | } |
151 | 147 | |
152 | 148 | func (s *service) Locations() []Location { |
153 | 149 | var result []Location |
154 | for _, v := range s.locationRepository.FindAll() { | |
150 | for _, v := range s.locations.FindAll() { | |
155 | 151 | result = append(result, Location{ |
156 | 152 | UNLocode: string(v.UNLocode), |
157 | 153 | Name: v.Name, |
161 | 157 | } |
162 | 158 | |
163 | 159 | // NewService creates a booking service with necessary dependencies. |
164 | func NewService(cr cargo.Repository, lr location.Repository, her cargo.HandlingEventRepository, rs routing.Service) Service { | |
160 | func NewService(cargos cargo.Repository, locations location.Repository, events cargo.HandlingEventRepository, rs routing.Service) Service { | |
165 | 161 | return &service{ |
166 | cargoRepository: cr, | |
167 | locationRepository: lr, | |
168 | handlingEventRepository: her, | |
169 | routingService: rs, | |
162 | cargos: cargos, | |
163 | locations: locations, | |
164 | handlingEvents: events, | |
165 | routingService: rs, | |
170 | 166 | } |
171 | 167 | } |
172 | 168 | |
187 | 183 | TrackingID string `json:"tracking_id"` |
188 | 184 | } |
189 | 185 | |
190 | func assemble(c *cargo.Cargo, her cargo.HandlingEventRepository) Cargo { | |
186 | func assemble(c *cargo.Cargo, events cargo.HandlingEventRepository) Cargo { | |
191 | 187 | return Cargo{ |
192 | 188 | TrackingID: string(c.TrackingID), |
193 | 189 | Origin: string(c.Origin), |
8 | 8 | "github.com/gorilla/mux" |
9 | 9 | "golang.org/x/net/context" |
10 | 10 | |
11 | kitlog "github.com/go-kit/kit/log" | |
12 | kithttp "github.com/go-kit/kit/transport/http" | |
13 | ||
11 | 14 | "github.com/go-kit/kit/examples/shipping/cargo" |
12 | 15 | "github.com/go-kit/kit/examples/shipping/location" |
13 | kitlog "github.com/go-kit/kit/log" | |
14 | kithttp "github.com/go-kit/kit/transport/http" | |
15 | 16 | ) |
16 | 17 | |
17 | 18 | // MakeHandler returns a handler for the booking service. |
80 | 81 | r.Handle("/booking/v1/cargos/{id}/assign_to_route", assignToRouteHandler).Methods("POST") |
81 | 82 | r.Handle("/booking/v1/cargos/{id}/change_destination", changeDestinationHandler).Methods("POST") |
82 | 83 | r.Handle("/booking/v1/locations", listLocationsHandler).Methods("GET") |
83 | r.Handle("/booking/v1/docs", http.StripPrefix("/booking/v1/docs", http.FileServer(http.Dir("booking/docs")))) | |
84 | 84 | |
85 | 85 | return r |
86 | 86 | } |
56 | 56 | // Repository provides access a cargo store. |
57 | 57 | type Repository interface { |
58 | 58 | Store(cargo *Cargo) error |
59 | Find(trackingID TrackingID) (*Cargo, error) | |
59 | Find(id TrackingID) (*Cargo, error) | |
60 | 60 | FindAll() []*Cargo |
61 | 61 | } |
62 | 62 |
91 | 91 | } |
92 | 92 | |
93 | 93 | // CreateHandlingEvent creates a validated handling event. |
94 | func (f *HandlingEventFactory) CreateHandlingEvent(registrationTime time.Time, completionTime time.Time, trackingID TrackingID, | |
94 | func (f *HandlingEventFactory) CreateHandlingEvent(registered time.Time, completed time.Time, id TrackingID, | |
95 | 95 | voyageNumber voyage.Number, unLocode location.UNLocode, eventType HandlingEventType) (HandlingEvent, error) { |
96 | 96 | |
97 | if _, err := f.CargoRepository.Find(trackingID); err != nil { | |
97 | if _, err := f.CargoRepository.Find(id); err != nil { | |
98 | 98 | return HandlingEvent{}, err |
99 | 99 | } |
100 | 100 | |
110 | 110 | } |
111 | 111 | |
112 | 112 | return HandlingEvent{ |
113 | TrackingID: trackingID, | |
113 | TrackingID: id, | |
114 | 114 | Activity: HandlingActivity{ |
115 | 115 | Type: eventType, |
116 | 116 | Location: unLocode, |
5 | 5 | "golang.org/x/net/context" |
6 | 6 | |
7 | 7 | "github.com/go-kit/kit/endpoint" |
8 | ||
8 | 9 | "github.com/go-kit/kit/examples/shipping/cargo" |
9 | 10 | "github.com/go-kit/kit/examples/shipping/location" |
10 | 11 | "github.com/go-kit/kit/examples/shipping/voyage" |
16 | 16 | } |
17 | 17 | |
18 | 18 | // NewInstrumentingService returns an instance of an instrumenting Service. |
19 | func NewInstrumentingService(requestCount metrics.Counter, requestLatency metrics.Histogram, s Service) Service { | |
19 | func NewInstrumentingService(counter metrics.Counter, latency metrics.Histogram, s Service) Service { | |
20 | 20 | return &instrumentingService{ |
21 | requestCount: requestCount, | |
22 | requestLatency: requestLatency, | |
21 | requestCount: counter, | |
22 | requestLatency: latency, | |
23 | 23 | Service: s, |
24 | 24 | } |
25 | 25 | } |
26 | 26 | |
27 | func (s *instrumentingService) RegisterHandlingEvent(completionTime time.Time, trackingID cargo.TrackingID, voyage voyage.Number, | |
27 | func (s *instrumentingService) RegisterHandlingEvent(completed time.Time, id cargo.TrackingID, voyageNumber voyage.Number, | |
28 | 28 | loc location.UNLocode, eventType cargo.HandlingEventType) error { |
29 | 29 | |
30 | 30 | defer func(begin time.Time) { |
32 | 32 | s.requestLatency.With("method", "register_incident").Observe(time.Since(begin).Seconds()) |
33 | 33 | }(time.Now()) |
34 | 34 | |
35 | return s.Service.RegisterHandlingEvent(completionTime, trackingID, voyage, loc, eventType) | |
35 | return s.Service.RegisterHandlingEvent(completed, id, voyageNumber, loc, eventType) | |
36 | 36 | } |
2 | 2 | import ( |
3 | 3 | "time" |
4 | 4 | |
5 | "github.com/go-kit/kit/log" | |
6 | ||
5 | 7 | "github.com/go-kit/kit/examples/shipping/cargo" |
6 | 8 | "github.com/go-kit/kit/examples/shipping/location" |
7 | 9 | "github.com/go-kit/kit/examples/shipping/voyage" |
8 | "github.com/go-kit/kit/log" | |
9 | 10 | ) |
10 | 11 | |
11 | 12 | type loggingService struct { |
18 | 19 | return &loggingService{logger, s} |
19 | 20 | } |
20 | 21 | |
21 | func (s *loggingService) RegisterHandlingEvent(completionTime time.Time, trackingID cargo.TrackingID, voyageNumber voyage.Number, | |
22 | func (s *loggingService) RegisterHandlingEvent(completed time.Time, id cargo.TrackingID, voyageNumber voyage.Number, | |
22 | 23 | unLocode location.UNLocode, eventType cargo.HandlingEventType) (err error) { |
23 | 24 | defer func(begin time.Time) { |
24 | 25 | s.logger.Log( |
25 | 26 | "method", "register_incident", |
26 | "tracking_id", trackingID, | |
27 | "tracking_id", id, | |
27 | 28 | "location", unLocode, |
28 | 29 | "voyage", voyageNumber, |
29 | 30 | "event_type", eventType, |
30 | "completion_time", completionTime, | |
31 | "completion_time", completed, | |
31 | 32 | "took", time.Since(begin), |
32 | 33 | "err", err, |
33 | 34 | ) |
34 | 35 | }(time.Now()) |
35 | return s.Service.RegisterHandlingEvent(completionTime, trackingID, voyageNumber, unLocode, eventType) | |
36 | return s.Service.RegisterHandlingEvent(completed, id, voyageNumber, unLocode, eventType) | |
36 | 37 | } |
23 | 23 | type Service interface { |
24 | 24 | // RegisterHandlingEvent registers a handling event in the system, and |
25 | 25 | // notifies interested parties that a cargo has been handled. |
26 | RegisterHandlingEvent(completionTime time.Time, trackingID cargo.TrackingID, voyageNumber voyage.Number, | |
26 | RegisterHandlingEvent(completed time.Time, id cargo.TrackingID, voyageNumber voyage.Number, | |
27 | 27 | unLocode location.UNLocode, eventType cargo.HandlingEventType) error |
28 | 28 | } |
29 | 29 | |
33 | 33 | handlingEventHandler EventHandler |
34 | 34 | } |
35 | 35 | |
36 | func (s *service) RegisterHandlingEvent(completionTime time.Time, trackingID cargo.TrackingID, voyage voyage.Number, | |
36 | func (s *service) RegisterHandlingEvent(completed time.Time, id cargo.TrackingID, voyageNumber voyage.Number, | |
37 | 37 | loc location.UNLocode, eventType cargo.HandlingEventType) error { |
38 | if completionTime.IsZero() || trackingID == "" || loc == "" || eventType == cargo.NotHandled { | |
38 | if completed.IsZero() || id == "" || loc == "" || eventType == cargo.NotHandled { | |
39 | 39 | return ErrInvalidArgument |
40 | 40 | } |
41 | 41 | |
42 | e, err := s.handlingEventFactory.CreateHandlingEvent(time.Now(), completionTime, trackingID, voyage, loc, eventType) | |
42 | e, err := s.handlingEventFactory.CreateHandlingEvent(time.Now(), completed, id, voyageNumber, loc, eventType) | |
43 | 43 | if err != nil { |
44 | 44 | return err |
45 | 45 | } |
7 | 7 | "github.com/gorilla/mux" |
8 | 8 | "golang.org/x/net/context" |
9 | 9 | |
10 | kitlog "github.com/go-kit/kit/log" | |
11 | kithttp "github.com/go-kit/kit/transport/http" | |
12 | ||
10 | 13 | "github.com/go-kit/kit/examples/shipping/cargo" |
11 | 14 | "github.com/go-kit/kit/examples/shipping/location" |
12 | 15 | "github.com/go-kit/kit/examples/shipping/voyage" |
13 | kitlog "github.com/go-kit/kit/log" | |
14 | kithttp "github.com/go-kit/kit/transport/http" | |
15 | 16 | ) |
16 | 17 | |
17 | 18 | // MakeHandler returns a handler for the handling service. |
0 | // Package inmem provides in-memory implementations of all the domain repositories. | |
1 | package inmem | |
2 | ||
3 | import ( | |
4 | "sync" | |
5 | ||
6 | "github.com/go-kit/kit/examples/shipping/cargo" | |
7 | "github.com/go-kit/kit/examples/shipping/location" | |
8 | "github.com/go-kit/kit/examples/shipping/voyage" | |
9 | ) | |
10 | ||
11 | type cargoRepository struct { | |
12 | mtx sync.RWMutex | |
13 | cargos map[cargo.TrackingID]*cargo.Cargo | |
14 | } | |
15 | ||
16 | func (r *cargoRepository) Store(c *cargo.Cargo) error { | |
17 | r.mtx.Lock() | |
18 | defer r.mtx.Unlock() | |
19 | r.cargos[c.TrackingID] = c | |
20 | return nil | |
21 | } | |
22 | ||
23 | func (r *cargoRepository) Find(id cargo.TrackingID) (*cargo.Cargo, error) { | |
24 | r.mtx.RLock() | |
25 | defer r.mtx.RUnlock() | |
26 | if val, ok := r.cargos[id]; ok { | |
27 | return val, nil | |
28 | } | |
29 | return nil, cargo.ErrUnknown | |
30 | } | |
31 | ||
32 | func (r *cargoRepository) FindAll() []*cargo.Cargo { | |
33 | r.mtx.RLock() | |
34 | defer r.mtx.RUnlock() | |
35 | c := make([]*cargo.Cargo, 0, len(r.cargos)) | |
36 | for _, val := range r.cargos { | |
37 | c = append(c, val) | |
38 | } | |
39 | return c | |
40 | } | |
41 | ||
42 | // NewCargoRepository returns a new instance of a in-memory cargo repository. | |
43 | func NewCargoRepository() cargo.Repository { | |
44 | return &cargoRepository{ | |
45 | cargos: make(map[cargo.TrackingID]*cargo.Cargo), | |
46 | } | |
47 | } | |
48 | ||
49 | type locationRepository struct { | |
50 | locations map[location.UNLocode]*location.Location | |
51 | } | |
52 | ||
53 | func (r *locationRepository) Find(locode location.UNLocode) (*location.Location, error) { | |
54 | if l, ok := r.locations[locode]; ok { | |
55 | return l, nil | |
56 | } | |
57 | return nil, location.ErrUnknown | |
58 | } | |
59 | ||
60 | func (r *locationRepository) FindAll() []*location.Location { | |
61 | l := make([]*location.Location, 0, len(r.locations)) | |
62 | for _, val := range r.locations { | |
63 | l = append(l, val) | |
64 | } | |
65 | return l | |
66 | } | |
67 | ||
68 | // NewLocationRepository returns a new instance of a in-memory location repository. | |
69 | func NewLocationRepository() location.Repository { | |
70 | r := &locationRepository{ | |
71 | locations: make(map[location.UNLocode]*location.Location), | |
72 | } | |
73 | ||
74 | r.locations[location.SESTO] = location.Stockholm | |
75 | r.locations[location.AUMEL] = location.Melbourne | |
76 | r.locations[location.CNHKG] = location.Hongkong | |
77 | r.locations[location.JNTKO] = location.Tokyo | |
78 | r.locations[location.NLRTM] = location.Rotterdam | |
79 | r.locations[location.DEHAM] = location.Hamburg | |
80 | ||
81 | return r | |
82 | } | |
83 | ||
84 | type voyageRepository struct { | |
85 | voyages map[voyage.Number]*voyage.Voyage | |
86 | } | |
87 | ||
88 | func (r *voyageRepository) Find(voyageNumber voyage.Number) (*voyage.Voyage, error) { | |
89 | if v, ok := r.voyages[voyageNumber]; ok { | |
90 | return v, nil | |
91 | } | |
92 | ||
93 | return nil, voyage.ErrUnknown | |
94 | } | |
95 | ||
96 | // NewVoyageRepository returns a new instance of a in-memory voyage repository. | |
97 | func NewVoyageRepository() voyage.Repository { | |
98 | r := &voyageRepository{ | |
99 | voyages: make(map[voyage.Number]*voyage.Voyage), | |
100 | } | |
101 | ||
102 | r.voyages[voyage.V100.Number] = voyage.V100 | |
103 | r.voyages[voyage.V300.Number] = voyage.V300 | |
104 | r.voyages[voyage.V400.Number] = voyage.V400 | |
105 | ||
106 | r.voyages[voyage.V0100S.Number] = voyage.V0100S | |
107 | r.voyages[voyage.V0200T.Number] = voyage.V0200T | |
108 | r.voyages[voyage.V0300A.Number] = voyage.V0300A | |
109 | r.voyages[voyage.V0301S.Number] = voyage.V0301S | |
110 | r.voyages[voyage.V0400S.Number] = voyage.V0400S | |
111 | ||
112 | return r | |
113 | } | |
114 | ||
115 | type handlingEventRepository struct { | |
116 | mtx sync.RWMutex | |
117 | events map[cargo.TrackingID][]cargo.HandlingEvent | |
118 | } | |
119 | ||
120 | func (r *handlingEventRepository) Store(e cargo.HandlingEvent) { | |
121 | r.mtx.Lock() | |
122 | defer r.mtx.Unlock() | |
123 | // Make array if it's the first event with this tracking ID. | |
124 | if _, ok := r.events[e.TrackingID]; !ok { | |
125 | r.events[e.TrackingID] = make([]cargo.HandlingEvent, 0) | |
126 | } | |
127 | r.events[e.TrackingID] = append(r.events[e.TrackingID], e) | |
128 | } | |
129 | ||
130 | func (r *handlingEventRepository) QueryHandlingHistory(id cargo.TrackingID) cargo.HandlingHistory { | |
131 | r.mtx.RLock() | |
132 | defer r.mtx.RUnlock() | |
133 | return cargo.HandlingHistory{HandlingEvents: r.events[id]} | |
134 | } | |
135 | ||
136 | // NewHandlingEventRepository returns a new instance of a in-memory handling event repository. | |
137 | func NewHandlingEventRepository() cargo.HandlingEventRepository { | |
138 | return &handlingEventRepository{ | |
139 | events: make(map[cargo.TrackingID][]cargo.HandlingEvent), | |
140 | } | |
141 | } |
13 | 13 | // InspectCargo inspects cargo and send relevant notifications to |
14 | 14 | // interested parties, for example if a cargo has been misdirected, or |
15 | 15 | // unloaded at the final destination. |
16 | InspectCargo(trackingID cargo.TrackingID) | |
16 | InspectCargo(id cargo.TrackingID) | |
17 | 17 | } |
18 | 18 | |
19 | 19 | type service struct { |
20 | cargoRepository cargo.Repository | |
21 | handlingEventRepository cargo.HandlingEventRepository | |
22 | cargoEventHandler EventHandler | |
20 | cargos cargo.Repository | |
21 | events cargo.HandlingEventRepository | |
22 | handler EventHandler | |
23 | 23 | } |
24 | 24 | |
25 | 25 | // TODO: Should be transactional |
26 | func (s *service) InspectCargo(trackingID cargo.TrackingID) { | |
27 | c, err := s.cargoRepository.Find(trackingID) | |
26 | func (s *service) InspectCargo(id cargo.TrackingID) { | |
27 | c, err := s.cargos.Find(id) | |
28 | 28 | if err != nil { |
29 | 29 | return |
30 | 30 | } |
31 | 31 | |
32 | h := s.handlingEventRepository.QueryHandlingHistory(trackingID) | |
32 | h := s.events.QueryHandlingHistory(id) | |
33 | 33 | |
34 | 34 | c.DeriveDeliveryProgress(h) |
35 | 35 | |
36 | 36 | if c.Delivery.IsMisdirected { |
37 | s.cargoEventHandler.CargoWasMisdirected(c) | |
37 | s.handler.CargoWasMisdirected(c) | |
38 | 38 | } |
39 | 39 | |
40 | 40 | if c.Delivery.IsUnloadedAtDestination { |
41 | s.cargoEventHandler.CargoHasArrived(c) | |
41 | s.handler.CargoHasArrived(c) | |
42 | 42 | } |
43 | 43 | |
44 | s.cargoRepository.Store(c) | |
44 | s.cargos.Store(c) | |
45 | 45 | } |
46 | 46 | |
47 | 47 | // NewService creates a inspection service with necessary dependencies. |
48 | func NewService(cargoRepository cargo.Repository, handlingEventRepository cargo.HandlingEventRepository, eventHandler EventHandler) Service { | |
49 | return &service{cargoRepository, handlingEventRepository, eventHandler} | |
48 | func NewService(cargos cargo.Repository, events cargo.HandlingEventRepository, handler EventHandler) Service { | |
49 | return &service{cargos, events, handler} | |
50 | 50 | } |
21 | 21 | |
22 | 22 | // Repository provides access a location store. |
23 | 23 | type Repository interface { |
24 | Find(locode UNLocode) (Location, error) | |
25 | FindAll() []Location | |
24 | Find(locode UNLocode) (*Location, error) | |
25 | FindAll() []*Location | |
26 | 26 | } |
14 | 14 | |
15 | 15 | // Sample locations. |
16 | 16 | var ( |
17 | Stockholm = Location{SESTO, "Stockholm"} | |
18 | Melbourne = Location{AUMEL, "Melbourne"} | |
19 | Hongkong = Location{CNHKG, "Hongkong"} | |
20 | NewYork = Location{USNYC, "New York"} | |
21 | Chicago = Location{USCHI, "Chicago"} | |
22 | Tokyo = Location{JNTKO, "Tokyo"} | |
23 | Hamburg = Location{DEHAM, "Hamburg"} | |
24 | Rotterdam = Location{NLRTM, "Rotterdam"} | |
25 | Helsinki = Location{FIHEL, "Helsinki"} | |
17 | Stockholm = &Location{SESTO, "Stockholm"} | |
18 | Melbourne = &Location{AUMEL, "Melbourne"} | |
19 | Hongkong = &Location{CNHKG, "Hongkong"} | |
20 | NewYork = &Location{USNYC, "New York"} | |
21 | Chicago = &Location{USCHI, "Chicago"} | |
22 | Tokyo = &Location{JNTKO, "Tokyo"} | |
23 | Hamburg = &Location{DEHAM, "Hamburg"} | |
24 | Rotterdam = &Location{NLRTM, "Rotterdam"} | |
25 | Helsinki = &Location{FIHEL, "Helsinki"} | |
26 | 26 | ) |
18 | 18 | "github.com/go-kit/kit/examples/shipping/booking" |
19 | 19 | "github.com/go-kit/kit/examples/shipping/cargo" |
20 | 20 | "github.com/go-kit/kit/examples/shipping/handling" |
21 | "github.com/go-kit/kit/examples/shipping/inmem" | |
21 | 22 | "github.com/go-kit/kit/examples/shipping/inspection" |
22 | 23 | "github.com/go-kit/kit/examples/shipping/location" |
23 | "github.com/go-kit/kit/examples/shipping/repository" | |
24 | 24 | "github.com/go-kit/kit/examples/shipping/routing" |
25 | 25 | "github.com/go-kit/kit/examples/shipping/tracking" |
26 | 26 | ) |
49 | 49 | logger = log.NewContext(logger).With("ts", log.DefaultTimestampUTC) |
50 | 50 | |
51 | 51 | var ( |
52 | cargos = repository.NewCargo() | |
53 | locations = repository.NewLocation() | |
54 | voyages = repository.NewVoyage() | |
55 | handlingEvents = repository.NewHandlingEvent() | |
52 | cargos = inmem.NewCargoRepository() | |
53 | locations = inmem.NewLocationRepository() | |
54 | voyages = inmem.NewVoyageRepository() | |
55 | handlingEvents = inmem.NewHandlingEventRepository() | |
56 | 56 | ) |
57 | 57 | |
58 | 58 | // Configure some questionable dependencies. |
73 | 73 | fieldKeys := []string{"method"} |
74 | 74 | |
75 | 75 | var rs routing.Service |
76 | rs = routing.NewProxyingMiddleware(*routingServiceURL, ctx)(rs) | |
76 | rs = routing.NewProxyingMiddleware(ctx, *routingServiceURL)(rs) | |
77 | 77 | |
78 | 78 | var bs booking.Service |
79 | 79 | bs = booking.NewService(cargos, locations, handlingEvents, rs) |
185 | 185 | Destination: location.SESTO, |
186 | 186 | ArrivalDeadline: time.Now().AddDate(0, 0, 7), |
187 | 187 | }) |
188 | _ = r.Store(test1) | |
188 | if err := r.Store(test1); err != nil { | |
189 | panic(err) | |
190 | } | |
189 | 191 | |
190 | 192 | test2 := cargo.New("ABC123", cargo.RouteSpecification{ |
191 | 193 | Origin: location.SESTO, |
192 | 194 | Destination: location.CNHKG, |
193 | 195 | ArrivalDeadline: time.Now().AddDate(0, 0, 14), |
194 | 196 | }) |
195 | _ = r.Store(test2) | |
197 | if err := r.Store(test2); err != nil { | |
198 | panic(err) | |
199 | } | |
196 | 200 | } |
197 | 201 | |
198 | 202 | type serializedLogger struct { |
0 | package mock | |
1 | ||
2 | import ( | |
3 | "github.com/go-kit/kit/examples/shipping/cargo" | |
4 | "github.com/go-kit/kit/examples/shipping/location" | |
5 | "github.com/go-kit/kit/examples/shipping/voyage" | |
6 | ) | |
7 | ||
8 | // CargoRepository is a mock cargo repository. | |
9 | type CargoRepository struct { | |
10 | StoreFn func(c *cargo.Cargo) error | |
11 | StoreInvoked bool | |
12 | ||
13 | FindFn func(id cargo.TrackingID) (*cargo.Cargo, error) | |
14 | FindInvoked bool | |
15 | ||
16 | FindAllFn func() []*cargo.Cargo | |
17 | FindAllInvoked bool | |
18 | } | |
19 | ||
20 | // Store calls the StoreFn. | |
21 | func (r *CargoRepository) Store(c *cargo.Cargo) error { | |
22 | r.StoreInvoked = true | |
23 | return r.StoreFn(c) | |
24 | } | |
25 | ||
26 | // Find calls the FindFn. | |
27 | func (r *CargoRepository) Find(id cargo.TrackingID) (*cargo.Cargo, error) { | |
28 | r.FindInvoked = true | |
29 | return r.FindFn(id) | |
30 | } | |
31 | ||
32 | // FindAll calls the FindAllFn. | |
33 | func (r *CargoRepository) FindAll() []*cargo.Cargo { | |
34 | r.FindAllInvoked = true | |
35 | return r.FindAllFn() | |
36 | } | |
37 | ||
38 | // LocationRepository is a mock location repository. | |
39 | type LocationRepository struct { | |
40 | FindFn func(location.UNLocode) (*location.Location, error) | |
41 | FindInvoked bool | |
42 | ||
43 | FindAllFn func() []*location.Location | |
44 | FindAllInvoked bool | |
45 | } | |
46 | ||
47 | // Find calls the FindFn. | |
48 | func (r *LocationRepository) Find(locode location.UNLocode) (*location.Location, error) { | |
49 | r.FindInvoked = true | |
50 | return r.FindFn(locode) | |
51 | } | |
52 | ||
53 | // FindAll calls the FindAllFn. | |
54 | func (r *LocationRepository) FindAll() []*location.Location { | |
55 | r.FindAllInvoked = true | |
56 | return r.FindAllFn() | |
57 | } | |
58 | ||
59 | // VoyageRepository is a mock voyage repository. | |
60 | type VoyageRepository struct { | |
61 | FindFn func(voyage.Number) (*voyage.Voyage, error) | |
62 | FindInvoked bool | |
63 | } | |
64 | ||
65 | // Find calls the FindFn. | |
66 | func (r *VoyageRepository) Find(number voyage.Number) (*voyage.Voyage, error) { | |
67 | r.FindInvoked = true | |
68 | return r.FindFn(number) | |
69 | } | |
70 | ||
71 | // HandlingEventRepository is a mock handling events repository. | |
72 | type HandlingEventRepository struct { | |
73 | StoreFn func(cargo.HandlingEvent) | |
74 | StoreInvoked bool | |
75 | ||
76 | QueryHandlingHistoryFn func(cargo.TrackingID) cargo.HandlingHistory | |
77 | QueryHandlingHistoryInvoked bool | |
78 | } | |
79 | ||
80 | // Store calls the StoreFn. | |
81 | func (r *HandlingEventRepository) Store(e cargo.HandlingEvent) { | |
82 | r.StoreInvoked = true | |
83 | r.StoreFn(e) | |
84 | } | |
85 | ||
86 | // QueryHandlingHistory calls the QueryHandlingHistoryFn. | |
87 | func (r *HandlingEventRepository) QueryHandlingHistory(id cargo.TrackingID) cargo.HandlingHistory { | |
88 | r.QueryHandlingHistoryInvoked = true | |
89 | return r.QueryHandlingHistoryFn(id) | |
90 | } | |
91 | ||
92 | // RoutingService provides a mock routing service. | |
93 | type RoutingService struct { | |
94 | FetchRoutesFn func(cargo.RouteSpecification) []cargo.Itinerary | |
95 | FetchRoutesInvoked bool | |
96 | } | |
97 | ||
98 | // FetchRoutesForSpecification calls the FetchRoutesFn. | |
99 | func (s *RoutingService) FetchRoutesForSpecification(rs cargo.RouteSpecification) []cargo.Itinerary { | |
100 | s.FetchRoutesInvoked = true | |
101 | return s.FetchRoutesFn(rs) | |
102 | } |
0 | // Package repository provides implementations of all the domain repositories. | |
1 | package repository | |
2 | ||
3 | import ( | |
4 | "sync" | |
5 | ||
6 | "github.com/go-kit/kit/examples/shipping/cargo" | |
7 | "github.com/go-kit/kit/examples/shipping/location" | |
8 | "github.com/go-kit/kit/examples/shipping/voyage" | |
9 | ) | |
10 | ||
11 | type cargoRepository struct { | |
12 | mtx sync.RWMutex | |
13 | cargos map[cargo.TrackingID]*cargo.Cargo | |
14 | } | |
15 | ||
16 | func (r *cargoRepository) Store(c *cargo.Cargo) error { | |
17 | r.mtx.Lock() | |
18 | defer r.mtx.Unlock() | |
19 | r.cargos[c.TrackingID] = c | |
20 | return nil | |
21 | } | |
22 | ||
23 | func (r *cargoRepository) Find(trackingID cargo.TrackingID) (*cargo.Cargo, error) { | |
24 | r.mtx.RLock() | |
25 | defer r.mtx.RUnlock() | |
26 | if val, ok := r.cargos[trackingID]; ok { | |
27 | return val, nil | |
28 | } | |
29 | return nil, cargo.ErrUnknown | |
30 | } | |
31 | ||
32 | func (r *cargoRepository) FindAll() []*cargo.Cargo { | |
33 | r.mtx.RLock() | |
34 | defer r.mtx.RUnlock() | |
35 | c := make([]*cargo.Cargo, 0, len(r.cargos)) | |
36 | for _, val := range r.cargos { | |
37 | c = append(c, val) | |
38 | } | |
39 | return c | |
40 | } | |
41 | ||
42 | // NewCargo returns a new instance of a in-memory cargo repository. | |
43 | func NewCargo() cargo.Repository { | |
44 | return &cargoRepository{ | |
45 | cargos: make(map[cargo.TrackingID]*cargo.Cargo), | |
46 | } | |
47 | } | |
48 | ||
49 | type locationRepository struct { | |
50 | locations map[location.UNLocode]location.Location | |
51 | } | |
52 | ||
53 | func (r *locationRepository) Find(locode location.UNLocode) (location.Location, error) { | |
54 | if l, ok := r.locations[locode]; ok { | |
55 | return l, nil | |
56 | } | |
57 | return location.Location{}, location.ErrUnknown | |
58 | } | |
59 | ||
60 | func (r *locationRepository) FindAll() []location.Location { | |
61 | l := make([]location.Location, 0, len(r.locations)) | |
62 | for _, val := range r.locations { | |
63 | l = append(l, val) | |
64 | } | |
65 | return l | |
66 | } | |
67 | ||
68 | // NewLocation returns a new instance of a in-memory location repository. | |
69 | func NewLocation() location.Repository { | |
70 | r := &locationRepository{ | |
71 | locations: make(map[location.UNLocode]location.Location), | |
72 | } | |
73 | ||
74 | r.locations[location.SESTO] = location.Stockholm | |
75 | r.locations[location.AUMEL] = location.Melbourne | |
76 | r.locations[location.CNHKG] = location.Hongkong | |
77 | r.locations[location.JNTKO] = location.Tokyo | |
78 | r.locations[location.NLRTM] = location.Rotterdam | |
79 | r.locations[location.DEHAM] = location.Hamburg | |
80 | ||
81 | return r | |
82 | } | |
83 | ||
84 | type voyageRepository struct { | |
85 | voyages map[voyage.Number]*voyage.Voyage | |
86 | } | |
87 | ||
88 | func (r *voyageRepository) Find(voyageNumber voyage.Number) (*voyage.Voyage, error) { | |
89 | if v, ok := r.voyages[voyageNumber]; ok { | |
90 | return v, nil | |
91 | } | |
92 | ||
93 | return nil, voyage.ErrUnknown | |
94 | } | |
95 | ||
96 | // NewVoyage returns a new instance of a in-memory voyage repository. | |
97 | func NewVoyage() voyage.Repository { | |
98 | r := &voyageRepository{ | |
99 | voyages: make(map[voyage.Number]*voyage.Voyage), | |
100 | } | |
101 | ||
102 | r.voyages[voyage.V100.Number] = voyage.V100 | |
103 | r.voyages[voyage.V300.Number] = voyage.V300 | |
104 | r.voyages[voyage.V400.Number] = voyage.V400 | |
105 | ||
106 | r.voyages[voyage.V0100S.Number] = voyage.V0100S | |
107 | r.voyages[voyage.V0200T.Number] = voyage.V0200T | |
108 | r.voyages[voyage.V0300A.Number] = voyage.V0300A | |
109 | r.voyages[voyage.V0301S.Number] = voyage.V0301S | |
110 | r.voyages[voyage.V0400S.Number] = voyage.V0400S | |
111 | ||
112 | return r | |
113 | } | |
114 | ||
115 | type handlingEventRepository struct { | |
116 | mtx sync.RWMutex | |
117 | events map[cargo.TrackingID][]cargo.HandlingEvent | |
118 | } | |
119 | ||
120 | func (r *handlingEventRepository) Store(e cargo.HandlingEvent) { | |
121 | r.mtx.Lock() | |
122 | defer r.mtx.Unlock() | |
123 | // Make array if it's the first event with this tracking ID. | |
124 | if _, ok := r.events[e.TrackingID]; !ok { | |
125 | r.events[e.TrackingID] = make([]cargo.HandlingEvent, 0) | |
126 | } | |
127 | r.events[e.TrackingID] = append(r.events[e.TrackingID], e) | |
128 | } | |
129 | ||
130 | func (r *handlingEventRepository) QueryHandlingHistory(trackingID cargo.TrackingID) cargo.HandlingHistory { | |
131 | r.mtx.RLock() | |
132 | defer r.mtx.RUnlock() | |
133 | return cargo.HandlingHistory{HandlingEvents: r.events[trackingID]} | |
134 | } | |
135 | ||
136 | // NewHandlingEvent returns a new instance of a in-memory handling event repository. | |
137 | func NewHandlingEvent() cargo.HandlingEventRepository { | |
138 | return &handlingEventRepository{ | |
139 | events: make(map[cargo.TrackingID][]cargo.HandlingEvent), | |
140 | } | |
141 | } |
9 | 9 | |
10 | 10 | "github.com/go-kit/kit/circuitbreaker" |
11 | 11 | "github.com/go-kit/kit/endpoint" |
12 | kithttp "github.com/go-kit/kit/transport/http" | |
13 | ||
12 | 14 | "github.com/go-kit/kit/examples/shipping/cargo" |
13 | 15 | "github.com/go-kit/kit/examples/shipping/location" |
14 | 16 | "github.com/go-kit/kit/examples/shipping/voyage" |
15 | kithttp "github.com/go-kit/kit/transport/http" | |
16 | 17 | ) |
17 | 18 | |
18 | 19 | type proxyService struct { |
55 | 56 | type ServiceMiddleware func(Service) Service |
56 | 57 | |
57 | 58 | // NewProxyingMiddleware returns a new instance of a proxying middleware. |
58 | func NewProxyingMiddleware(proxyURL string, ctx context.Context) ServiceMiddleware { | |
59 | func NewProxyingMiddleware(ctx context.Context, proxyURL string) ServiceMiddleware { | |
59 | 60 | return func(next Service) Service { |
60 | 61 | var e endpoint.Endpoint |
61 | 62 | e = makeFetchRoutesEndpoint(ctx, proxyURL) |
12 | 12 | } |
13 | 13 | |
14 | 14 | // NewInstrumentingService returns an instance of an instrumenting Service. |
15 | func NewInstrumentingService(requestCount metrics.Counter, requestLatency metrics.Histogram, s Service) Service { | |
15 | func NewInstrumentingService(counter metrics.Counter, latency metrics.Histogram, s Service) Service { | |
16 | 16 | return &instrumentingService{ |
17 | requestCount: requestCount, | |
18 | requestLatency: requestLatency, | |
17 | requestCount: counter, | |
18 | requestLatency: latency, | |
19 | 19 | Service: s, |
20 | 20 | } |
21 | 21 | } |
36 | 36 | } |
37 | 37 | |
38 | 38 | // NewService returns a new instance of the default Service. |
39 | func NewService(cargos cargo.Repository, handlingEvents cargo.HandlingEventRepository) Service { | |
39 | func NewService(cargos cargo.Repository, events cargo.HandlingEventRepository) Service { | |
40 | 40 | return &service{ |
41 | 41 | cargos: cargos, |
42 | handlingEvents: handlingEvents, | |
42 | handlingEvents: events, | |
43 | 43 | } |
44 | 44 | } |
45 | 45 | |
70 | 70 | Expected bool `json:"expected"` |
71 | 71 | } |
72 | 72 | |
73 | func assemble(c *cargo.Cargo, her cargo.HandlingEventRepository) Cargo { | |
73 | func assemble(c *cargo.Cargo, events cargo.HandlingEventRepository) Cargo { | |
74 | 74 | return Cargo{ |
75 | 75 | TrackingID: string(c.TrackingID), |
76 | 76 | Origin: string(c.Origin), |
79 | 79 | NextExpectedActivity: nextExpectedActivity(c), |
80 | 80 | ArrivalDeadline: c.RouteSpecification.ArrivalDeadline, |
81 | 81 | StatusText: assembleStatusText(c), |
82 | Events: assembleEvents(c, her), | |
82 | Events: assembleEvents(c, events), | |
83 | 83 | } |
84 | 84 | } |
85 | 85 | |
128 | 128 | } |
129 | 129 | } |
130 | 130 | |
131 | func assembleEvents(c *cargo.Cargo, r cargo.HandlingEventRepository) []Event { | |
132 | h := r.QueryHandlingHistory(c.TrackingID) | |
131 | func assembleEvents(c *cargo.Cargo, handlingEvents cargo.HandlingEventRepository) []Event { | |
132 | h := handlingEvents.QueryHandlingHistory(c.TrackingID) | |
133 | 133 | |
134 | 134 | var events []Event |
135 | 135 | for _, e := range h.HandlingEvents { |
7 | 7 | "github.com/gorilla/mux" |
8 | 8 | "golang.org/x/net/context" |
9 | 9 | |
10 | "github.com/go-kit/kit/examples/shipping/cargo" | |
11 | 10 | kitlog "github.com/go-kit/kit/log" |
12 | 11 | kithttp "github.com/go-kit/kit/transport/http" |
12 | ||
13 | "github.com/go-kit/kit/examples/shipping/cargo" | |
13 | 14 | ) |
14 | 15 | |
15 | 16 | // MakeHandler returns a handler for the tracking service. |
5 | 5 | var ( |
6 | 6 | V100 = New("V100", Schedule{ |
7 | 7 | []CarrierMovement{ |
8 | {DepartureLocation: location.Hongkong, ArrivalLocation: location.Tokyo}, | |
9 | {DepartureLocation: location.Tokyo, ArrivalLocation: location.NewYork}, | |
8 | {DepartureLocation: location.CNHKG, ArrivalLocation: location.JNTKO}, | |
9 | {DepartureLocation: location.JNTKO, ArrivalLocation: location.USNYC}, | |
10 | 10 | }, |
11 | 11 | }) |
12 | 12 | |
13 | 13 | V300 = New("V300", Schedule{ |
14 | 14 | []CarrierMovement{ |
15 | {DepartureLocation: location.Tokyo, ArrivalLocation: location.Rotterdam}, | |
16 | {DepartureLocation: location.Rotterdam, ArrivalLocation: location.Hamburg}, | |
17 | {DepartureLocation: location.Hamburg, ArrivalLocation: location.Melbourne}, | |
18 | {DepartureLocation: location.Melbourne, ArrivalLocation: location.Tokyo}, | |
15 | {DepartureLocation: location.JNTKO, ArrivalLocation: location.NLRTM}, | |
16 | {DepartureLocation: location.NLRTM, ArrivalLocation: location.DEHAM}, | |
17 | {DepartureLocation: location.DEHAM, ArrivalLocation: location.AUMEL}, | |
18 | {DepartureLocation: location.AUMEL, ArrivalLocation: location.JNTKO}, | |
19 | 19 | }, |
20 | 20 | }) |
21 | 21 | |
22 | 22 | V400 = New("V400", Schedule{ |
23 | 23 | []CarrierMovement{ |
24 | {DepartureLocation: location.Hamburg, ArrivalLocation: location.Stockholm}, | |
25 | {DepartureLocation: location.Stockholm, ArrivalLocation: location.Helsinki}, | |
26 | {DepartureLocation: location.Helsinki, ArrivalLocation: location.Hamburg}, | |
24 | {DepartureLocation: location.DEHAM, ArrivalLocation: location.SESTO}, | |
25 | {DepartureLocation: location.SESTO, ArrivalLocation: location.FIHEL}, | |
26 | {DepartureLocation: location.FIHEL, ArrivalLocation: location.DEHAM}, | |
27 | 27 | }, |
28 | 28 | }) |
29 | 29 | ) |
28 | 28 | |
29 | 29 | // CarrierMovement is a vessel voyage from one location to another. |
30 | 30 | type CarrierMovement struct { |
31 | DepartureLocation location.Location | |
32 | ArrivalLocation location.Location | |
31 | DepartureLocation location.UNLocode | |
32 | ArrivalLocation location.UNLocode | |
33 | 33 | DepartureTime time.Time |
34 | 34 | ArrivalTime time.Time |
35 | 35 | } |