Skip to content

Commit

Permalink
Feature/osrm table connector (#139)
Browse files Browse the repository at this point in the history
* feat: initial implementation for osrm table connector
issue: #137

* feat: implement request for table service
issue: #137

* feat: implement  osrm table response
issue: #137

* fix: fix decoded problem related with table response
issue: #137

* fix: remove comments
issue: #137

* fix: fix typo.
issue: #137

* fix: add default value for Request's element
issue: #137

* fix: add comments
issue: #137

* fix: modify comments
issue: #137

* fix: refactor code.
issue: #137

* fix: in table.response using osrmtype.waypoint instead of route.waypoint
issue: #137
  • Loading branch information
CodeBear801 authored Feb 5, 2020
1 parent dc103fa commit c754036
Show file tree
Hide file tree
Showing 7 changed files with 330 additions and 0 deletions.
9 changes: 9 additions & 0 deletions integration/pkg/api/osrm/osrmtype/waypoint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package osrmtype

// Waypoint object used to describe waypoint used in route or table.
type Waypoint struct {
Name string `json:"name"`
Location [2]float64 `json:"location,omitempty"` // [longitude, latitude]
Distance float64 `json:"distance"`
Hint string `json:"hint"`
}
8 changes: 8 additions & 0 deletions integration/pkg/api/osrm/table/options/key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package options

// Table service Query Parameter/Option Keys
const (
KeySources = "sources"
KeyDestinations = "destinations"
KeyAnnotations = "annotations"
)
30 changes: 30 additions & 0 deletions integration/pkg/api/osrm/table/options/parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package options

import (
"fmt"
"strings"

"github.com/Telenav/osrm-backend/integration/pkg/api"
"github.com/golang/glog"
)

// ParseAnnotations parses table service Annotations option.
func ParseAnnotations(s string) (string, error) {

validAnnotationsValues := map[string]struct{}{
AnnotationsValueDistance: struct{}{},
AnnotationsValueDuration: struct{}{},
}

splits := strings.Split(s, api.Comma)
for _, split := range splits {
if _, found := validAnnotationsValues[split]; !found {

err := fmt.Errorf("invalid annotations value: %s", s)
glog.Warning(err)
return "", err
}
}

return s, nil
}
12 changes: 12 additions & 0 deletions integration/pkg/api/osrm/table/options/values.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package options

// Annotations default value
const (
AnnotationsDefaultValue = AnnotationsValueDuration
)

// Annotations values
const (
AnnotationsValueDistance = "distance"
AnnotationsValueDuration = "duration"
)
158 changes: 158 additions & 0 deletions integration/pkg/api/osrm/table/request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package table

import (
"fmt"
"net/url"
"strings"

"github.com/Telenav/osrm-backend/integration/pkg/api"
"github.com/Telenav/osrm-backend/integration/pkg/api/osrm/coordinate"
"github.com/Telenav/osrm-backend/integration/pkg/api/osrm/genericoptions"
"github.com/Telenav/osrm-backend/integration/pkg/api/osrm/table/options"
"github.com/golang/glog"
)

// Request for OSRM table service
// http://project-osrm.org/docs/v5.5.1/api/#table-service
type Request struct {
// Path
Service string
Version string
Profile string
Coordinates coordinate.Coordinates

// Options
Sources genericoptions.Elements
Destinations genericoptions.Elements
Annotations string
}

// NewRequest create an empty table Request.
func NewRequest() *Request {

return &Request{
// Path
Service: "table",
Version: "v1",
Profile: "driving",
Coordinates: coordinate.Coordinates{},

// Options
Sources: genericoptions.Elements{},
Destinations: genericoptions.Elements{},
Annotations: options.AnnotationsDefaultValue,
}

}

// ParseRequestURL parse Request URL to Request.
func ParseRequestURL(u *url.URL) (*Request, error) {
if u == nil {
return nil, fmt.Errorf("empty URL input")
}

req := NewRequest()

if err := req.parsePath(u.Path); err != nil {
return nil, err
}

req.parseQuery(api.ParseQueryDiscardError(u.RawQuery))

return req, nil
}

func (r *Request) parsePath(path string) error {
p := path
p = strings.TrimPrefix(p, api.Slash)
p = strings.TrimSuffix(p, api.Slash)

s := strings.Split(p, api.Slash)
if len(s) < 4 {
return fmt.Errorf("invalid path values %v parsed from %s", s, path)
}
r.Service = s[0]
r.Version = s[1]
r.Profile = s[2]

var err error
if r.Coordinates, err = coordinate.ParseCoordinates(s[3]); err != nil {
return err
}

return nil
}

func (r *Request) parseQuery(values url.Values) {

if v := values.Get(options.KeySources); len(v) > 0 {
if sources, err := genericoptions.ParseElemenets(v); err == nil {
r.Sources = sources
}
}

if v := values.Get(options.KeyDestinations); len(v) > 0 {
if destinations, err := genericoptions.ParseElemenets(v); err == nil {
r.Destinations = destinations
}
}

if v := values.Get(options.KeyAnnotations); len(v) > 0 {
if annotations, err := options.ParseAnnotations(v); err == nil {
r.Annotations = annotations
}
}
}

// RequestURI convert TableRequest to RequestURI (e.g. "/path?foo=bar").
// see more in https://golang.org/pkg/net/url/#URL.RequestURI
func (r *Request) RequestURI() string {
s := r.pathPrefix()

coordinatesStr := r.Coordinates.String()
if len(coordinatesStr) > 0 {
s += coordinatesStr
}

queryStr := r.QueryString()
if len(queryStr) > 0 {
s += api.QuestionMark + queryStr
}

return s
}

func (r *Request) pathPrefix() string {
//i.e. "/table/v1/driving/"
return api.Slash + r.Service + api.Slash + r.Version + api.Slash + r.Profile + api.Slash
}

// QueryString convert TableRequest to "URL encoded" form ("bar=baz&foo=quux"), but NOT escape.
func (r *Request) QueryString() string {
rawQuery := r.QueryValues().Encode()
query, err := url.QueryUnescape(rawQuery)
if err != nil {
glog.Warning(err)
return rawQuery // use rawQuery if unescape fail
}
return query
}

// QueryValues convert table Request to url.Values.
func (r *Request) QueryValues() (v url.Values) {
v = make(url.Values)

if len(r.Sources) > 0 {
v.Add(options.KeySources, r.Sources.String())
}

if len(r.Destinations) > 0 {
v.Add(options.KeyDestinations, r.Destinations.String())
}

if len(r.Annotations) > 0 {
v.Add(options.KeyAnnotations, r.Annotations)
}

return
}
98 changes: 98 additions & 0 deletions integration/pkg/api/osrm/table/request_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package table

import (
"testing"

"github.com/Telenav/osrm-backend/integration/pkg/api/osrm/coordinate"
"github.com/Telenav/osrm-backend/integration/pkg/api/osrm/genericoptions"
)

func TestTableRequestURI(t *testing.T) {
cases := []struct {
r Request
expect string
}{
{
Request{
Service: "table",
Version: "v1",
Profile: "driving",
Coordinates: coordinate.Coordinates{coordinate.Coordinate{Lat: 37.364336, Lon: -122.006349}, coordinate.Coordinate{Lat: 37.313767, Lon: -121.875654}},
},
"/table/v1/driving/-122.006349,37.364336;-121.875654,37.313767",
},
{
Request{
Service: "table",
Version: "v1",
Profile: "driving",
Coordinates: coordinate.Coordinates{coordinate.Coordinate{Lat: 37.364336, Lon: -122.006349}, coordinate.Coordinate{Lat: 37.313767, Lon: -121.875654}, coordinate.Coordinate{Lat: 37.313769, Lon: -121.875655}},
},
"/table/v1/driving/-122.006349,37.364336;-121.875654,37.313767;-121.875655,37.313769",
},
{
Request{
Service: "table",
Version: "v1",
Profile: "driving",
Coordinates: coordinate.Coordinates{coordinate.Coordinate{Lat: 37.364336, Lon: -122.006349}, coordinate.Coordinate{Lat: 37.313767, Lon: -121.875654}, coordinate.Coordinate{Lat: 37.313769, Lon: -121.875655}},
Sources: genericoptions.Elements{"0"},
},
"/table/v1/driving/-122.006349,37.364336;-121.875654,37.313767;-121.875655,37.313769?sources=0",
},
{
Request{
Service: "table",
Version: "v1",
Profile: "driving",
Coordinates: coordinate.Coordinates{coordinate.Coordinate{Lat: 37.364336, Lon: -122.006349}, coordinate.Coordinate{Lat: 37.313767, Lon: -121.875654}, coordinate.Coordinate{Lat: 37.313769, Lon: -121.875655}},
Sources: genericoptions.Elements{"0"},
Destinations: genericoptions.Elements{"1", "2"},
},
"/table/v1/driving/-122.006349,37.364336;-121.875654,37.313767;-121.875655,37.313769?destinations=1;2&sources=0",
},
{
Request{
Service: "table",
Version: "v1",
Profile: "driving",
Coordinates: coordinate.Coordinates{coordinate.Coordinate{Lat: 37.364336, Lon: -122.006349}, coordinate.Coordinate{Lat: 37.313767, Lon: -121.875654}, coordinate.Coordinate{Lat: 37.313769, Lon: -121.875655}},
Sources: genericoptions.Elements{"0"},
Destinations: genericoptions.Elements{"1", "2"},
Annotations: "duration",
},
"/table/v1/driving/-122.006349,37.364336;-121.875654,37.313767;-121.875655,37.313769?annotations=duration&destinations=1;2&sources=0",
},
{
Request{
Service: "table",
Version: "v1",
Profile: "driving",
Coordinates: coordinate.Coordinates{coordinate.Coordinate{Lat: 37.364336, Lon: -122.006349}, coordinate.Coordinate{Lat: 37.313767, Lon: -121.875654}, coordinate.Coordinate{Lat: 37.313769, Lon: -121.875655}},
Sources: genericoptions.Elements{"0"},
Destinations: genericoptions.Elements{"1", "2"},
Annotations: "distance",
},
"/table/v1/driving/-122.006349,37.364336;-121.875654,37.313767;-121.875655,37.313769?annotations=distance&destinations=1;2&sources=0",
},
{
Request{
Service: "table",
Version: "v1",
Profile: "driving",
Coordinates: coordinate.Coordinates{coordinate.Coordinate{Lat: 37.364336, Lon: -122.006349}, coordinate.Coordinate{Lat: 37.313767, Lon: -121.875654}, coordinate.Coordinate{Lat: 37.313769, Lon: -121.875655}},
Sources: genericoptions.Elements{"0"},
Destinations: genericoptions.Elements{"1", "2"},
Annotations: "duration,distance",
},
"/table/v1/driving/-122.006349,37.364336;-121.875654,37.313767;-121.875655,37.313769?annotations=duration,distance&destinations=1;2&sources=0",
},
}

for _, c := range cases {
s := c.r.RequestURI()
if s != c.expect {
t.Errorf("%v QueryString(), expect %s, but got %s", c.r, c.expect, s)
}
}
}
15 changes: 15 additions & 0 deletions integration/pkg/api/osrm/table/response.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package table

import (
"github.com/Telenav/osrm-backend/integration/pkg/api/osrm/osrmtype"
)

// Response represents OSRM api v1 table response.
type Response struct {
Code string `json:"code"`
Message string `json:"message,omitempty"`
Sources []*osrmtype.Waypoint `json:"sources"`
Destinations []*osrmtype.Waypoint `json:"destinations"`
Durations [][]*float64 `json:"durations"`
Distances [][]*float64 `json:"distances"`
}

0 comments on commit c754036

Please sign in to comment.