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