Moved to google.golang.org/genproto/googleapis/api/annotations
Fixes #52
This commit is contained in:
25
vendor/github.com/go-kit/kit/examples/shipping/README.md
generated
vendored
Normal file
25
vendor/github.com/go-kit/kit/examples/shipping/README.md
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# shipping
|
||||
|
||||
This example demonstrates a more real-world application consisting of multiple services.
|
||||
|
||||
## Description
|
||||
|
||||
The implementation is based on the container shipping domain from the [Domain Driven Design](http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215) book by Eric Evans, which was [originally](http://dddsample.sourceforge.net/) implemented in Java but has since been ported to Go. This example is a somewhat stripped down version to demonstrate the use of Go kit. The [original Go application](https://github.com/marcusolsson/goddd) is maintained separately and accompanied by an [AngularJS application](https://github.com/marcusolsson/dddelivery-angularjs) as well as a mock [routing service](https://github.com/marcusolsson/pathfinder).
|
||||
|
||||
### Organization
|
||||
|
||||
The application consists of three application services, `booking`, `handling` and `tracking`. Each of these is an individual Go kit service as seen in previous examples.
|
||||
|
||||
- __booking__ - used by the shipping company to book and route cargos.
|
||||
- __handling__ - used by our staff around the world to register whenever the cargo has been received, loaded etc.
|
||||
- __tracking__ - used by the customer to track the cargo along the route
|
||||
|
||||
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.
|
||||
|
||||
`inmem` contains in-memory implementations for the repositories found in the domain packages.
|
||||
|
||||
The `routing` package provides a _domain service_ that is used to query an external application for possible routes.
|
||||
|
||||
## Contributing
|
||||
|
||||
As with all Go kit examples you are more than welcome to contribute. If you do however, please consider contributing back to the original project as well.
|
139
vendor/github.com/go-kit/kit/examples/shipping/booking/endpoint.go
generated
vendored
Normal file
139
vendor/github.com/go-kit/kit/examples/shipping/booking/endpoint.go
generated
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
package booking
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
|
||||
"github.com/go-kit/kit/examples/shipping/cargo"
|
||||
"github.com/go-kit/kit/examples/shipping/location"
|
||||
)
|
||||
|
||||
type bookCargoRequest struct {
|
||||
Origin location.UNLocode
|
||||
Destination location.UNLocode
|
||||
ArrivalDeadline time.Time
|
||||
}
|
||||
|
||||
type bookCargoResponse struct {
|
||||
ID cargo.TrackingID `json:"tracking_id,omitempty"`
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r bookCargoResponse) error() error { return r.Err }
|
||||
|
||||
func makeBookCargoEndpoint(s Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(bookCargoRequest)
|
||||
id, err := s.BookNewCargo(req.Origin, req.Destination, req.ArrivalDeadline)
|
||||
return bookCargoResponse{ID: id, Err: err}, nil
|
||||
}
|
||||
}
|
||||
|
||||
type loadCargoRequest struct {
|
||||
ID cargo.TrackingID
|
||||
}
|
||||
|
||||
type loadCargoResponse struct {
|
||||
Cargo *Cargo `json:"cargo,omitempty"`
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r loadCargoResponse) error() error { return r.Err }
|
||||
|
||||
func makeLoadCargoEndpoint(s Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(loadCargoRequest)
|
||||
c, err := s.LoadCargo(req.ID)
|
||||
return loadCargoResponse{Cargo: &c, Err: err}, nil
|
||||
}
|
||||
}
|
||||
|
||||
type requestRoutesRequest struct {
|
||||
ID cargo.TrackingID
|
||||
}
|
||||
|
||||
type requestRoutesResponse struct {
|
||||
Routes []cargo.Itinerary `json:"routes,omitempty"`
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r requestRoutesResponse) error() error { return r.Err }
|
||||
|
||||
func makeRequestRoutesEndpoint(s Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(requestRoutesRequest)
|
||||
itin := s.RequestPossibleRoutesForCargo(req.ID)
|
||||
return requestRoutesResponse{Routes: itin, Err: nil}, nil
|
||||
}
|
||||
}
|
||||
|
||||
type assignToRouteRequest struct {
|
||||
ID cargo.TrackingID
|
||||
Itinerary cargo.Itinerary
|
||||
}
|
||||
|
||||
type assignToRouteResponse struct {
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r assignToRouteResponse) error() error { return r.Err }
|
||||
|
||||
func makeAssignToRouteEndpoint(s Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(assignToRouteRequest)
|
||||
err := s.AssignCargoToRoute(req.ID, req.Itinerary)
|
||||
return assignToRouteResponse{Err: err}, nil
|
||||
}
|
||||
}
|
||||
|
||||
type changeDestinationRequest struct {
|
||||
ID cargo.TrackingID
|
||||
Destination location.UNLocode
|
||||
}
|
||||
|
||||
type changeDestinationResponse struct {
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r changeDestinationResponse) error() error { return r.Err }
|
||||
|
||||
func makeChangeDestinationEndpoint(s Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(changeDestinationRequest)
|
||||
err := s.ChangeDestination(req.ID, req.Destination)
|
||||
return changeDestinationResponse{Err: err}, nil
|
||||
}
|
||||
}
|
||||
|
||||
type listCargosRequest struct{}
|
||||
|
||||
type listCargosResponse struct {
|
||||
Cargos []Cargo `json:"cargos,omitempty"`
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r listCargosResponse) error() error { return r.Err }
|
||||
|
||||
func makeListCargosEndpoint(s Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
_ = request.(listCargosRequest)
|
||||
return listCargosResponse{Cargos: s.Cargos(), Err: nil}, nil
|
||||
}
|
||||
}
|
||||
|
||||
type listLocationsRequest struct {
|
||||
}
|
||||
|
||||
type listLocationsResponse struct {
|
||||
Locations []Location `json:"locations,omitempty"`
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func makeListLocationsEndpoint(s Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
_ = request.(listLocationsRequest)
|
||||
return listLocationsResponse{Locations: s.Locations(), Err: nil}, nil
|
||||
}
|
||||
}
|
88
vendor/github.com/go-kit/kit/examples/shipping/booking/instrumenting.go
generated
vendored
Normal file
88
vendor/github.com/go-kit/kit/examples/shipping/booking/instrumenting.go
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
package booking
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/metrics"
|
||||
|
||||
"github.com/go-kit/kit/examples/shipping/cargo"
|
||||
"github.com/go-kit/kit/examples/shipping/location"
|
||||
)
|
||||
|
||||
type instrumentingService struct {
|
||||
requestCount metrics.Counter
|
||||
requestLatency metrics.Histogram
|
||||
Service
|
||||
}
|
||||
|
||||
// NewInstrumentingService returns an instance of an instrumenting Service.
|
||||
func NewInstrumentingService(counter metrics.Counter, latency metrics.Histogram, s Service) Service {
|
||||
return &instrumentingService{
|
||||
requestCount: counter,
|
||||
requestLatency: latency,
|
||||
Service: s,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *instrumentingService) BookNewCargo(origin, destination location.UNLocode, deadline time.Time) (cargo.TrackingID, error) {
|
||||
defer func(begin time.Time) {
|
||||
s.requestCount.With("method", "book").Add(1)
|
||||
s.requestLatency.With("method", "book").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return s.Service.BookNewCargo(origin, destination, deadline)
|
||||
}
|
||||
|
||||
func (s *instrumentingService) LoadCargo(id cargo.TrackingID) (c Cargo, err error) {
|
||||
defer func(begin time.Time) {
|
||||
s.requestCount.With("method", "load").Add(1)
|
||||
s.requestLatency.With("method", "load").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return s.Service.LoadCargo(id)
|
||||
}
|
||||
|
||||
func (s *instrumentingService) RequestPossibleRoutesForCargo(id cargo.TrackingID) []cargo.Itinerary {
|
||||
defer func(begin time.Time) {
|
||||
s.requestCount.With("method", "request_routes").Add(1)
|
||||
s.requestLatency.With("method", "request_routes").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return s.Service.RequestPossibleRoutesForCargo(id)
|
||||
}
|
||||
|
||||
func (s *instrumentingService) AssignCargoToRoute(id cargo.TrackingID, itinerary cargo.Itinerary) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
s.requestCount.With("method", "assign_to_route").Add(1)
|
||||
s.requestLatency.With("method", "assign_to_route").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return s.Service.AssignCargoToRoute(id, itinerary)
|
||||
}
|
||||
|
||||
func (s *instrumentingService) ChangeDestination(id cargo.TrackingID, l location.UNLocode) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
s.requestCount.With("method", "change_destination").Add(1)
|
||||
s.requestLatency.With("method", "change_destination").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return s.Service.ChangeDestination(id, l)
|
||||
}
|
||||
|
||||
func (s *instrumentingService) Cargos() []Cargo {
|
||||
defer func(begin time.Time) {
|
||||
s.requestCount.With("method", "list_cargos").Add(1)
|
||||
s.requestLatency.With("method", "list_cargos").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return s.Service.Cargos()
|
||||
}
|
||||
|
||||
func (s *instrumentingService) Locations() []Location {
|
||||
defer func(begin time.Time) {
|
||||
s.requestCount.With("method", "list_locations").Add(1)
|
||||
s.requestLatency.With("method", "list_locations").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return s.Service.Locations()
|
||||
}
|
102
vendor/github.com/go-kit/kit/examples/shipping/booking/logging.go
generated
vendored
Normal file
102
vendor/github.com/go-kit/kit/examples/shipping/booking/logging.go
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
package booking
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
|
||||
"github.com/go-kit/kit/examples/shipping/cargo"
|
||||
"github.com/go-kit/kit/examples/shipping/location"
|
||||
)
|
||||
|
||||
type loggingService struct {
|
||||
logger log.Logger
|
||||
Service
|
||||
}
|
||||
|
||||
// NewLoggingService returns a new instance of a logging Service.
|
||||
func NewLoggingService(logger log.Logger, s Service) Service {
|
||||
return &loggingService{logger, s}
|
||||
}
|
||||
|
||||
func (s *loggingService) BookNewCargo(origin location.UNLocode, destination location.UNLocode, deadline time.Time) (id cargo.TrackingID, err error) {
|
||||
defer func(begin time.Time) {
|
||||
s.logger.Log(
|
||||
"method", "book",
|
||||
"origin", origin,
|
||||
"destination", destination,
|
||||
"arrival_deadline", deadline,
|
||||
"took", time.Since(begin),
|
||||
"err", err,
|
||||
)
|
||||
}(time.Now())
|
||||
return s.Service.BookNewCargo(origin, destination, deadline)
|
||||
}
|
||||
|
||||
func (s *loggingService) LoadCargo(id cargo.TrackingID) (c Cargo, err error) {
|
||||
defer func(begin time.Time) {
|
||||
s.logger.Log(
|
||||
"method", "load",
|
||||
"tracking_id", id,
|
||||
"took", time.Since(begin),
|
||||
"err", err,
|
||||
)
|
||||
}(time.Now())
|
||||
return s.Service.LoadCargo(id)
|
||||
}
|
||||
|
||||
func (s *loggingService) RequestPossibleRoutesForCargo(id cargo.TrackingID) []cargo.Itinerary {
|
||||
defer func(begin time.Time) {
|
||||
s.logger.Log(
|
||||
"method", "request_routes",
|
||||
"tracking_id", id,
|
||||
"took", time.Since(begin),
|
||||
)
|
||||
}(time.Now())
|
||||
return s.Service.RequestPossibleRoutesForCargo(id)
|
||||
}
|
||||
|
||||
func (s *loggingService) AssignCargoToRoute(id cargo.TrackingID, itinerary cargo.Itinerary) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
s.logger.Log(
|
||||
"method", "assign_to_route",
|
||||
"tracking_id", id,
|
||||
"took", time.Since(begin),
|
||||
"err", err,
|
||||
)
|
||||
}(time.Now())
|
||||
return s.Service.AssignCargoToRoute(id, itinerary)
|
||||
}
|
||||
|
||||
func (s *loggingService) ChangeDestination(id cargo.TrackingID, l location.UNLocode) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
s.logger.Log(
|
||||
"method", "change_destination",
|
||||
"tracking_id", id,
|
||||
"destination", l,
|
||||
"took", time.Since(begin),
|
||||
"err", err,
|
||||
)
|
||||
}(time.Now())
|
||||
return s.Service.ChangeDestination(id, l)
|
||||
}
|
||||
|
||||
func (s *loggingService) Cargos() []Cargo {
|
||||
defer func(begin time.Time) {
|
||||
s.logger.Log(
|
||||
"method", "list_cargos",
|
||||
"took", time.Since(begin),
|
||||
)
|
||||
}(time.Now())
|
||||
return s.Service.Cargos()
|
||||
}
|
||||
|
||||
func (s *loggingService) Locations() []Location {
|
||||
defer func(begin time.Time) {
|
||||
s.logger.Log(
|
||||
"method", "list_locations",
|
||||
"took", time.Since(begin),
|
||||
)
|
||||
}(time.Now())
|
||||
return s.Service.Locations()
|
||||
}
|
197
vendor/github.com/go-kit/kit/examples/shipping/booking/service.go
generated
vendored
Normal file
197
vendor/github.com/go-kit/kit/examples/shipping/booking/service.go
generated
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
// Package booking provides the use-case of booking a cargo. Used by views
|
||||
// facing an administrator.
|
||||
package booking
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/examples/shipping/cargo"
|
||||
"github.com/go-kit/kit/examples/shipping/location"
|
||||
"github.com/go-kit/kit/examples/shipping/routing"
|
||||
)
|
||||
|
||||
// ErrInvalidArgument is returned when one or more arguments are invalid.
|
||||
var ErrInvalidArgument = errors.New("invalid argument")
|
||||
|
||||
// Service is the interface that provides booking methods.
|
||||
type Service interface {
|
||||
// BookNewCargo registers a new cargo in the tracking system, not yet
|
||||
// routed.
|
||||
BookNewCargo(origin location.UNLocode, destination location.UNLocode, deadline time.Time) (cargo.TrackingID, error)
|
||||
|
||||
// LoadCargo returns a read model of a cargo.
|
||||
LoadCargo(id cargo.TrackingID) (Cargo, error)
|
||||
|
||||
// RequestPossibleRoutesForCargo requests a list of itineraries describing
|
||||
// possible routes for this cargo.
|
||||
RequestPossibleRoutesForCargo(id cargo.TrackingID) []cargo.Itinerary
|
||||
|
||||
// AssignCargoToRoute assigns a cargo to the route specified by the
|
||||
// itinerary.
|
||||
AssignCargoToRoute(id cargo.TrackingID, itinerary cargo.Itinerary) error
|
||||
|
||||
// ChangeDestination changes the destination of a cargo.
|
||||
ChangeDestination(id cargo.TrackingID, destination location.UNLocode) error
|
||||
|
||||
// Cargos returns a list of all cargos that have been booked.
|
||||
Cargos() []Cargo
|
||||
|
||||
// Locations returns a list of registered locations.
|
||||
Locations() []Location
|
||||
}
|
||||
|
||||
type service struct {
|
||||
cargos cargo.Repository
|
||||
locations location.Repository
|
||||
handlingEvents cargo.HandlingEventRepository
|
||||
routingService routing.Service
|
||||
}
|
||||
|
||||
func (s *service) AssignCargoToRoute(id cargo.TrackingID, itinerary cargo.Itinerary) error {
|
||||
if id == "" || len(itinerary.Legs) == 0 {
|
||||
return ErrInvalidArgument
|
||||
}
|
||||
|
||||
c, err := s.cargos.Find(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.AssignToRoute(itinerary)
|
||||
|
||||
return s.cargos.Store(c)
|
||||
}
|
||||
|
||||
func (s *service) BookNewCargo(origin, destination location.UNLocode, deadline time.Time) (cargo.TrackingID, error) {
|
||||
if origin == "" || destination == "" || deadline.IsZero() {
|
||||
return "", ErrInvalidArgument
|
||||
}
|
||||
|
||||
id := cargo.NextTrackingID()
|
||||
rs := cargo.RouteSpecification{
|
||||
Origin: origin,
|
||||
Destination: destination,
|
||||
ArrivalDeadline: deadline,
|
||||
}
|
||||
|
||||
c := cargo.New(id, rs)
|
||||
|
||||
if err := s.cargos.Store(c); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return c.TrackingID, nil
|
||||
}
|
||||
|
||||
func (s *service) LoadCargo(id cargo.TrackingID) (Cargo, error) {
|
||||
if id == "" {
|
||||
return Cargo{}, ErrInvalidArgument
|
||||
}
|
||||
|
||||
c, err := s.cargos.Find(id)
|
||||
if err != nil {
|
||||
return Cargo{}, err
|
||||
}
|
||||
|
||||
return assemble(c, s.handlingEvents), nil
|
||||
}
|
||||
|
||||
func (s *service) ChangeDestination(id cargo.TrackingID, destination location.UNLocode) error {
|
||||
if id == "" || destination == "" {
|
||||
return ErrInvalidArgument
|
||||
}
|
||||
|
||||
c, err := s.cargos.Find(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l, err := s.locations.Find(destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.SpecifyNewRoute(cargo.RouteSpecification{
|
||||
Origin: c.Origin,
|
||||
Destination: l.UNLocode,
|
||||
ArrivalDeadline: c.RouteSpecification.ArrivalDeadline,
|
||||
})
|
||||
|
||||
if err := s.cargos.Store(c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *service) RequestPossibleRoutesForCargo(id cargo.TrackingID) []cargo.Itinerary {
|
||||
if id == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
c, err := s.cargos.Find(id)
|
||||
if err != nil {
|
||||
return []cargo.Itinerary{}
|
||||
}
|
||||
|
||||
return s.routingService.FetchRoutesForSpecification(c.RouteSpecification)
|
||||
}
|
||||
|
||||
func (s *service) Cargos() []Cargo {
|
||||
var result []Cargo
|
||||
for _, c := range s.cargos.FindAll() {
|
||||
result = append(result, assemble(c, s.handlingEvents))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *service) Locations() []Location {
|
||||
var result []Location
|
||||
for _, v := range s.locations.FindAll() {
|
||||
result = append(result, Location{
|
||||
UNLocode: string(v.UNLocode),
|
||||
Name: v.Name,
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// NewService creates a booking service with necessary dependencies.
|
||||
func NewService(cargos cargo.Repository, locations location.Repository, events cargo.HandlingEventRepository, rs routing.Service) Service {
|
||||
return &service{
|
||||
cargos: cargos,
|
||||
locations: locations,
|
||||
handlingEvents: events,
|
||||
routingService: rs,
|
||||
}
|
||||
}
|
||||
|
||||
// Location is a read model for booking views.
|
||||
type Location struct {
|
||||
UNLocode string `json:"locode"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// Cargo is a read model for booking views.
|
||||
type Cargo struct {
|
||||
ArrivalDeadline time.Time `json:"arrival_deadline"`
|
||||
Destination string `json:"destination"`
|
||||
Legs []cargo.Leg `json:"legs,omitempty"`
|
||||
Misrouted bool `json:"misrouted"`
|
||||
Origin string `json:"origin"`
|
||||
Routed bool `json:"routed"`
|
||||
TrackingID string `json:"tracking_id"`
|
||||
}
|
||||
|
||||
func assemble(c *cargo.Cargo, events cargo.HandlingEventRepository) Cargo {
|
||||
return Cargo{
|
||||
TrackingID: string(c.TrackingID),
|
||||
Origin: string(c.Origin),
|
||||
Destination: string(c.RouteSpecification.Destination),
|
||||
Misrouted: c.Delivery.RoutingStatus == cargo.Misrouted,
|
||||
Routed: !c.Itinerary.IsEmpty(),
|
||||
ArrivalDeadline: c.RouteSpecification.ArrivalDeadline,
|
||||
Legs: c.Itinerary.Legs,
|
||||
}
|
||||
}
|
194
vendor/github.com/go-kit/kit/examples/shipping/booking/transport.go
generated
vendored
Normal file
194
vendor/github.com/go-kit/kit/examples/shipping/booking/transport.go
generated
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
package booking
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
kitlog "github.com/go-kit/kit/log"
|
||||
kithttp "github.com/go-kit/kit/transport/http"
|
||||
|
||||
"github.com/go-kit/kit/examples/shipping/cargo"
|
||||
"github.com/go-kit/kit/examples/shipping/location"
|
||||
)
|
||||
|
||||
// MakeHandler returns a handler for the booking service.
|
||||
func MakeHandler(bs Service, logger kitlog.Logger) http.Handler {
|
||||
opts := []kithttp.ServerOption{
|
||||
kithttp.ServerErrorLogger(logger),
|
||||
kithttp.ServerErrorEncoder(encodeError),
|
||||
}
|
||||
|
||||
bookCargoHandler := kithttp.NewServer(
|
||||
makeBookCargoEndpoint(bs),
|
||||
decodeBookCargoRequest,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
)
|
||||
loadCargoHandler := kithttp.NewServer(
|
||||
makeLoadCargoEndpoint(bs),
|
||||
decodeLoadCargoRequest,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
)
|
||||
requestRoutesHandler := kithttp.NewServer(
|
||||
makeRequestRoutesEndpoint(bs),
|
||||
decodeRequestRoutesRequest,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
)
|
||||
assignToRouteHandler := kithttp.NewServer(
|
||||
makeAssignToRouteEndpoint(bs),
|
||||
decodeAssignToRouteRequest,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
)
|
||||
changeDestinationHandler := kithttp.NewServer(
|
||||
makeChangeDestinationEndpoint(bs),
|
||||
decodeChangeDestinationRequest,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
)
|
||||
listCargosHandler := kithttp.NewServer(
|
||||
makeListCargosEndpoint(bs),
|
||||
decodeListCargosRequest,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
)
|
||||
listLocationsHandler := kithttp.NewServer(
|
||||
makeListLocationsEndpoint(bs),
|
||||
decodeListLocationsRequest,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
)
|
||||
|
||||
r := mux.NewRouter()
|
||||
|
||||
r.Handle("/booking/v1/cargos", bookCargoHandler).Methods("POST")
|
||||
r.Handle("/booking/v1/cargos", listCargosHandler).Methods("GET")
|
||||
r.Handle("/booking/v1/cargos/{id}", loadCargoHandler).Methods("GET")
|
||||
r.Handle("/booking/v1/cargos/{id}/request_routes", requestRoutesHandler).Methods("GET")
|
||||
r.Handle("/booking/v1/cargos/{id}/assign_to_route", assignToRouteHandler).Methods("POST")
|
||||
r.Handle("/booking/v1/cargos/{id}/change_destination", changeDestinationHandler).Methods("POST")
|
||||
r.Handle("/booking/v1/locations", listLocationsHandler).Methods("GET")
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
var errBadRoute = errors.New("bad route")
|
||||
|
||||
func decodeBookCargoRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
var body struct {
|
||||
Origin string `json:"origin"`
|
||||
Destination string `json:"destination"`
|
||||
ArrivalDeadline time.Time `json:"arrival_deadline"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bookCargoRequest{
|
||||
Origin: location.UNLocode(body.Origin),
|
||||
Destination: location.UNLocode(body.Destination),
|
||||
ArrivalDeadline: body.ArrivalDeadline,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func decodeLoadCargoRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
vars := mux.Vars(r)
|
||||
id, ok := vars["id"]
|
||||
if !ok {
|
||||
return nil, errBadRoute
|
||||
}
|
||||
return loadCargoRequest{ID: cargo.TrackingID(id)}, nil
|
||||
}
|
||||
|
||||
func decodeRequestRoutesRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
vars := mux.Vars(r)
|
||||
id, ok := vars["id"]
|
||||
if !ok {
|
||||
return nil, errBadRoute
|
||||
}
|
||||
return requestRoutesRequest{ID: cargo.TrackingID(id)}, nil
|
||||
}
|
||||
|
||||
func decodeAssignToRouteRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
vars := mux.Vars(r)
|
||||
id, ok := vars["id"]
|
||||
if !ok {
|
||||
return nil, errBadRoute
|
||||
}
|
||||
|
||||
var itinerary cargo.Itinerary
|
||||
if err := json.NewDecoder(r.Body).Decode(&itinerary); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return assignToRouteRequest{
|
||||
ID: cargo.TrackingID(id),
|
||||
Itinerary: itinerary,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func decodeChangeDestinationRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
vars := mux.Vars(r)
|
||||
id, ok := vars["id"]
|
||||
if !ok {
|
||||
return nil, errBadRoute
|
||||
}
|
||||
|
||||
var body struct {
|
||||
Destination string `json:"destination"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return changeDestinationRequest{
|
||||
ID: cargo.TrackingID(id),
|
||||
Destination: location.UNLocode(body.Destination),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func decodeListCargosRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
return listCargosRequest{}, nil
|
||||
}
|
||||
|
||||
func decodeListLocationsRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
return listLocationsRequest{}, nil
|
||||
}
|
||||
|
||||
func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error {
|
||||
if e, ok := response.(errorer); ok && e.error() != nil {
|
||||
encodeError(ctx, e.error(), w)
|
||||
return nil
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type errorer interface {
|
||||
error() error
|
||||
}
|
||||
|
||||
// encode errors from business-logic
|
||||
func encodeError(_ context.Context, err error, w http.ResponseWriter) {
|
||||
switch err {
|
||||
case cargo.ErrUnknown:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
case ErrInvalidArgument:
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
default:
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
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
|
||||
}
|
34
vendor/github.com/go-kit/kit/examples/shipping/handling/endpoint.go
generated
vendored
Normal file
34
vendor/github.com/go-kit/kit/examples/shipping/handling/endpoint.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
package handling
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
|
||||
"github.com/go-kit/kit/examples/shipping/cargo"
|
||||
"github.com/go-kit/kit/examples/shipping/location"
|
||||
"github.com/go-kit/kit/examples/shipping/voyage"
|
||||
)
|
||||
|
||||
type registerIncidentRequest struct {
|
||||
ID cargo.TrackingID
|
||||
Location location.UNLocode
|
||||
Voyage voyage.Number
|
||||
EventType cargo.HandlingEventType
|
||||
CompletionTime time.Time
|
||||
}
|
||||
|
||||
type registerIncidentResponse struct {
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r registerIncidentResponse) error() error { return r.Err }
|
||||
|
||||
func makeRegisterIncidentEndpoint(hs Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(registerIncidentRequest)
|
||||
err := hs.RegisterHandlingEvent(req.CompletionTime, req.ID, req.Voyage, req.Location, req.EventType)
|
||||
return registerIncidentResponse{Err: err}, nil
|
||||
}
|
||||
}
|
37
vendor/github.com/go-kit/kit/examples/shipping/handling/instrumenting.go
generated
vendored
Normal file
37
vendor/github.com/go-kit/kit/examples/shipping/handling/instrumenting.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
package handling
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/metrics"
|
||||
|
||||
"github.com/go-kit/kit/examples/shipping/cargo"
|
||||
"github.com/go-kit/kit/examples/shipping/location"
|
||||
"github.com/go-kit/kit/examples/shipping/voyage"
|
||||
)
|
||||
|
||||
type instrumentingService struct {
|
||||
requestCount metrics.Counter
|
||||
requestLatency metrics.Histogram
|
||||
Service
|
||||
}
|
||||
|
||||
// NewInstrumentingService returns an instance of an instrumenting Service.
|
||||
func NewInstrumentingService(counter metrics.Counter, latency metrics.Histogram, s Service) Service {
|
||||
return &instrumentingService{
|
||||
requestCount: counter,
|
||||
requestLatency: latency,
|
||||
Service: s,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *instrumentingService) RegisterHandlingEvent(completed time.Time, id cargo.TrackingID, voyageNumber voyage.Number,
|
||||
loc location.UNLocode, eventType cargo.HandlingEventType) error {
|
||||
|
||||
defer func(begin time.Time) {
|
||||
s.requestCount.With("method", "register_incident").Add(1)
|
||||
s.requestLatency.With("method", "register_incident").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return s.Service.RegisterHandlingEvent(completed, id, voyageNumber, loc, eventType)
|
||||
}
|
38
vendor/github.com/go-kit/kit/examples/shipping/handling/logging.go
generated
vendored
Normal file
38
vendor/github.com/go-kit/kit/examples/shipping/handling/logging.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package handling
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
|
||||
"github.com/go-kit/kit/examples/shipping/cargo"
|
||||
"github.com/go-kit/kit/examples/shipping/location"
|
||||
"github.com/go-kit/kit/examples/shipping/voyage"
|
||||
)
|
||||
|
||||
type loggingService struct {
|
||||
logger log.Logger
|
||||
Service
|
||||
}
|
||||
|
||||
// NewLoggingService returns a new instance of a logging Service.
|
||||
func NewLoggingService(logger log.Logger, s Service) Service {
|
||||
return &loggingService{logger, s}
|
||||
}
|
||||
|
||||
func (s *loggingService) RegisterHandlingEvent(completed time.Time, id cargo.TrackingID, voyageNumber voyage.Number,
|
||||
unLocode location.UNLocode, eventType cargo.HandlingEventType) (err error) {
|
||||
defer func(begin time.Time) {
|
||||
s.logger.Log(
|
||||
"method", "register_incident",
|
||||
"tracking_id", id,
|
||||
"location", unLocode,
|
||||
"voyage", voyageNumber,
|
||||
"event_type", eventType,
|
||||
"completion_time", completed,
|
||||
"took", time.Since(begin),
|
||||
"err", err,
|
||||
)
|
||||
}(time.Now())
|
||||
return s.Service.RegisterHandlingEvent(completed, id, voyageNumber, unLocode, eventType)
|
||||
}
|
76
vendor/github.com/go-kit/kit/examples/shipping/handling/service.go
generated
vendored
Normal file
76
vendor/github.com/go-kit/kit/examples/shipping/handling/service.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
// Package handling provides the use-case for registering incidents. Used by
|
||||
// views facing the people handling the cargo along its route.
|
||||
package handling
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/examples/shipping/cargo"
|
||||
"github.com/go-kit/kit/examples/shipping/inspection"
|
||||
"github.com/go-kit/kit/examples/shipping/location"
|
||||
"github.com/go-kit/kit/examples/shipping/voyage"
|
||||
)
|
||||
|
||||
// ErrInvalidArgument is returned when one or more arguments are invalid.
|
||||
var ErrInvalidArgument = errors.New("invalid argument")
|
||||
|
||||
// EventHandler provides a means of subscribing to registered handling events.
|
||||
type EventHandler interface {
|
||||
CargoWasHandled(cargo.HandlingEvent)
|
||||
}
|
||||
|
||||
// Service provides handling operations.
|
||||
type Service interface {
|
||||
// RegisterHandlingEvent registers a handling event in the system, and
|
||||
// notifies interested parties that a cargo has been handled.
|
||||
RegisterHandlingEvent(completed time.Time, id cargo.TrackingID, voyageNumber voyage.Number,
|
||||
unLocode location.UNLocode, eventType cargo.HandlingEventType) error
|
||||
}
|
||||
|
||||
type service struct {
|
||||
handlingEventRepository cargo.HandlingEventRepository
|
||||
handlingEventFactory cargo.HandlingEventFactory
|
||||
handlingEventHandler EventHandler
|
||||
}
|
||||
|
||||
func (s *service) RegisterHandlingEvent(completed time.Time, id cargo.TrackingID, voyageNumber voyage.Number,
|
||||
loc location.UNLocode, eventType cargo.HandlingEventType) error {
|
||||
if completed.IsZero() || id == "" || loc == "" || eventType == cargo.NotHandled {
|
||||
return ErrInvalidArgument
|
||||
}
|
||||
|
||||
e, err := s.handlingEventFactory.CreateHandlingEvent(time.Now(), completed, id, voyageNumber, loc, eventType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.handlingEventRepository.Store(e)
|
||||
s.handlingEventHandler.CargoWasHandled(e)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewService creates a handling event service with necessary dependencies.
|
||||
func NewService(r cargo.HandlingEventRepository, f cargo.HandlingEventFactory, h EventHandler) Service {
|
||||
return &service{
|
||||
handlingEventRepository: r,
|
||||
handlingEventFactory: f,
|
||||
handlingEventHandler: h,
|
||||
}
|
||||
}
|
||||
|
||||
type handlingEventHandler struct {
|
||||
InspectionService inspection.Service
|
||||
}
|
||||
|
||||
func (h *handlingEventHandler) CargoWasHandled(event cargo.HandlingEvent) {
|
||||
h.InspectionService.InspectCargo(event.TrackingID)
|
||||
}
|
||||
|
||||
// NewEventHandler returns a new instance of a EventHandler.
|
||||
func NewEventHandler(s inspection.Service) EventHandler {
|
||||
return &handlingEventHandler{
|
||||
InspectionService: s,
|
||||
}
|
||||
}
|
100
vendor/github.com/go-kit/kit/examples/shipping/handling/transport.go
generated
vendored
Normal file
100
vendor/github.com/go-kit/kit/examples/shipping/handling/transport.go
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
package handling
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
kitlog "github.com/go-kit/kit/log"
|
||||
kithttp "github.com/go-kit/kit/transport/http"
|
||||
|
||||
"github.com/go-kit/kit/examples/shipping/cargo"
|
||||
"github.com/go-kit/kit/examples/shipping/location"
|
||||
"github.com/go-kit/kit/examples/shipping/voyage"
|
||||
)
|
||||
|
||||
// MakeHandler returns a handler for the handling service.
|
||||
func MakeHandler(hs Service, logger kitlog.Logger) http.Handler {
|
||||
r := mux.NewRouter()
|
||||
|
||||
opts := []kithttp.ServerOption{
|
||||
kithttp.ServerErrorLogger(logger),
|
||||
kithttp.ServerErrorEncoder(encodeError),
|
||||
}
|
||||
|
||||
registerIncidentHandler := kithttp.NewServer(
|
||||
makeRegisterIncidentEndpoint(hs),
|
||||
decodeRegisterIncidentRequest,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
)
|
||||
|
||||
r.Handle("/handling/v1/incidents", registerIncidentHandler).Methods("POST")
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func decodeRegisterIncidentRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
var body struct {
|
||||
CompletionTime time.Time `json:"completion_time"`
|
||||
TrackingID string `json:"tracking_id"`
|
||||
VoyageNumber string `json:"voyage"`
|
||||
Location string `json:"location"`
|
||||
EventType string `json:"event_type"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return registerIncidentRequest{
|
||||
CompletionTime: body.CompletionTime,
|
||||
ID: cargo.TrackingID(body.TrackingID),
|
||||
Voyage: voyage.Number(body.VoyageNumber),
|
||||
Location: location.UNLocode(body.Location),
|
||||
EventType: stringToEventType(body.EventType),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func stringToEventType(s string) cargo.HandlingEventType {
|
||||
types := map[string]cargo.HandlingEventType{
|
||||
cargo.Receive.String(): cargo.Receive,
|
||||
cargo.Load.String(): cargo.Load,
|
||||
cargo.Unload.String(): cargo.Unload,
|
||||
cargo.Customs.String(): cargo.Customs,
|
||||
cargo.Claim.String(): cargo.Claim,
|
||||
}
|
||||
return types[s]
|
||||
}
|
||||
|
||||
func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error {
|
||||
if e, ok := response.(errorer); ok && e.error() != nil {
|
||||
encodeError(ctx, e.error(), w)
|
||||
return nil
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type errorer interface {
|
||||
error() error
|
||||
}
|
||||
|
||||
// encode errors from business-logic
|
||||
func encodeError(_ context.Context, err error, w http.ResponseWriter) {
|
||||
switch err {
|
||||
case cargo.ErrUnknown:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
case ErrInvalidArgument:
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
default:
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
142
vendor/github.com/go-kit/kit/examples/shipping/inmem/inmem.go
generated
vendored
Normal file
142
vendor/github.com/go-kit/kit/examples/shipping/inmem/inmem.go
generated
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
// Package inmem provides in-memory implementations of all the domain repositories.
|
||||
package inmem
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/go-kit/kit/examples/shipping/cargo"
|
||||
"github.com/go-kit/kit/examples/shipping/location"
|
||||
"github.com/go-kit/kit/examples/shipping/voyage"
|
||||
)
|
||||
|
||||
type cargoRepository struct {
|
||||
mtx sync.RWMutex
|
||||
cargos map[cargo.TrackingID]*cargo.Cargo
|
||||
}
|
||||
|
||||
func (r *cargoRepository) Store(c *cargo.Cargo) error {
|
||||
r.mtx.Lock()
|
||||
defer r.mtx.Unlock()
|
||||
r.cargos[c.TrackingID] = c
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *cargoRepository) Find(id cargo.TrackingID) (*cargo.Cargo, error) {
|
||||
r.mtx.RLock()
|
||||
defer r.mtx.RUnlock()
|
||||
if val, ok := r.cargos[id]; ok {
|
||||
return val, nil
|
||||
}
|
||||
return nil, cargo.ErrUnknown
|
||||
}
|
||||
|
||||
func (r *cargoRepository) FindAll() []*cargo.Cargo {
|
||||
r.mtx.RLock()
|
||||
defer r.mtx.RUnlock()
|
||||
c := make([]*cargo.Cargo, 0, len(r.cargos))
|
||||
for _, val := range r.cargos {
|
||||
c = append(c, val)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// NewCargoRepository returns a new instance of a in-memory cargo repository.
|
||||
func NewCargoRepository() cargo.Repository {
|
||||
return &cargoRepository{
|
||||
cargos: make(map[cargo.TrackingID]*cargo.Cargo),
|
||||
}
|
||||
}
|
||||
|
||||
type locationRepository struct {
|
||||
locations map[location.UNLocode]*location.Location
|
||||
}
|
||||
|
||||
func (r *locationRepository) Find(locode location.UNLocode) (*location.Location, error) {
|
||||
if l, ok := r.locations[locode]; ok {
|
||||
return l, nil
|
||||
}
|
||||
return nil, location.ErrUnknown
|
||||
}
|
||||
|
||||
func (r *locationRepository) FindAll() []*location.Location {
|
||||
l := make([]*location.Location, 0, len(r.locations))
|
||||
for _, val := range r.locations {
|
||||
l = append(l, val)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// NewLocationRepository returns a new instance of a in-memory location repository.
|
||||
func NewLocationRepository() location.Repository {
|
||||
r := &locationRepository{
|
||||
locations: make(map[location.UNLocode]*location.Location),
|
||||
}
|
||||
|
||||
r.locations[location.SESTO] = location.Stockholm
|
||||
r.locations[location.AUMEL] = location.Melbourne
|
||||
r.locations[location.CNHKG] = location.Hongkong
|
||||
r.locations[location.JNTKO] = location.Tokyo
|
||||
r.locations[location.NLRTM] = location.Rotterdam
|
||||
r.locations[location.DEHAM] = location.Hamburg
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
type voyageRepository struct {
|
||||
voyages map[voyage.Number]*voyage.Voyage
|
||||
}
|
||||
|
||||
func (r *voyageRepository) Find(voyageNumber voyage.Number) (*voyage.Voyage, error) {
|
||||
if v, ok := r.voyages[voyageNumber]; ok {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
return nil, voyage.ErrUnknown
|
||||
}
|
||||
|
||||
// NewVoyageRepository returns a new instance of a in-memory voyage repository.
|
||||
func NewVoyageRepository() voyage.Repository {
|
||||
r := &voyageRepository{
|
||||
voyages: make(map[voyage.Number]*voyage.Voyage),
|
||||
}
|
||||
|
||||
r.voyages[voyage.V100.Number] = voyage.V100
|
||||
r.voyages[voyage.V300.Number] = voyage.V300
|
||||
r.voyages[voyage.V400.Number] = voyage.V400
|
||||
|
||||
r.voyages[voyage.V0100S.Number] = voyage.V0100S
|
||||
r.voyages[voyage.V0200T.Number] = voyage.V0200T
|
||||
r.voyages[voyage.V0300A.Number] = voyage.V0300A
|
||||
r.voyages[voyage.V0301S.Number] = voyage.V0301S
|
||||
r.voyages[voyage.V0400S.Number] = voyage.V0400S
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
type handlingEventRepository struct {
|
||||
mtx sync.RWMutex
|
||||
events map[cargo.TrackingID][]cargo.HandlingEvent
|
||||
}
|
||||
|
||||
func (r *handlingEventRepository) Store(e cargo.HandlingEvent) {
|
||||
r.mtx.Lock()
|
||||
defer r.mtx.Unlock()
|
||||
// Make array if it's the first event with this tracking ID.
|
||||
if _, ok := r.events[e.TrackingID]; !ok {
|
||||
r.events[e.TrackingID] = make([]cargo.HandlingEvent, 0)
|
||||
}
|
||||
r.events[e.TrackingID] = append(r.events[e.TrackingID], e)
|
||||
}
|
||||
|
||||
func (r *handlingEventRepository) QueryHandlingHistory(id cargo.TrackingID) cargo.HandlingHistory {
|
||||
r.mtx.RLock()
|
||||
defer r.mtx.RUnlock()
|
||||
return cargo.HandlingHistory{HandlingEvents: r.events[id]}
|
||||
}
|
||||
|
||||
// NewHandlingEventRepository returns a new instance of a in-memory handling event repository.
|
||||
func NewHandlingEventRepository() cargo.HandlingEventRepository {
|
||||
return &handlingEventRepository{
|
||||
events: make(map[cargo.TrackingID][]cargo.HandlingEvent),
|
||||
}
|
||||
}
|
53
vendor/github.com/go-kit/kit/examples/shipping/inspection/inspection.go
generated
vendored
Normal file
53
vendor/github.com/go-kit/kit/examples/shipping/inspection/inspection.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
// Package inspection provides means to inspect cargos.
|
||||
package inspection
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/examples/shipping/cargo"
|
||||
)
|
||||
|
||||
// EventHandler provides means of subscribing to inspection events.
|
||||
type EventHandler interface {
|
||||
CargoWasMisdirected(*cargo.Cargo)
|
||||
CargoHasArrived(*cargo.Cargo)
|
||||
}
|
||||
|
||||
// Service provides cargo inspection operations.
|
||||
type Service interface {
|
||||
// InspectCargo inspects cargo and send relevant notifications to
|
||||
// interested parties, for example if a cargo has been misdirected, or
|
||||
// unloaded at the final destination.
|
||||
InspectCargo(id cargo.TrackingID)
|
||||
}
|
||||
|
||||
type service struct {
|
||||
cargos cargo.Repository
|
||||
events cargo.HandlingEventRepository
|
||||
handler EventHandler
|
||||
}
|
||||
|
||||
// TODO: Should be transactional
|
||||
func (s *service) InspectCargo(id cargo.TrackingID) {
|
||||
c, err := s.cargos.Find(id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
h := s.events.QueryHandlingHistory(id)
|
||||
|
||||
c.DeriveDeliveryProgress(h)
|
||||
|
||||
if c.Delivery.IsMisdirected {
|
||||
s.handler.CargoWasMisdirected(c)
|
||||
}
|
||||
|
||||
if c.Delivery.IsUnloadedAtDestination {
|
||||
s.handler.CargoHasArrived(c)
|
||||
}
|
||||
|
||||
s.cargos.Store(c)
|
||||
}
|
||||
|
||||
// NewService creates a inspection service with necessary dependencies.
|
||||
func NewService(cargos cargo.Repository, events cargo.HandlingEventRepository, handler EventHandler) Service {
|
||||
return &service{cargos, events, handler}
|
||||
}
|
29
vendor/github.com/go-kit/kit/examples/shipping/location/location.go
generated
vendored
Normal file
29
vendor/github.com/go-kit/kit/examples/shipping/location/location.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// Package location provides the Location aggregate.
|
||||
package location
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// UNLocode is the United Nations location code that uniquely identifies a
|
||||
// particular location.
|
||||
//
|
||||
// http://www.unece.org/cefact/locode/
|
||||
// http://www.unece.org/cefact/locode/DocColumnDescription.htm#LOCODE
|
||||
type UNLocode string
|
||||
|
||||
// Location is a location is our model is stops on a journey, such as cargo
|
||||
// origin or destination, or carrier movement endpoints.
|
||||
type Location struct {
|
||||
UNLocode UNLocode
|
||||
Name string
|
||||
}
|
||||
|
||||
// ErrUnknown is used when a location could not be found.
|
||||
var ErrUnknown = errors.New("unknown location")
|
||||
|
||||
// Repository provides access a location store.
|
||||
type Repository interface {
|
||||
Find(locode UNLocode) (*Location, error)
|
||||
FindAll() []*Location
|
||||
}
|
27
vendor/github.com/go-kit/kit/examples/shipping/location/sample_locations.go
generated
vendored
Normal file
27
vendor/github.com/go-kit/kit/examples/shipping/location/sample_locations.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package location
|
||||
|
||||
// Sample UN locodes.
|
||||
var (
|
||||
SESTO UNLocode = "SESTO"
|
||||
AUMEL UNLocode = "AUMEL"
|
||||
CNHKG UNLocode = "CNHKG"
|
||||
USNYC UNLocode = "USNYC"
|
||||
USCHI UNLocode = "USCHI"
|
||||
JNTKO UNLocode = "JNTKO"
|
||||
DEHAM UNLocode = "DEHAM"
|
||||
NLRTM UNLocode = "NLRTM"
|
||||
FIHEL UNLocode = "FIHEL"
|
||||
)
|
||||
|
||||
// Sample locations.
|
||||
var (
|
||||
Stockholm = &Location{SESTO, "Stockholm"}
|
||||
Melbourne = &Location{AUMEL, "Melbourne"}
|
||||
Hongkong = &Location{CNHKG, "Hongkong"}
|
||||
NewYork = &Location{USNYC, "New York"}
|
||||
Chicago = &Location{USCHI, "Chicago"}
|
||||
Tokyo = &Location{JNTKO, "Tokyo"}
|
||||
Hamburg = &Location{DEHAM, "Hamburg"}
|
||||
Rotterdam = &Location{NLRTM, "Rotterdam"}
|
||||
Helsinki = &Location{FIHEL, "Helsinki"}
|
||||
)
|
212
vendor/github.com/go-kit/kit/examples/shipping/main.go
generated
vendored
Normal file
212
vendor/github.com/go-kit/kit/examples/shipping/main.go
generated
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
|
||||
|
||||
"github.com/go-kit/kit/examples/shipping/booking"
|
||||
"github.com/go-kit/kit/examples/shipping/cargo"
|
||||
"github.com/go-kit/kit/examples/shipping/handling"
|
||||
"github.com/go-kit/kit/examples/shipping/inmem"
|
||||
"github.com/go-kit/kit/examples/shipping/inspection"
|
||||
"github.com/go-kit/kit/examples/shipping/location"
|
||||
"github.com/go-kit/kit/examples/shipping/routing"
|
||||
"github.com/go-kit/kit/examples/shipping/tracking"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultPort = "8080"
|
||||
defaultRoutingServiceURL = "http://localhost:7878"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
addr = envString("PORT", defaultPort)
|
||||
rsurl = envString("ROUTINGSERVICE_URL", defaultRoutingServiceURL)
|
||||
|
||||
httpAddr = flag.String("http.addr", ":"+addr, "HTTP listen address")
|
||||
routingServiceURL = flag.String("service.routing", rsurl, "routing service URL")
|
||||
|
||||
ctx = context.Background()
|
||||
)
|
||||
|
||||
flag.Parse()
|
||||
|
||||
var logger log.Logger
|
||||
logger = log.NewLogfmtLogger(os.Stderr)
|
||||
logger = &serializedLogger{Logger: logger}
|
||||
logger = log.With(logger, "ts", log.DefaultTimestampUTC)
|
||||
|
||||
var (
|
||||
cargos = inmem.NewCargoRepository()
|
||||
locations = inmem.NewLocationRepository()
|
||||
voyages = inmem.NewVoyageRepository()
|
||||
handlingEvents = inmem.NewHandlingEventRepository()
|
||||
)
|
||||
|
||||
// Configure some questionable dependencies.
|
||||
var (
|
||||
handlingEventFactory = cargo.HandlingEventFactory{
|
||||
CargoRepository: cargos,
|
||||
VoyageRepository: voyages,
|
||||
LocationRepository: locations,
|
||||
}
|
||||
handlingEventHandler = handling.NewEventHandler(
|
||||
inspection.NewService(cargos, handlingEvents, nil),
|
||||
)
|
||||
)
|
||||
|
||||
// Facilitate testing by adding some cargos.
|
||||
storeTestData(cargos)
|
||||
|
||||
fieldKeys := []string{"method"}
|
||||
|
||||
var rs routing.Service
|
||||
rs = routing.NewProxyingMiddleware(ctx, *routingServiceURL)(rs)
|
||||
|
||||
var bs booking.Service
|
||||
bs = booking.NewService(cargos, locations, handlingEvents, rs)
|
||||
bs = booking.NewLoggingService(log.With(logger, "component", "booking"), bs)
|
||||
bs = booking.NewInstrumentingService(
|
||||
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: "api",
|
||||
Subsystem: "booking_service",
|
||||
Name: "request_count",
|
||||
Help: "Number of requests received.",
|
||||
}, fieldKeys),
|
||||
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
|
||||
Namespace: "api",
|
||||
Subsystem: "booking_service",
|
||||
Name: "request_latency_microseconds",
|
||||
Help: "Total duration of requests in microseconds.",
|
||||
}, fieldKeys),
|
||||
bs,
|
||||
)
|
||||
|
||||
var ts tracking.Service
|
||||
ts = tracking.NewService(cargos, handlingEvents)
|
||||
ts = tracking.NewLoggingService(log.With(logger, "component", "tracking"), ts)
|
||||
ts = tracking.NewInstrumentingService(
|
||||
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: "api",
|
||||
Subsystem: "tracking_service",
|
||||
Name: "request_count",
|
||||
Help: "Number of requests received.",
|
||||
}, fieldKeys),
|
||||
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
|
||||
Namespace: "api",
|
||||
Subsystem: "tracking_service",
|
||||
Name: "request_latency_microseconds",
|
||||
Help: "Total duration of requests in microseconds.",
|
||||
}, fieldKeys),
|
||||
ts,
|
||||
)
|
||||
|
||||
var hs handling.Service
|
||||
hs = handling.NewService(handlingEvents, handlingEventFactory, handlingEventHandler)
|
||||
hs = handling.NewLoggingService(log.With(logger, "component", "handling"), hs)
|
||||
hs = handling.NewInstrumentingService(
|
||||
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: "api",
|
||||
Subsystem: "handling_service",
|
||||
Name: "request_count",
|
||||
Help: "Number of requests received.",
|
||||
}, fieldKeys),
|
||||
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
|
||||
Namespace: "api",
|
||||
Subsystem: "handling_service",
|
||||
Name: "request_latency_microseconds",
|
||||
Help: "Total duration of requests in microseconds.",
|
||||
}, fieldKeys),
|
||||
hs,
|
||||
)
|
||||
|
||||
httpLogger := log.With(logger, "component", "http")
|
||||
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.Handle("/booking/v1/", booking.MakeHandler(bs, httpLogger))
|
||||
mux.Handle("/tracking/v1/", tracking.MakeHandler(ts, httpLogger))
|
||||
mux.Handle("/handling/v1/", handling.MakeHandler(hs, httpLogger))
|
||||
|
||||
http.Handle("/", accessControl(mux))
|
||||
http.Handle("/metrics", stdprometheus.Handler())
|
||||
|
||||
errs := make(chan error, 2)
|
||||
go func() {
|
||||
logger.Log("transport", "http", "address", *httpAddr, "msg", "listening")
|
||||
errs <- http.ListenAndServe(*httpAddr, nil)
|
||||
}()
|
||||
go func() {
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, syscall.SIGINT)
|
||||
errs <- fmt.Errorf("%s", <-c)
|
||||
}()
|
||||
|
||||
logger.Log("terminated", <-errs)
|
||||
}
|
||||
|
||||
func accessControl(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type")
|
||||
|
||||
if r.Method == "OPTIONS" {
|
||||
return
|
||||
}
|
||||
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func envString(env, fallback string) string {
|
||||
e := os.Getenv(env)
|
||||
if e == "" {
|
||||
return fallback
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func storeTestData(r cargo.Repository) {
|
||||
test1 := cargo.New("FTL456", cargo.RouteSpecification{
|
||||
Origin: location.AUMEL,
|
||||
Destination: location.SESTO,
|
||||
ArrivalDeadline: time.Now().AddDate(0, 0, 7),
|
||||
})
|
||||
if err := r.Store(test1); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
test2 := cargo.New("ABC123", cargo.RouteSpecification{
|
||||
Origin: location.SESTO,
|
||||
Destination: location.CNHKG,
|
||||
ArrivalDeadline: time.Now().AddDate(0, 0, 14),
|
||||
})
|
||||
if err := r.Store(test2); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
type serializedLogger struct {
|
||||
mtx sync.Mutex
|
||||
log.Logger
|
||||
}
|
||||
|
||||
func (l *serializedLogger) Log(keyvals ...interface{}) error {
|
||||
l.mtx.Lock()
|
||||
defer l.mtx.Unlock()
|
||||
return l.Logger.Log(keyvals...)
|
||||
}
|
117
vendor/github.com/go-kit/kit/examples/shipping/routing/proxying.go
generated
vendored
Normal file
117
vendor/github.com/go-kit/kit/examples/shipping/routing/proxying.go
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
package routing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/circuitbreaker"
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
kithttp "github.com/go-kit/kit/transport/http"
|
||||
|
||||
"github.com/go-kit/kit/examples/shipping/cargo"
|
||||
"github.com/go-kit/kit/examples/shipping/location"
|
||||
"github.com/go-kit/kit/examples/shipping/voyage"
|
||||
)
|
||||
|
||||
type proxyService struct {
|
||||
context.Context
|
||||
FetchRoutesEndpoint endpoint.Endpoint
|
||||
Service
|
||||
}
|
||||
|
||||
func (s proxyService) FetchRoutesForSpecification(rs cargo.RouteSpecification) []cargo.Itinerary {
|
||||
response, err := s.FetchRoutesEndpoint(s.Context, fetchRoutesRequest{
|
||||
From: string(rs.Origin),
|
||||
To: string(rs.Destination),
|
||||
})
|
||||
if err != nil {
|
||||
return []cargo.Itinerary{}
|
||||
}
|
||||
|
||||
resp := response.(fetchRoutesResponse)
|
||||
|
||||
var itineraries []cargo.Itinerary
|
||||
for _, r := range resp.Paths {
|
||||
var legs []cargo.Leg
|
||||
for _, e := range r.Edges {
|
||||
legs = append(legs, cargo.Leg{
|
||||
VoyageNumber: voyage.Number(e.Voyage),
|
||||
LoadLocation: location.UNLocode(e.Origin),
|
||||
UnloadLocation: location.UNLocode(e.Destination),
|
||||
LoadTime: e.Departure,
|
||||
UnloadTime: e.Arrival,
|
||||
})
|
||||
}
|
||||
|
||||
itineraries = append(itineraries, cargo.Itinerary{Legs: legs})
|
||||
}
|
||||
|
||||
return itineraries
|
||||
}
|
||||
|
||||
// ServiceMiddleware defines a middleware for a routing service.
|
||||
type ServiceMiddleware func(Service) Service
|
||||
|
||||
// NewProxyingMiddleware returns a new instance of a proxying middleware.
|
||||
func NewProxyingMiddleware(ctx context.Context, proxyURL string) ServiceMiddleware {
|
||||
return func(next Service) Service {
|
||||
var e endpoint.Endpoint
|
||||
e = makeFetchRoutesEndpoint(ctx, proxyURL)
|
||||
e = circuitbreaker.Hystrix("fetch-routes")(e)
|
||||
return proxyService{ctx, e, next}
|
||||
}
|
||||
}
|
||||
|
||||
type fetchRoutesRequest struct {
|
||||
From string
|
||||
To string
|
||||
}
|
||||
|
||||
type fetchRoutesResponse struct {
|
||||
Paths []struct {
|
||||
Edges []struct {
|
||||
Origin string `json:"origin"`
|
||||
Destination string `json:"destination"`
|
||||
Voyage string `json:"voyage"`
|
||||
Departure time.Time `json:"departure"`
|
||||
Arrival time.Time `json:"arrival"`
|
||||
} `json:"edges"`
|
||||
} `json:"paths"`
|
||||
}
|
||||
|
||||
func makeFetchRoutesEndpoint(ctx context.Context, instance string) endpoint.Endpoint {
|
||||
u, err := url.Parse(instance)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u.Path == "" {
|
||||
u.Path = "/paths"
|
||||
}
|
||||
return kithttp.NewClient(
|
||||
"GET", u,
|
||||
encodeFetchRoutesRequest,
|
||||
decodeFetchRoutesResponse,
|
||||
).Endpoint()
|
||||
}
|
||||
|
||||
func decodeFetchRoutesResponse(_ context.Context, resp *http.Response) (interface{}, error) {
|
||||
var response fetchRoutesResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func encodeFetchRoutesRequest(_ context.Context, r *http.Request, request interface{}) error {
|
||||
req := request.(fetchRoutesRequest)
|
||||
|
||||
vals := r.URL.Query()
|
||||
vals.Add("from", req.From)
|
||||
vals.Add("to", req.To)
|
||||
r.URL.RawQuery = vals.Encode()
|
||||
|
||||
return nil
|
||||
}
|
15
vendor/github.com/go-kit/kit/examples/shipping/routing/routing.go
generated
vendored
Normal file
15
vendor/github.com/go-kit/kit/examples/shipping/routing/routing.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
// Package routing provides the routing domain service. It does not actually
|
||||
// implement the routing service but merely acts as a proxy for a separate
|
||||
// bounded context.
|
||||
package routing
|
||||
|
||||
import (
|
||||
"github.com/go-kit/kit/examples/shipping/cargo"
|
||||
)
|
||||
|
||||
// Service provides access to an external routing service.
|
||||
type Service interface {
|
||||
// FetchRoutesForSpecification finds all possible routes that satisfy a
|
||||
// given specification.
|
||||
FetchRoutesForSpecification(rs cargo.RouteSpecification) []cargo.Itinerary
|
||||
}
|
26
vendor/github.com/go-kit/kit/examples/shipping/tracking/endpoint.go
generated
vendored
Normal file
26
vendor/github.com/go-kit/kit/examples/shipping/tracking/endpoint.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package tracking
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
)
|
||||
|
||||
type trackCargoRequest struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
type trackCargoResponse struct {
|
||||
Cargo *Cargo `json:"cargo,omitempty"`
|
||||
Err error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (r trackCargoResponse) error() error { return r.Err }
|
||||
|
||||
func makeTrackCargoEndpoint(ts Service) endpoint.Endpoint {
|
||||
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
req := request.(trackCargoRequest)
|
||||
c, err := ts.Track(req.ID)
|
||||
return trackCargoResponse{Cargo: &c, Err: err}, nil
|
||||
}
|
||||
}
|
31
vendor/github.com/go-kit/kit/examples/shipping/tracking/instrumenting.go
generated
vendored
Normal file
31
vendor/github.com/go-kit/kit/examples/shipping/tracking/instrumenting.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
package tracking
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/metrics"
|
||||
)
|
||||
|
||||
type instrumentingService struct {
|
||||
requestCount metrics.Counter
|
||||
requestLatency metrics.Histogram
|
||||
Service
|
||||
}
|
||||
|
||||
// NewInstrumentingService returns an instance of an instrumenting Service.
|
||||
func NewInstrumentingService(counter metrics.Counter, latency metrics.Histogram, s Service) Service {
|
||||
return &instrumentingService{
|
||||
requestCount: counter,
|
||||
requestLatency: latency,
|
||||
Service: s,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *instrumentingService) Track(id string) (Cargo, error) {
|
||||
defer func(begin time.Time) {
|
||||
s.requestCount.With("method", "track").Add(1)
|
||||
s.requestLatency.With("method", "track").Observe(time.Since(begin).Seconds())
|
||||
}(time.Now())
|
||||
|
||||
return s.Service.Track(id)
|
||||
}
|
24
vendor/github.com/go-kit/kit/examples/shipping/tracking/logging.go
generated
vendored
Normal file
24
vendor/github.com/go-kit/kit/examples/shipping/tracking/logging.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package tracking
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
)
|
||||
|
||||
type loggingService struct {
|
||||
logger log.Logger
|
||||
Service
|
||||
}
|
||||
|
||||
// NewLoggingService returns a new instance of a logging Service.
|
||||
func NewLoggingService(logger log.Logger, s Service) Service {
|
||||
return &loggingService{logger, s}
|
||||
}
|
||||
|
||||
func (s *loggingService) Track(id string) (c Cargo, err error) {
|
||||
defer func(begin time.Time) {
|
||||
s.logger.Log("method", "track", "tracking_id", id, "took", time.Since(begin), "err", err)
|
||||
}(time.Now())
|
||||
return s.Service.Track(id)
|
||||
}
|
163
vendor/github.com/go-kit/kit/examples/shipping/tracking/service.go
generated
vendored
Normal file
163
vendor/github.com/go-kit/kit/examples/shipping/tracking/service.go
generated
vendored
Normal file
@@ -0,0 +1,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
|
||||
}
|
74
vendor/github.com/go-kit/kit/examples/shipping/tracking/transport.go
generated
vendored
Normal file
74
vendor/github.com/go-kit/kit/examples/shipping/tracking/transport.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
package tracking
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
kitlog "github.com/go-kit/kit/log"
|
||||
kithttp "github.com/go-kit/kit/transport/http"
|
||||
|
||||
"github.com/go-kit/kit/examples/shipping/cargo"
|
||||
)
|
||||
|
||||
// MakeHandler returns a handler for the tracking service.
|
||||
func MakeHandler(ts Service, logger kitlog.Logger) http.Handler {
|
||||
r := mux.NewRouter()
|
||||
|
||||
opts := []kithttp.ServerOption{
|
||||
kithttp.ServerErrorLogger(logger),
|
||||
kithttp.ServerErrorEncoder(encodeError),
|
||||
}
|
||||
|
||||
trackCargoHandler := kithttp.NewServer(
|
||||
makeTrackCargoEndpoint(ts),
|
||||
decodeTrackCargoRequest,
|
||||
encodeResponse,
|
||||
opts...,
|
||||
)
|
||||
|
||||
r.Handle("/tracking/v1/cargos/{id}", trackCargoHandler).Methods("GET")
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func decodeTrackCargoRequest(_ context.Context, r *http.Request) (interface{}, error) {
|
||||
vars := mux.Vars(r)
|
||||
id, ok := vars["id"]
|
||||
if !ok {
|
||||
return nil, errors.New("bad route")
|
||||
}
|
||||
return trackCargoRequest{ID: id}, nil
|
||||
}
|
||||
|
||||
func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error {
|
||||
if e, ok := response.(errorer); ok && e.error() != nil {
|
||||
encodeError(ctx, e.error(), w)
|
||||
return nil
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type errorer interface {
|
||||
error() error
|
||||
}
|
||||
|
||||
// encode errors from business-logic
|
||||
func encodeError(_ context.Context, err error, w http.ResponseWriter) {
|
||||
switch err {
|
||||
case cargo.ErrUnknown:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
case ErrInvalidArgument:
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
default:
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
40
vendor/github.com/go-kit/kit/examples/shipping/voyage/sample_voyages.go
generated
vendored
Normal file
40
vendor/github.com/go-kit/kit/examples/shipping/voyage/sample_voyages.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package voyage
|
||||
|
||||
import "github.com/go-kit/kit/examples/shipping/location"
|
||||
|
||||
// A set of sample voyages.
|
||||
var (
|
||||
V100 = New("V100", Schedule{
|
||||
[]CarrierMovement{
|
||||
{DepartureLocation: location.CNHKG, ArrivalLocation: location.JNTKO},
|
||||
{DepartureLocation: location.JNTKO, ArrivalLocation: location.USNYC},
|
||||
},
|
||||
})
|
||||
|
||||
V300 = New("V300", Schedule{
|
||||
[]CarrierMovement{
|
||||
{DepartureLocation: location.JNTKO, ArrivalLocation: location.NLRTM},
|
||||
{DepartureLocation: location.NLRTM, ArrivalLocation: location.DEHAM},
|
||||
{DepartureLocation: location.DEHAM, ArrivalLocation: location.AUMEL},
|
||||
{DepartureLocation: location.AUMEL, ArrivalLocation: location.JNTKO},
|
||||
},
|
||||
})
|
||||
|
||||
V400 = New("V400", Schedule{
|
||||
[]CarrierMovement{
|
||||
{DepartureLocation: location.DEHAM, ArrivalLocation: location.SESTO},
|
||||
{DepartureLocation: location.SESTO, ArrivalLocation: location.FIHEL},
|
||||
{DepartureLocation: location.FIHEL, ArrivalLocation: location.DEHAM},
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
// These voyages are hard-coded into the current pathfinder. Make sure
|
||||
// they exist.
|
||||
var (
|
||||
V0100S = New("0100S", Schedule{[]CarrierMovement{}})
|
||||
V0200T = New("0200T", Schedule{[]CarrierMovement{}})
|
||||
V0300A = New("0300A", Schedule{[]CarrierMovement{}})
|
||||
V0301S = New("0301S", Schedule{[]CarrierMovement{}})
|
||||
V0400S = New("0400S", Schedule{[]CarrierMovement{}})
|
||||
)
|
44
vendor/github.com/go-kit/kit/examples/shipping/voyage/voyage.go
generated
vendored
Normal file
44
vendor/github.com/go-kit/kit/examples/shipping/voyage/voyage.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
// Package voyage provides the Voyage aggregate.
|
||||
package voyage
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/examples/shipping/location"
|
||||
)
|
||||
|
||||
// Number uniquely identifies a particular Voyage.
|
||||
type Number string
|
||||
|
||||
// Voyage is a uniquely identifiable series of carrier movements.
|
||||
type Voyage struct {
|
||||
Number Number
|
||||
Schedule Schedule
|
||||
}
|
||||
|
||||
// New creates a voyage with a voyage number and a provided schedule.
|
||||
func New(n Number, s Schedule) *Voyage {
|
||||
return &Voyage{Number: n, Schedule: s}
|
||||
}
|
||||
|
||||
// Schedule describes a voyage schedule.
|
||||
type Schedule struct {
|
||||
CarrierMovements []CarrierMovement
|
||||
}
|
||||
|
||||
// CarrierMovement is a vessel voyage from one location to another.
|
||||
type CarrierMovement struct {
|
||||
DepartureLocation location.UNLocode
|
||||
ArrivalLocation location.UNLocode
|
||||
DepartureTime time.Time
|
||||
ArrivalTime time.Time
|
||||
}
|
||||
|
||||
// ErrUnknown is used when a voyage could not be found.
|
||||
var ErrUnknown = errors.New("unknown voyage")
|
||||
|
||||
// Repository provides access a voyage store.
|
||||
type Repository interface {
|
||||
Find(Number) (*Voyage, error)
|
||||
}
|
Reference in New Issue
Block a user