Moved to google.golang.org/genproto/googleapis/api/annotations
Fixes #52
This commit is contained in:
137
vendor/github.com/go-kit/kit/examples/shipping/cargo/cargo.go
generated
vendored
Normal file
137
vendor/github.com/go-kit/kit/examples/shipping/cargo/cargo.go
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
// Package cargo contains the heart of the domain model.
|
||||
package cargo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pborman/uuid"
|
||||
|
||||
"github.com/go-kit/kit/examples/shipping/location"
|
||||
)
|
||||
|
||||
// TrackingID uniquely identifies a particular cargo.
|
||||
type TrackingID string
|
||||
|
||||
// Cargo is the central class in the domain model.
|
||||
type Cargo struct {
|
||||
TrackingID TrackingID
|
||||
Origin location.UNLocode
|
||||
RouteSpecification RouteSpecification
|
||||
Itinerary Itinerary
|
||||
Delivery Delivery
|
||||
}
|
||||
|
||||
// SpecifyNewRoute specifies a new route for this cargo.
|
||||
func (c *Cargo) SpecifyNewRoute(rs RouteSpecification) {
|
||||
c.RouteSpecification = rs
|
||||
c.Delivery = c.Delivery.UpdateOnRouting(c.RouteSpecification, c.Itinerary)
|
||||
}
|
||||
|
||||
// AssignToRoute attaches a new itinerary to this cargo.
|
||||
func (c *Cargo) AssignToRoute(itinerary Itinerary) {
|
||||
c.Itinerary = itinerary
|
||||
c.Delivery = c.Delivery.UpdateOnRouting(c.RouteSpecification, c.Itinerary)
|
||||
}
|
||||
|
||||
// DeriveDeliveryProgress updates all aspects of the cargo aggregate status
|
||||
// based on the current route specification, itinerary and handling of the cargo.
|
||||
func (c *Cargo) DeriveDeliveryProgress(history HandlingHistory) {
|
||||
c.Delivery = DeriveDeliveryFrom(c.RouteSpecification, c.Itinerary, history)
|
||||
}
|
||||
|
||||
// New creates a new, unrouted cargo.
|
||||
func New(id TrackingID, rs RouteSpecification) *Cargo {
|
||||
itinerary := Itinerary{}
|
||||
history := HandlingHistory{make([]HandlingEvent, 0)}
|
||||
|
||||
return &Cargo{
|
||||
TrackingID: id,
|
||||
Origin: rs.Origin,
|
||||
RouteSpecification: rs,
|
||||
Delivery: DeriveDeliveryFrom(rs, itinerary, history),
|
||||
}
|
||||
}
|
||||
|
||||
// Repository provides access a cargo store.
|
||||
type Repository interface {
|
||||
Store(cargo *Cargo) error
|
||||
Find(id TrackingID) (*Cargo, error)
|
||||
FindAll() []*Cargo
|
||||
}
|
||||
|
||||
// ErrUnknown is used when a cargo could not be found.
|
||||
var ErrUnknown = errors.New("unknown cargo")
|
||||
|
||||
// NextTrackingID generates a new tracking ID.
|
||||
// TODO: Move to infrastructure(?)
|
||||
func NextTrackingID() TrackingID {
|
||||
return TrackingID(strings.Split(strings.ToUpper(uuid.New()), "-")[0])
|
||||
}
|
||||
|
||||
// RouteSpecification Contains information about a route: its origin,
|
||||
// destination and arrival deadline.
|
||||
type RouteSpecification struct {
|
||||
Origin location.UNLocode
|
||||
Destination location.UNLocode
|
||||
ArrivalDeadline time.Time
|
||||
}
|
||||
|
||||
// IsSatisfiedBy checks whether provided itinerary satisfies this
|
||||
// specification.
|
||||
func (s RouteSpecification) IsSatisfiedBy(itinerary Itinerary) bool {
|
||||
return itinerary.Legs != nil &&
|
||||
s.Origin == itinerary.InitialDepartureLocation() &&
|
||||
s.Destination == itinerary.FinalArrivalLocation()
|
||||
}
|
||||
|
||||
// RoutingStatus describes status of cargo routing.
|
||||
type RoutingStatus int
|
||||
|
||||
// Valid routing statuses.
|
||||
const (
|
||||
NotRouted RoutingStatus = iota
|
||||
Misrouted
|
||||
Routed
|
||||
)
|
||||
|
||||
func (s RoutingStatus) String() string {
|
||||
switch s {
|
||||
case NotRouted:
|
||||
return "Not routed"
|
||||
case Misrouted:
|
||||
return "Misrouted"
|
||||
case Routed:
|
||||
return "Routed"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// TransportStatus describes status of cargo transportation.
|
||||
type TransportStatus int
|
||||
|
||||
// Valid transport statuses.
|
||||
const (
|
||||
NotReceived TransportStatus = iota
|
||||
InPort
|
||||
OnboardCarrier
|
||||
Claimed
|
||||
Unknown
|
||||
)
|
||||
|
||||
func (s TransportStatus) String() string {
|
||||
switch s {
|
||||
case NotReceived:
|
||||
return "Not received"
|
||||
case InPort:
|
||||
return "In port"
|
||||
case OnboardCarrier:
|
||||
return "Onboard carrier"
|
||||
case Claimed:
|
||||
return "Claimed"
|
||||
case Unknown:
|
||||
return "Unknown"
|
||||
}
|
||||
return ""
|
||||
}
|
174
vendor/github.com/go-kit/kit/examples/shipping/cargo/delivery.go
generated
vendored
Normal file
174
vendor/github.com/go-kit/kit/examples/shipping/cargo/delivery.go
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
package cargo
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/examples/shipping/location"
|
||||
"github.com/go-kit/kit/examples/shipping/voyage"
|
||||
)
|
||||
|
||||
// Delivery is the actual transportation of the cargo, as opposed to the
|
||||
// customer requirement (RouteSpecification) and the plan (Itinerary).
|
||||
type Delivery struct {
|
||||
Itinerary Itinerary
|
||||
RouteSpecification RouteSpecification
|
||||
RoutingStatus RoutingStatus
|
||||
TransportStatus TransportStatus
|
||||
NextExpectedActivity HandlingActivity
|
||||
LastEvent HandlingEvent
|
||||
LastKnownLocation location.UNLocode
|
||||
CurrentVoyage voyage.Number
|
||||
ETA time.Time
|
||||
IsMisdirected bool
|
||||
IsUnloadedAtDestination bool
|
||||
}
|
||||
|
||||
// UpdateOnRouting creates a new delivery snapshot to reflect changes in
|
||||
// routing, i.e. when the route specification or the itinerary has changed but
|
||||
// no additional handling of the cargo has been performed.
|
||||
func (d Delivery) UpdateOnRouting(rs RouteSpecification, itinerary Itinerary) Delivery {
|
||||
return newDelivery(d.LastEvent, itinerary, rs)
|
||||
}
|
||||
|
||||
// IsOnTrack checks if the delivery is on track.
|
||||
func (d Delivery) IsOnTrack() bool {
|
||||
return d.RoutingStatus == Routed && !d.IsMisdirected
|
||||
}
|
||||
|
||||
// DeriveDeliveryFrom creates a new delivery snapshot based on the complete
|
||||
// handling history of a cargo, as well as its route specification and
|
||||
// itinerary.
|
||||
func DeriveDeliveryFrom(rs RouteSpecification, itinerary Itinerary, history HandlingHistory) Delivery {
|
||||
lastEvent, _ := history.MostRecentlyCompletedEvent()
|
||||
return newDelivery(lastEvent, itinerary, rs)
|
||||
}
|
||||
|
||||
// newDelivery creates a up-to-date delivery based on an handling event,
|
||||
// itinerary and a route specification.
|
||||
func newDelivery(lastEvent HandlingEvent, itinerary Itinerary, rs RouteSpecification) Delivery {
|
||||
var (
|
||||
routingStatus = calculateRoutingStatus(itinerary, rs)
|
||||
transportStatus = calculateTransportStatus(lastEvent)
|
||||
lastKnownLocation = calculateLastKnownLocation(lastEvent)
|
||||
isMisdirected = calculateMisdirectedStatus(lastEvent, itinerary)
|
||||
isUnloadedAtDestination = calculateUnloadedAtDestination(lastEvent, rs)
|
||||
currentVoyage = calculateCurrentVoyage(transportStatus, lastEvent)
|
||||
)
|
||||
|
||||
d := Delivery{
|
||||
LastEvent: lastEvent,
|
||||
Itinerary: itinerary,
|
||||
RouteSpecification: rs,
|
||||
RoutingStatus: routingStatus,
|
||||
TransportStatus: transportStatus,
|
||||
LastKnownLocation: lastKnownLocation,
|
||||
IsMisdirected: isMisdirected,
|
||||
IsUnloadedAtDestination: isUnloadedAtDestination,
|
||||
CurrentVoyage: currentVoyage,
|
||||
}
|
||||
|
||||
d.NextExpectedActivity = calculateNextExpectedActivity(d)
|
||||
d.ETA = calculateETA(d)
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// Below are internal functions used when creating a new delivery.
|
||||
|
||||
func calculateRoutingStatus(itinerary Itinerary, rs RouteSpecification) RoutingStatus {
|
||||
if itinerary.Legs == nil {
|
||||
return NotRouted
|
||||
}
|
||||
|
||||
if rs.IsSatisfiedBy(itinerary) {
|
||||
return Routed
|
||||
}
|
||||
|
||||
return Misrouted
|
||||
}
|
||||
|
||||
func calculateMisdirectedStatus(event HandlingEvent, itinerary Itinerary) bool {
|
||||
if event.Activity.Type == NotHandled {
|
||||
return false
|
||||
}
|
||||
|
||||
return !itinerary.IsExpected(event)
|
||||
}
|
||||
|
||||
func calculateUnloadedAtDestination(event HandlingEvent, rs RouteSpecification) bool {
|
||||
if event.Activity.Type == NotHandled {
|
||||
return false
|
||||
}
|
||||
|
||||
return event.Activity.Type == Unload && rs.Destination == event.Activity.Location
|
||||
}
|
||||
|
||||
func calculateTransportStatus(event HandlingEvent) TransportStatus {
|
||||
switch event.Activity.Type {
|
||||
case NotHandled:
|
||||
return NotReceived
|
||||
case Load:
|
||||
return OnboardCarrier
|
||||
case Unload:
|
||||
return InPort
|
||||
case Receive:
|
||||
return InPort
|
||||
case Customs:
|
||||
return InPort
|
||||
case Claim:
|
||||
return Claimed
|
||||
}
|
||||
return Unknown
|
||||
}
|
||||
|
||||
func calculateLastKnownLocation(event HandlingEvent) location.UNLocode {
|
||||
return event.Activity.Location
|
||||
}
|
||||
|
||||
func calculateNextExpectedActivity(d Delivery) HandlingActivity {
|
||||
if !d.IsOnTrack() {
|
||||
return HandlingActivity{}
|
||||
}
|
||||
|
||||
switch d.LastEvent.Activity.Type {
|
||||
case NotHandled:
|
||||
return HandlingActivity{Type: Receive, Location: d.RouteSpecification.Origin}
|
||||
case Receive:
|
||||
l := d.Itinerary.Legs[0]
|
||||
return HandlingActivity{Type: Load, Location: l.LoadLocation, VoyageNumber: l.VoyageNumber}
|
||||
case Load:
|
||||
for _, l := range d.Itinerary.Legs {
|
||||
if l.LoadLocation == d.LastEvent.Activity.Location {
|
||||
return HandlingActivity{Type: Unload, Location: l.UnloadLocation, VoyageNumber: l.VoyageNumber}
|
||||
}
|
||||
}
|
||||
case Unload:
|
||||
for i, l := range d.Itinerary.Legs {
|
||||
if l.UnloadLocation == d.LastEvent.Activity.Location {
|
||||
if i < len(d.Itinerary.Legs)-1 {
|
||||
return HandlingActivity{Type: Load, Location: d.Itinerary.Legs[i+1].LoadLocation, VoyageNumber: d.Itinerary.Legs[i+1].VoyageNumber}
|
||||
}
|
||||
|
||||
return HandlingActivity{Type: Claim, Location: l.UnloadLocation}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return HandlingActivity{}
|
||||
}
|
||||
|
||||
func calculateCurrentVoyage(transportStatus TransportStatus, event HandlingEvent) voyage.Number {
|
||||
if transportStatus == OnboardCarrier && event.Activity.Type != NotHandled {
|
||||
return event.Activity.VoyageNumber
|
||||
}
|
||||
|
||||
return voyage.Number("")
|
||||
}
|
||||
|
||||
func calculateETA(d Delivery) time.Time {
|
||||
if !d.IsOnTrack() {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
return d.Itinerary.FinalArrivalTime()
|
||||
}
|
121
vendor/github.com/go-kit/kit/examples/shipping/cargo/handling.go
generated
vendored
Normal file
121
vendor/github.com/go-kit/kit/examples/shipping/cargo/handling.go
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
package cargo
|
||||
|
||||
// TODO: It would make sense to have this in its own package. Unfortunately,
|
||||
// then there would be a circular dependency between the cargo and handling
|
||||
// packages since cargo.Delivery would use handling.HandlingEvent and
|
||||
// handling.HandlingEvent would use cargo.TrackingID. Also,
|
||||
// HandlingEventFactory depends on the cargo repository.
|
||||
//
|
||||
// It would make sense not having the cargo package depend on handling.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/examples/shipping/location"
|
||||
"github.com/go-kit/kit/examples/shipping/voyage"
|
||||
)
|
||||
|
||||
// HandlingActivity represents how and where a cargo can be handled, and can
|
||||
// be used to express predictions about what is expected to happen to a cargo
|
||||
// in the future.
|
||||
type HandlingActivity struct {
|
||||
Type HandlingEventType
|
||||
Location location.UNLocode
|
||||
VoyageNumber voyage.Number
|
||||
}
|
||||
|
||||
// HandlingEvent is used to register the event when, for instance, a cargo is
|
||||
// unloaded from a carrier at a some location at a given time.
|
||||
type HandlingEvent struct {
|
||||
TrackingID TrackingID
|
||||
Activity HandlingActivity
|
||||
}
|
||||
|
||||
// HandlingEventType describes type of a handling event.
|
||||
type HandlingEventType int
|
||||
|
||||
// Valid handling event types.
|
||||
const (
|
||||
NotHandled HandlingEventType = iota
|
||||
Load
|
||||
Unload
|
||||
Receive
|
||||
Claim
|
||||
Customs
|
||||
)
|
||||
|
||||
func (t HandlingEventType) String() string {
|
||||
switch t {
|
||||
case NotHandled:
|
||||
return "Not Handled"
|
||||
case Load:
|
||||
return "Load"
|
||||
case Unload:
|
||||
return "Unload"
|
||||
case Receive:
|
||||
return "Receive"
|
||||
case Claim:
|
||||
return "Claim"
|
||||
case Customs:
|
||||
return "Customs"
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// HandlingHistory is the handling history of a cargo.
|
||||
type HandlingHistory struct {
|
||||
HandlingEvents []HandlingEvent
|
||||
}
|
||||
|
||||
// MostRecentlyCompletedEvent returns most recently completed handling event.
|
||||
func (h HandlingHistory) MostRecentlyCompletedEvent() (HandlingEvent, error) {
|
||||
if len(h.HandlingEvents) == 0 {
|
||||
return HandlingEvent{}, errors.New("delivery history is empty")
|
||||
}
|
||||
|
||||
return h.HandlingEvents[len(h.HandlingEvents)-1], nil
|
||||
}
|
||||
|
||||
// HandlingEventRepository provides access a handling event store.
|
||||
type HandlingEventRepository interface {
|
||||
Store(e HandlingEvent)
|
||||
QueryHandlingHistory(TrackingID) HandlingHistory
|
||||
}
|
||||
|
||||
// HandlingEventFactory creates handling events.
|
||||
type HandlingEventFactory struct {
|
||||
CargoRepository Repository
|
||||
VoyageRepository voyage.Repository
|
||||
LocationRepository location.Repository
|
||||
}
|
||||
|
||||
// CreateHandlingEvent creates a validated handling event.
|
||||
func (f *HandlingEventFactory) CreateHandlingEvent(registered time.Time, completed time.Time, id TrackingID,
|
||||
voyageNumber voyage.Number, unLocode location.UNLocode, eventType HandlingEventType) (HandlingEvent, error) {
|
||||
|
||||
if _, err := f.CargoRepository.Find(id); err != nil {
|
||||
return HandlingEvent{}, err
|
||||
}
|
||||
|
||||
if _, err := f.VoyageRepository.Find(voyageNumber); err != nil {
|
||||
// TODO: This is pretty ugly, but when creating a Receive event, the voyage number is not known.
|
||||
if len(voyageNumber) > 0 {
|
||||
return HandlingEvent{}, err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := f.LocationRepository.Find(unLocode); err != nil {
|
||||
return HandlingEvent{}, err
|
||||
}
|
||||
|
||||
return HandlingEvent{
|
||||
TrackingID: id,
|
||||
Activity: HandlingActivity{
|
||||
Type: eventType,
|
||||
Location: unLocode,
|
||||
VoyageNumber: voyageNumber,
|
||||
},
|
||||
}, nil
|
||||
}
|
91
vendor/github.com/go-kit/kit/examples/shipping/cargo/itinerary.go
generated
vendored
Normal file
91
vendor/github.com/go-kit/kit/examples/shipping/cargo/itinerary.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
package cargo
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/examples/shipping/location"
|
||||
"github.com/go-kit/kit/examples/shipping/voyage"
|
||||
)
|
||||
|
||||
// Leg describes the transportation between two locations on a voyage.
|
||||
type Leg struct {
|
||||
VoyageNumber voyage.Number `json:"voyage_number"`
|
||||
LoadLocation location.UNLocode `json:"from"`
|
||||
UnloadLocation location.UNLocode `json:"to"`
|
||||
LoadTime time.Time `json:"load_time"`
|
||||
UnloadTime time.Time `json:"unload_time"`
|
||||
}
|
||||
|
||||
// NewLeg creates a new itinerary leg.
|
||||
func NewLeg(voyageNumber voyage.Number, loadLocation, unloadLocation location.UNLocode, loadTime, unloadTime time.Time) Leg {
|
||||
return Leg{
|
||||
VoyageNumber: voyageNumber,
|
||||
LoadLocation: loadLocation,
|
||||
UnloadLocation: unloadLocation,
|
||||
LoadTime: loadTime,
|
||||
UnloadTime: unloadTime,
|
||||
}
|
||||
}
|
||||
|
||||
// Itinerary specifies steps required to transport a cargo from its origin to
|
||||
// destination.
|
||||
type Itinerary struct {
|
||||
Legs []Leg `json:"legs"`
|
||||
}
|
||||
|
||||
// InitialDepartureLocation returns the start of the itinerary.
|
||||
func (i Itinerary) InitialDepartureLocation() location.UNLocode {
|
||||
if i.IsEmpty() {
|
||||
return location.UNLocode("")
|
||||
}
|
||||
return i.Legs[0].LoadLocation
|
||||
}
|
||||
|
||||
// FinalArrivalLocation returns the end of the itinerary.
|
||||
func (i Itinerary) FinalArrivalLocation() location.UNLocode {
|
||||
if i.IsEmpty() {
|
||||
return location.UNLocode("")
|
||||
}
|
||||
return i.Legs[len(i.Legs)-1].UnloadLocation
|
||||
}
|
||||
|
||||
// FinalArrivalTime returns the expected arrival time at final destination.
|
||||
func (i Itinerary) FinalArrivalTime() time.Time {
|
||||
return i.Legs[len(i.Legs)-1].UnloadTime
|
||||
}
|
||||
|
||||
// IsEmpty checks if the itinerary contains at least one leg.
|
||||
func (i Itinerary) IsEmpty() bool {
|
||||
return i.Legs == nil || len(i.Legs) == 0
|
||||
}
|
||||
|
||||
// IsExpected checks if the given handling event is expected when executing
|
||||
// this itinerary.
|
||||
func (i Itinerary) IsExpected(event HandlingEvent) bool {
|
||||
if i.IsEmpty() {
|
||||
return true
|
||||
}
|
||||
|
||||
switch event.Activity.Type {
|
||||
case Receive:
|
||||
return i.InitialDepartureLocation() == event.Activity.Location
|
||||
case Load:
|
||||
for _, l := range i.Legs {
|
||||
if l.LoadLocation == event.Activity.Location && l.VoyageNumber == event.Activity.VoyageNumber {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
case Unload:
|
||||
for _, l := range i.Legs {
|
||||
if l.UnloadLocation == event.Activity.Location && l.VoyageNumber == event.Activity.VoyageNumber {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
case Claim:
|
||||
return i.FinalArrivalLocation() == event.Activity.Location
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
Reference in New Issue
Block a user