New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MYST 532 Tequilapi new Location Endpoint #235
Changes from 5 commits
6af7dd4
8db43b2
046a30a
adc974b
099ee11
44951b2
8cdd263
26851de
eb9d988
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,14 +61,16 @@ func NewCommandWith( | |
filepath.Join(options.DirectoryConfig, options.LocationDatabase), | ||
) | ||
|
||
locationCache := location.NewLocationCache(locationDetector) | ||
|
||
vpnClientFactory := connection.ConfigureVpnClientFactory( | ||
mysteriumClient, | ||
options.OpenvpnBinary, | ||
options.DirectoryConfig, | ||
options.DirectoryRuntime, | ||
signerFactory, | ||
statsKeeper, | ||
locationDetector, | ||
locationCache, | ||
) | ||
connectionManager := connection.NewManager(mysteriumClient, dialogFactory, vpnClientFactory, statsKeeper) | ||
|
||
|
@@ -82,10 +84,12 @@ func NewCommandWith( | |
checkOpenvpn: func() error { | ||
return openvpn.CheckOpenvpnBinary(options.OpenvpnBinary) | ||
}, | ||
locationCache: locationCache, | ||
} | ||
|
||
tequilapi_endpoints.AddRoutesForIdentities(router, identityManager, mysteriumClient, signerFactory) | ||
tequilapi_endpoints.AddRoutesForConnection(router, connectionManager, ipResolver, statsKeeper) | ||
tequilapi_endpoints.AddRoutesForLocation(router, locationDetector, locationCache) | ||
tequilapi_endpoints.AddRoutesForProposals(router, mysteriumClient) | ||
tequilapi_endpoints.AddRouteForStop(router, node_cmd.NewApplicationStopper(command.Kill)) | ||
|
||
|
@@ -97,9 +101,10 @@ type Command struct { | |
connectionManager connection.Manager | ||
httpAPIServer tequilapi.APIServer | ||
checkOpenvpn func() error | ||
locationCache location.Cache | ||
} | ||
|
||
// Start starts Tequilapi service - does not block | ||
// Start starts Tequilapi service, fetches location | ||
func (cmd *Command) Start() error { | ||
log.Info("[Client version]", version.AsString()) | ||
err := cmd.checkOpenvpn() | ||
|
@@ -118,6 +123,13 @@ func (cmd *Command) Start() error { | |
} | ||
log.Infof("Api started on: %d", port) | ||
|
||
originalLocation, err := cmd.locationCache.RefreshAndGet() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are't You using/injecting variable There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because after calling RefreshAndGet() location is fetched and stored in cache for future uses There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By the way - I suggest moving log.xx line after location is fetched. In case of error it should not display Api started... and then error There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
if err != nil { | ||
log.Warn("Failed to detect country", err) | ||
} else { | ||
log.Info("Country detected: ", originalLocation.Country) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,5 +7,11 @@ type Resolver interface { | |
|
||
// Detector allows detecting location by current ip | ||
type Detector interface { | ||
DetectCountry() (string, error) | ||
DetectLocation() (Location, error) | ||
} | ||
|
||
// Cache allows caching location | ||
type Cache interface { | ||
Get() (Location, error) | ||
RefreshAndGet() (Location, error) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Most of uses of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree. And printing can just take with |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package location | ||
|
||
type Location struct { | ||
IP string `json:"ip"` | ||
Country string `json:"country"` | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package location | ||
|
||
type locationCache struct { | ||
locationDetector Detector | ||
location Location | ||
err error | ||
} | ||
|
||
// NewLocationCache constructs Cache | ||
func NewLocationCache(locationDetector Detector) Cache { | ||
return &locationCache{ | ||
locationDetector: locationDetector, | ||
} | ||
} | ||
|
||
// Gets location from cache | ||
func (lc *locationCache) Get() (Location, error) { | ||
return lc.location, lc.err | ||
} | ||
|
||
// Stores location to cache | ||
func (lc *locationCache) RefreshAndGet() (Location, error) { | ||
location, err := lc.locationDetector.DetectLocation() | ||
lc.location = location | ||
lc.err = err | ||
return lc.location, lc.err | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package location | ||
|
||
import ( | ||
"testing" | ||
"errors" | ||
"github.com/mysterium/node/ip" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestLocationCacheFirstCall(t *testing.T) { | ||
ipResolver := ip.NewFakeResolver("100.100.100.100") | ||
locationResolver := NewResolverFake("country") | ||
locationDetector := NewDetectorWithLocationResolver(ipResolver, locationResolver) | ||
locationCache := NewLocationCache(locationDetector) | ||
location, err := locationCache.Get() | ||
assert.Equal(t, Location{}, location) | ||
assert.NoError(t, err) | ||
} | ||
|
||
func TestLocationCacheFirstSecondCalls(t *testing.T) { | ||
ipResolver := ip.NewFakeResolver("100.100.100.100") | ||
locationResolver := NewResolverFake("country") | ||
locationDetector := NewDetectorWithLocationResolver(ipResolver, locationResolver) | ||
locationCache := NewLocationCache(locationDetector) | ||
location, err := locationCache.RefreshAndGet() | ||
assert.Equal(t, "country", location.Country) | ||
assert.Equal(t, "100.100.100.100", location.IP) | ||
assert.NoError(t, err) | ||
|
||
locationSecondCall, err := locationCache.Get() | ||
assert.Equal(t, location, locationSecondCall) | ||
assert.NoError(t, err) | ||
} | ||
|
||
func TestLocationCacheWithError(t *testing.T) { | ||
ipResolver := ip.NewFakeResolver("") | ||
locationErr := errors.New("location resolver error") | ||
locationResolver := NewFailingResolverFake(locationErr) | ||
locationDetector := NewDetectorWithLocationResolver(ipResolver, locationResolver) | ||
locationCache := NewLocationCache(locationDetector) | ||
locationCache.RefreshAndGet() | ||
location, err := locationCache.Get() | ||
assert.EqualError(t, locationErr, err.Error()) | ||
assert.Equal(t, Location{}, location) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package endpoints | ||
|
||
import ( | ||
"github.com/julienschmidt/httprouter" | ||
"github.com/mysterium/node/location" | ||
"github.com/mysterium/node/tequilapi/utils" | ||
"net/http" | ||
) | ||
|
||
// LocationEndpoint struct represents /location resource and it's subresources | ||
type LocationEndpoint struct { | ||
locationDetector location.Detector | ||
locationCache location.Cache | ||
} | ||
|
||
// NewLocationEndpoint creates and returns location endpoint | ||
func NewLocationEndpoint(locationDetector location.Detector, locationCache location.Cache) *LocationEndpoint { | ||
return &LocationEndpoint{ | ||
locationDetector: locationDetector, | ||
locationCache: locationCache, | ||
} | ||
} | ||
|
||
// GetLocation responds with original and current countries | ||
func (ce *LocationEndpoint) GetLocation(writer http.ResponseWriter, request *http.Request, params httprouter.Params) { | ||
originalLocation, err := ce.locationCache.Get() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we failed to get location (original) on startup do we always display that error? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would make it '(le *LocationEndpoint)', not '(ce *LocationEndpoint)' There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In case only one of original and current ip is not available, it makes sense to return what we have. I.e. {
original: null,
current: "1.1.1.1"
} or {
original: "1.1.1.1",
current: null
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @tadovas changed logic where locationCache.Get() no longer returns error so the answer is no, we display error on startup only There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @donce Currently if some data is not available, it has empty value {
"original": {
"ip": "",
"country": ""
},
"current": {
"ip": "1.1.1.1",
"country": ""
}
} |
||
if err != nil { | ||
utils.SendError(writer, err, http.StatusServiceUnavailable) | ||
return | ||
} | ||
|
||
currentLocation, err := ce.locationDetector.DetectLocation() | ||
if err != nil { | ||
utils.SendError(writer, err, http.StatusServiceUnavailable) | ||
return | ||
} | ||
|
||
response := struct { | ||
Original location.Location `json:"original"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. startup exposes 'when' it was looked up, while 'original' represents real collected country. I would go for original There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, when you put it like this, it does make sense - but at first look, it didn't |
||
Current location.Location `json:"current"` | ||
}{ | ||
Original: originalLocation, | ||
Current: currentLocation, | ||
} | ||
utils.WriteAsJSON(response, writer) | ||
} | ||
|
||
// AddRoutesForLocation adds location routes to given router | ||
func AddRoutesForLocation(router *httprouter.Router, locationDetector location.Detector, locationCache location.Cache) { | ||
locationEndpoint := NewLocationEndpoint(locationDetector, locationCache) | ||
router.GET("/location", locationEndpoint.GetLocation) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not detecting on connection start?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it mean that we are always getting startup location?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting location on app start