1package cargo 2 3// TODO: It would make sense to have this in its own package. Unfortunately, 4// then there would be a circular dependency between the cargo and handling 5// packages since cargo.Delivery would use handling.HandlingEvent and 6// handling.HandlingEvent would use cargo.TrackingID. Also, 7// HandlingEventFactory depends on the cargo repository. 8// 9// It would make sense not having the cargo package depend on handling. 10 11import ( 12 "errors" 13 "time" 14 15 "github.com/go-kit/kit/examples/shipping/location" 16 "github.com/go-kit/kit/examples/shipping/voyage" 17) 18 19// HandlingActivity represents how and where a cargo can be handled, and can 20// be used to express predictions about what is expected to happen to a cargo 21// in the future. 22type HandlingActivity struct { 23 Type HandlingEventType 24 Location location.UNLocode 25 VoyageNumber voyage.Number 26} 27 28// HandlingEvent is used to register the event when, for instance, a cargo is 29// unloaded from a carrier at a some location at a given time. 30type HandlingEvent struct { 31 TrackingID TrackingID 32 Activity HandlingActivity 33} 34 35// HandlingEventType describes type of a handling event. 36type HandlingEventType int 37 38// Valid handling event types. 39const ( 40 NotHandled HandlingEventType = iota 41 Load 42 Unload 43 Receive 44 Claim 45 Customs 46) 47 48func (t HandlingEventType) String() string { 49 switch t { 50 case NotHandled: 51 return "Not Handled" 52 case Load: 53 return "Load" 54 case Unload: 55 return "Unload" 56 case Receive: 57 return "Receive" 58 case Claim: 59 return "Claim" 60 case Customs: 61 return "Customs" 62 } 63 64 return "" 65} 66 67// HandlingHistory is the handling history of a cargo. 68type HandlingHistory struct { 69 HandlingEvents []HandlingEvent 70} 71 72// MostRecentlyCompletedEvent returns most recently completed handling event. 73func (h HandlingHistory) MostRecentlyCompletedEvent() (HandlingEvent, error) { 74 if len(h.HandlingEvents) == 0 { 75 return HandlingEvent{}, errors.New("delivery history is empty") 76 } 77 78 return h.HandlingEvents[len(h.HandlingEvents)-1], nil 79} 80 81// HandlingEventRepository provides access a handling event store. 82type HandlingEventRepository interface { 83 Store(e HandlingEvent) 84 QueryHandlingHistory(TrackingID) HandlingHistory 85} 86 87// HandlingEventFactory creates handling events. 88type HandlingEventFactory struct { 89 CargoRepository Repository 90 VoyageRepository voyage.Repository 91 LocationRepository location.Repository 92} 93 94// CreateHandlingEvent creates a validated handling event. 95func (f *HandlingEventFactory) CreateHandlingEvent(registered time.Time, completed time.Time, id TrackingID, 96 voyageNumber voyage.Number, unLocode location.UNLocode, eventType HandlingEventType) (HandlingEvent, error) { 97 98 if _, err := f.CargoRepository.Find(id); err != nil { 99 return HandlingEvent{}, err 100 } 101 102 if _, err := f.VoyageRepository.Find(voyageNumber); err != nil { 103 // TODO: This is pretty ugly, but when creating a Receive event, the voyage number is not known. 104 if len(voyageNumber) > 0 { 105 return HandlingEvent{}, err 106 } 107 } 108 109 if _, err := f.LocationRepository.Find(unLocode); err != nil { 110 return HandlingEvent{}, err 111 } 112 113 return HandlingEvent{ 114 TrackingID: id, 115 Activity: HandlingActivity{ 116 Type: eventType, 117 Location: unLocode, 118 VoyageNumber: voyageNumber, 119 }, 120 }, nil 121} 122