Use DNS Location Record for Location (#12409)
* Use DNS Location Record for Location * . * . * . * grammar fix * dns class * code changes * composer update * add missing composer.lock update * reposition Location record parsing * composer update * default yes ; change lookup order * merge master * . * move Location Record Parser to Model Level * . * fix location record code * Location precedence with tests Setting from UI disables all lookups * update composer.lock and mix-manifest.json * Style fixes Co-authored-by: Tony Murray <murraytony@gmail.com>
This commit is contained in:
parent
93b91ebc91
commit
75a0a5e374
|
@ -72,8 +72,9 @@ class OS implements Module
|
|||
$deviceModel->hardware = ($hardware ?? $deviceModel->hardware) ?: null;
|
||||
$deviceModel->features = ($features ?? $deviceModel->features) ?: null;
|
||||
$deviceModel->serial = ($serial ?? $deviceModel->serial) ?: null;
|
||||
if (! empty($location)) {
|
||||
$deviceModel->setLocation(new Location(['location' => $location]));
|
||||
if (! empty($location)) { // legacy support, remove when no longer needed
|
||||
$deviceModel->setLocation($location);
|
||||
optional($deviceModel->location)->save();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,14 +103,8 @@ class OS implements Module
|
|||
private function updateLocation(\LibreNMS\OS $os)
|
||||
{
|
||||
$device = $os->getDevice();
|
||||
if ($device->override_sysLocation) {
|
||||
optional($device->location)->lookupCoordinates();
|
||||
} else {
|
||||
$new = $os->fetchLocation(); // fetch location data from device
|
||||
$new->lookupCoordinates();
|
||||
$device->setLocation($new);
|
||||
}
|
||||
|
||||
$new_location = $device->override_sysLocation ? new Location() : $os->fetchLocation(); // fetch location data from device
|
||||
$device->setLocation($new_location, true); // set location and lookup coordinates if needed
|
||||
optional($device->location)->save();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
/**
|
||||
* Dns.php
|
||||
*
|
||||
* Get version info about LibreNMS and various components/dependencies
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @link http://librenms.org
|
||||
* @copyright 2021 Thomas Berberich
|
||||
* @author Thomas Berberch <sourcehhdoctor@gmail.com>
|
||||
*/
|
||||
|
||||
namespace LibreNMS\Util;
|
||||
|
||||
use LibreNMS\Interfaces\Geocoder;
|
||||
|
||||
class Dns implements Geocoder
|
||||
{
|
||||
protected $resolver;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->resolver = new \Net_DNS2_Resolver();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $domain Domain which has to be parsed
|
||||
* @param string $record DNS Record which should be searched
|
||||
* @return array List of matching records
|
||||
*/
|
||||
public function getRecord($domain, $record = 'A')
|
||||
{
|
||||
try {
|
||||
$ret = $this->resolver->query($domain, $record);
|
||||
|
||||
return $ret->answer;
|
||||
} catch (\Net_DNS2_Exception $e) {
|
||||
d_echo('::query() failed: ' . $e->getMessage());
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public function getCoordinates($hostname)
|
||||
{
|
||||
foreach ($this->getRecord($hostname, 'LOC') as $record) {
|
||||
return [
|
||||
'lat' => $record->latitude,
|
||||
'lng' => $record->longitude,
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
|
@ -53,6 +53,7 @@ class LocationController extends Controller
|
|||
]);
|
||||
|
||||
$location->fill($request->only(['lat', 'lng']));
|
||||
$location->fixed_coordinates = true; // user has set coordinates, block automated changes
|
||||
$location->save();
|
||||
|
||||
return response()->json(['status' => 'success']);
|
||||
|
|
|
@ -327,29 +327,37 @@ class Device extends BaseModel
|
|||
/**
|
||||
* Update the location to the correct location and update GPS if needed
|
||||
*
|
||||
* @param \App\Models\Location $location location data
|
||||
* @param \App\Models\Location|string $new_location location data
|
||||
* @param string $hostname
|
||||
* @param bool $doLookup try to lookup the GPS coordinates
|
||||
*/
|
||||
public function setLocation(Location $location)
|
||||
public function setLocation($new_location, $doLookup = false)
|
||||
{
|
||||
$location->location = $location->location ? Rewrite::location($location->location) : null;
|
||||
$new_location = $new_location instanceof Location ? $new_location : new Location(['location' => $new_location]);
|
||||
$new_location->location = $new_location->location ? Rewrite::location($new_location->location) : null;
|
||||
$coord = array_filter($new_location->only(['lat', 'lng']));
|
||||
|
||||
if (! $location->location) { // disassociate if the location name is empty
|
||||
$this->location_id = null;
|
||||
if (! $this->override_sysLocation) {
|
||||
if (! $new_location->location) { // disassociate if the location name is empty
|
||||
$this->location()->dissociate();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$coord = array_filter($location->only(['lat', 'lng']));
|
||||
if (! $this->relationLoaded('location') || optional($this->location)->location !== $location->location) {
|
||||
if (! $location->exists) { // don't fetch if new location persisted to the DB, just use it
|
||||
$location = Location::firstOrCreate(['location' => $location->location], $coord);
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $this->relationLoaded('location') || optional($this->location)->location !== $new_location->location) {
|
||||
if (! $new_location->exists) { // don't fetch if new location persisted to the DB, just use it
|
||||
$new_location = Location::firstOrCreate(['location' => $new_location->location], $coord);
|
||||
}
|
||||
$this->location()->associate($new_location);
|
||||
}
|
||||
$this->location()->associate($location);
|
||||
}
|
||||
|
||||
// save new coords if needed
|
||||
if ($this->location) {
|
||||
$this->location->fill($coord)->save();
|
||||
// set coordinates
|
||||
if ($this->location && ! $this->location->fixed_coordinates) {
|
||||
$this->location->fill($coord);
|
||||
if ($doLookup && empty($coord)) { // only if requested and coordinates not passed explicitly
|
||||
$this->location->lookupCoordinates($this->hostname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,30 +26,21 @@ namespace App\Models;
|
|||
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use LibreNMS\Util\Dns;
|
||||
|
||||
class Location extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
public $fillable = ['location', 'lat', 'lng'];
|
||||
const CREATED_AT = null;
|
||||
const UPDATED_AT = 'timestamp';
|
||||
protected $casts = ['lat' => 'float', 'lng' => 'float'];
|
||||
protected $casts = ['lat' => 'float', 'lng' => 'float', 'fixed_coordinates' => 'bool'];
|
||||
|
||||
private $location_regex = '/\[\s*(?<lat>[-+]?(?:[1-8]?\d(?:\.\d+)?|90(?:\.0+)?))\s*,\s*(?<lng>[-+]?(?:180(?:\.0+)?|(?:(?:1[0-7]\d)|(?:[1-9]?\d))(?:\.\d+)?))\s*\]/';
|
||||
|
||||
/**
|
||||
* Set up listeners for this Model
|
||||
*/
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::creating(function (Location $location) {
|
||||
// parse coordinates for new locations
|
||||
$location->lookupCoordinates();
|
||||
});
|
||||
}
|
||||
|
||||
// ---- Helper Functions ----
|
||||
|
||||
/**
|
||||
|
@ -73,37 +64,57 @@ class Location extends Model
|
|||
}
|
||||
|
||||
/**
|
||||
* Try to parse coordinates then
|
||||
* call geocoding API to resolve latitude and longitude.
|
||||
* Try to parse coordinates
|
||||
* then try to lookup DNS LOC records if hostname is provided
|
||||
* then call geocoding API to resolve latitude and longitude.
|
||||
*
|
||||
* @param string $hostname
|
||||
* @return bool
|
||||
*/
|
||||
public function lookupCoordinates()
|
||||
public function lookupCoordinates($hostname = null)
|
||||
{
|
||||
if (! $this->hasCoordinates() && $this->location) {
|
||||
$this->parseCoordinates();
|
||||
if ($this->location && $this->parseCoordinates()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! $this->hasCoordinates() && \LibreNMS\Config::get('geoloc.latlng', true)) {
|
||||
$this->fetchCoordinates();
|
||||
$this->updateTimestamps();
|
||||
if ($hostname && \LibreNMS\Config::get('geoloc.dns')) {
|
||||
$coord = app(Dns::class)->getCoordinates($hostname);
|
||||
|
||||
if (! empty($coord)) {
|
||||
$this->fill($coord);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->location && ! $this->hasCoordinates() && \LibreNMS\Config::get('geoloc.latlng', true)) {
|
||||
return $this->fetchCoordinates();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove encoded GPS for nicer display
|
||||
*
|
||||
* @param bool $withCoords
|
||||
* @return string
|
||||
*/
|
||||
public function display()
|
||||
public function display($withCoords = false)
|
||||
{
|
||||
return (trim(preg_replace($this->location_regex, '', $this->location)) ?: $this->location)
|
||||
. ($this->coordinatesValid() ? " [$this->lat, $this->lng]" : '');
|
||||
. ($withCoords && $this->coordinatesValid() ? " [$this->lat,$this->lng]" : '');
|
||||
}
|
||||
|
||||
protected function parseCoordinates()
|
||||
{
|
||||
if (preg_match($this->location_regex, $this->location, $parsed)) {
|
||||
$this->fill($parsed);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function fetchCoordinates()
|
||||
|
@ -112,9 +123,13 @@ class Location extends Model
|
|||
/** @var \LibreNMS\Interfaces\Geocoder $api */
|
||||
$api = app(\LibreNMS\Interfaces\Geocoder::class);
|
||||
$this->fill($api->getCoordinates($this->location));
|
||||
|
||||
return true;
|
||||
} catch (BindingResolutionException $e) {
|
||||
// could not resolve geocoder, Laravel isn't booted. Fail silently.
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---- Query scopes ----
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
"oriceon/toastr-5-laravel": "dev-master",
|
||||
"pear/console_color2": "^0.1",
|
||||
"pear/console_table": "^1.3",
|
||||
"pear/net_dns2": "^1.5",
|
||||
"php-amqplib/php-amqplib": "^2.0",
|
||||
"phpmailer/phpmailer": "~6.0",
|
||||
"predis/predis": "^1.1",
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "0911a4d37f7dda7d8d6c8d1cfe3c1bbc",
|
||||
"content-hash": "74b245e22b8cf40ae29fdc12cc2e59eb",
|
||||
"packages": [
|
||||
{
|
||||
"name": "amenadiel/jpgraph",
|
||||
|
@ -2874,6 +2874,57 @@
|
|||
},
|
||||
"time": "2018-01-25T20:47:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pear/net_dns2",
|
||||
"version": "v1.5.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mikepultz/netdns2.git",
|
||||
"reference": "d5dbae0b0c0567923d25b3ae5e2bf1e9cbcedf76"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/mikepultz/netdns2/zipball/d5dbae0b0c0567923d25b3ae5e2bf1e9cbcedf76",
|
||||
"reference": "d5dbae0b0c0567923d25b3ae5e2bf1e9cbcedf76",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Net_DNS2": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-2-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mike Pultz",
|
||||
"email": "mike@mikepultz.com",
|
||||
"homepage": "https://mikepultz.com/",
|
||||
"role": "lead"
|
||||
}
|
||||
],
|
||||
"description": "Native PHP DNS Resolver and Updater Library",
|
||||
"homepage": "https://netdns2.com/",
|
||||
"keywords": [
|
||||
"PEAR",
|
||||
"dns",
|
||||
"network"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/mikepultz/netdns2/issues",
|
||||
"source": "https://github.com/mikepultz/netdns2"
|
||||
},
|
||||
"time": "2020-10-11T17:33:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-amqplib/php-amqplib",
|
||||
"version": "v2.12.1",
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Location;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class LocationFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = Location::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
return [
|
||||
'location' => $this->faker->randomElement([
|
||||
$this->faker->sentence($this->faker->numberBetween(1, 10)),
|
||||
str_replace("\n", ' ', $this->faker->address),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate add lat,lng
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Factories\Factory
|
||||
*/
|
||||
public function withCoordinates()
|
||||
{
|
||||
return $this->state(function (array $attributes) {
|
||||
return [
|
||||
'lat' => $this->faker->latitude,
|
||||
'lng' => $this->faker->longitude,
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class LocationAddFixedCoordinatesFlag extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('locations', function (Blueprint $table) {
|
||||
$table->boolean('fixed_coordinates')->default(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('locations', function (Blueprint $table) {
|
||||
$table->dropColumn('fixed_coordinates');
|
||||
});
|
||||
}
|
||||
}
|
|
@ -3,12 +3,12 @@
|
|||
"/js/manifest.js": "/js/manifest.js?id=411da0f32dfa6d682e04",
|
||||
"/css/app.css": "/css/app.css?id=996b9e3da0c3ab98067e",
|
||||
"/js/vendor.js": "/js/vendor.js?id=54e44dd06cb8f6a3e6fe",
|
||||
"/js/lang/de.js": "/js/lang/de.js?id=2c4ad02fa89b684d4f57",
|
||||
"/js/lang/en.js": "/js/lang/en.js?id=3760dc4a781c3ac20b3e",
|
||||
"/js/lang/fr.js": "/js/lang/fr.js?id=9552304f4fd7338af018",
|
||||
"/js/lang/it.js": "/js/lang/it.js?id=514765c5399ffaa111b9",
|
||||
"/js/lang/de.js": "/js/lang/de.js?id=d57e11c0b49446e43d32",
|
||||
"/js/lang/en.js": "/js/lang/en.js?id=abb57dae3941488e07e9",
|
||||
"/js/lang/fr.js": "/js/lang/fr.js?id=5c985dc7ace8c7f28baf",
|
||||
"/js/lang/it.js": "/js/lang/it.js?id=b28a63928155eeb4e2a1",
|
||||
"/js/lang/ru.js": "/js/lang/ru.js?id=f6b7c078755312a0907c",
|
||||
"/js/lang/uk.js": "/js/lang/uk.js?id=c19a5dcee4724579cb41",
|
||||
"/js/lang/zh-CN.js": "/js/lang/zh-CN.js?id=68da151165752f2e7983",
|
||||
"/js/lang/zh-TW.js": "/js/lang/zh-TW.js?id=69f27405927f6e708f84"
|
||||
"/js/lang/zh-CN.js": "/js/lang/zh-CN.js?id=12f95651fb6629cbf3f3",
|
||||
"/js/lang/zh-TW.js": "/js/lang/zh-TW.js?id=87ab9d2f187593100bc3"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Device;
|
||||
use App\Models\Location;
|
||||
|
||||
require_once 'includes/html/modal/device_maintenance.inc.php';
|
||||
|
||||
|
@ -16,15 +15,12 @@ if ($_POST['editing']) {
|
|||
}
|
||||
|
||||
$override_sysLocation = (int) isset($_POST['override_sysLocation']);
|
||||
$override_sysLocation_string = isset($_POST['sysLocation']) ? $_POST['sysLocation'] : null;
|
||||
$override_sysLocation_string = $_POST['sysLocation'] ?? null;
|
||||
|
||||
if ($override_sysLocation) {
|
||||
if ($override_sysLocation_string) {
|
||||
$location = Location::firstOrCreate(['location' => $override_sysLocation_string]);
|
||||
$device_model->location()->associate($location);
|
||||
} else {
|
||||
$device_model->location()->dissociate();
|
||||
}
|
||||
$device_model->override_sysLocation = false; // allow override (will be set to actual value later)
|
||||
$device_model->setLocation($override_sysLocation_string, true);
|
||||
optional($device_model->location)->save();
|
||||
} elseif ($device_model->override_sysLocation) {
|
||||
// no longer overridden, clear location
|
||||
$device_model->location()->dissociate();
|
||||
|
|
|
@ -1632,6 +1632,13 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"geoloc.dns": {
|
||||
"order": 3,
|
||||
"group": "webui",
|
||||
"section": "device",
|
||||
"default": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
"geoloc.engine": {
|
||||
"default": false,
|
||||
"group": "external",
|
||||
|
|
|
@ -843,6 +843,7 @@ locations:
|
|||
- { Field: lat, Type: 'double(10,6)', 'Null': true, Extra: '' }
|
||||
- { Field: lng, Type: 'double(10,6)', 'Null': true, Extra: '' }
|
||||
- { Field: timestamp, Type: datetime, 'Null': false, Extra: '' }
|
||||
- { Field: fixed_coordinates, Type: tinyint, 'Null': false, Extra: '', Default: '0' }
|
||||
Indexes:
|
||||
PRIMARY: { Name: PRIMARY, Columns: [id], Unique: true, Type: BTREE }
|
||||
locations_location_unique: { Name: locations_location_unique, Columns: [location], Unique: true, Type: BTREE }
|
||||
|
|
|
@ -673,6 +673,10 @@ return [
|
|||
'description' => 'Mapping Engine API Key',
|
||||
'help' => 'Geocoding API Key (Required to function)',
|
||||
],
|
||||
'dns' => [
|
||||
'description' => 'Use DNS Location Record',
|
||||
'help' => 'Use LOC Record from DNS Server to get geographic coordinates for Hostname',
|
||||
],
|
||||
'engine' => [
|
||||
'description' => 'Mapping Engine',
|
||||
'options' => [
|
||||
|
|
|
@ -0,0 +1,233 @@
|
|||
<?php
|
||||
/*
|
||||
* LocationTest.php
|
||||
*
|
||||
* -Description-
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @package LibreNMS
|
||||
* @link http://librenms.org
|
||||
* @copyright 2021 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
namespace LibreNMS\Tests\Unit;
|
||||
|
||||
use App\Models\Device;
|
||||
use App\Models\Location;
|
||||
use LibreNMS\Config;
|
||||
use LibreNMS\Interfaces\Geocoder;
|
||||
use LibreNMS\Tests\TestCase;
|
||||
use LibreNMS\Util\Dns;
|
||||
use Mockery\MockInterface;
|
||||
|
||||
class LocationTest extends TestCase
|
||||
{
|
||||
public function testCanSetLocation()
|
||||
{
|
||||
$device = Device::factory()->make(); /** @var Device $device */
|
||||
$device->setLocation('Where');
|
||||
|
||||
$this->assertEquals($device->location->location, 'Where');
|
||||
$this->assertNull($device->location->lat);
|
||||
$this->assertNull($device->location->lng);
|
||||
|
||||
$device->setLocation(null);
|
||||
$this->assertNull($device->location);
|
||||
}
|
||||
|
||||
public function testCanNotSetLocation()
|
||||
{
|
||||
$device = Device::factory()->make(); /** @var Device $device */
|
||||
$location = Location::factory()->make();
|
||||
|
||||
$device->override_sysLocation = true;
|
||||
$device->setLocation($location->location);
|
||||
$this->assertNull($device->location);
|
||||
}
|
||||
|
||||
public function testCanSetEncodedLocation()
|
||||
{
|
||||
$device = Device::factory()->make(); /** @var Device $device */
|
||||
|
||||
// valid coords
|
||||
$location = Location::factory()->withCoordinates()->make();
|
||||
$device->setLocation("$location->location [$location->lat,$location->lng]", true);
|
||||
$this->assertEquals("$location->location [$location->lat,$location->lng]", $device->location->location);
|
||||
$this->assertEquals($location->location, $device->location->display());
|
||||
$this->assertEquals($location->lat, $device->location->lat);
|
||||
$this->assertEquals($location->lng, $device->location->lng);
|
||||
|
||||
// with space
|
||||
$location = Location::factory()->withCoordinates()->make();
|
||||
$device->setLocation("$location->location [$location->lat, $location->lng]", true);
|
||||
$this->assertEquals("$location->location [$location->lat, $location->lng]", $device->location->location);
|
||||
$this->assertEquals($location->location, $device->location->display());
|
||||
$this->assertEquals("$location->location [$location->lat,$location->lng]", $device->location->display(true));
|
||||
$this->assertEquals($location->lat, $device->location->lat);
|
||||
$this->assertEquals($location->lng, $device->location->lng);
|
||||
|
||||
// invalid coords
|
||||
$location = Location::factory()->withCoordinates()->make(['lat' => 251.5007138]);
|
||||
$name = "$location->location [$location->lat,$location->lng]";
|
||||
$device->setLocation($name, true);
|
||||
$this->assertEquals($name, $device->location->location);
|
||||
$this->assertEquals($name, $device->location->display());
|
||||
$this->assertEquals($name, $device->location->display(true));
|
||||
$this->assertNull($device->location->lat);
|
||||
$this->assertNull($device->location->lng);
|
||||
}
|
||||
|
||||
public function testCanHandleGivenCoordinates()
|
||||
{
|
||||
$device = Device::factory()->make(); /** @var Device $device */
|
||||
$location = Location::factory()->withCoordinates()->make();
|
||||
|
||||
$device->setLocation($location);
|
||||
$this->assertEquals($location->location, $device->location->location);
|
||||
$this->assertEquals($location->location, $device->location->display());
|
||||
$this->assertEquals("$location->location [$location->lat,$location->lng]", $device->location->display(true));
|
||||
$this->assertEquals($location->lat, $device->location->lat);
|
||||
$this->assertEquals($location->lng, $device->location->lng);
|
||||
}
|
||||
|
||||
public function testCanNotSetFixedCoordinates()
|
||||
{
|
||||
$device = Device::factory()->make(); /** @var Device $device */
|
||||
$locationOne = Location::factory()->withCoordinates()->make();
|
||||
$locationTwo = Location::factory(['location' => $locationOne->location])->withCoordinates()->make();
|
||||
|
||||
$device->setLocation($locationOne);
|
||||
$this->assertEquals($locationOne->lat, $device->location->lat);
|
||||
$this->assertEquals($locationOne->lng, $device->location->lng);
|
||||
|
||||
$device->location->fixed_coordinates = true;
|
||||
$device->setLocation($locationTwo);
|
||||
$this->assertEquals($locationOne->lat, $device->location->lat);
|
||||
$this->assertEquals($locationOne->lng, $device->location->lng);
|
||||
|
||||
$device->location->fixed_coordinates = false;
|
||||
$device->setLocation($locationTwo);
|
||||
$this->assertEquals($locationTwo->lat, $device->location->lat);
|
||||
$this->assertEquals($locationTwo->lng, $device->location->lng);
|
||||
}
|
||||
|
||||
public function testDnsLookup()
|
||||
{
|
||||
$example = 'SW1A2AA.find.me.uk';
|
||||
$expected = ['lat' => 51.50354111111111, 'lng' => -0.12766972222222223];
|
||||
|
||||
$result = (new Dns())->getCoordinates($example);
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
public function testCanSetDnsCoordinate()
|
||||
{
|
||||
$device = Device::factory()->make(); /** @var Device $device */
|
||||
$location = Location::factory()->withCoordinates()->make();
|
||||
|
||||
$this->mock(Dns::class, function (MockInterface $mock) use ($location) {
|
||||
$mock->shouldReceive('getCoordinates')->once()->andReturn($location->only(['lat', 'lng']));
|
||||
});
|
||||
|
||||
$device->setLocation($location->location, true);
|
||||
$this->assertEquals($location->location, $device->location->location);
|
||||
$this->assertEquals($location->lat, $device->location->lat);
|
||||
$this->assertEquals($location->lng, $device->location->lng);
|
||||
|
||||
Config::set('geoloc.dns', false);
|
||||
$device->setLocation('No DNS', true);
|
||||
$this->assertEquals('No DNS', $device->location->location);
|
||||
$this->assertNull($device->location->lat);
|
||||
$this->assertNull($device->location->lng);
|
||||
}
|
||||
|
||||
public function testCanSetByApi()
|
||||
{
|
||||
$device = Device::factory()->make(); /** @var Device $device */
|
||||
$location = Location::factory()->withCoordinates()->make();
|
||||
|
||||
$this->mock(Geocoder::class, function (MockInterface $mock) use ($location) {
|
||||
$mock->shouldReceive('getCoordinates')->once()->andReturn($location->only(['lat', 'lng']));
|
||||
});
|
||||
|
||||
Config::set('geoloc.latlng', false);
|
||||
$device->setLocation('No API', true);
|
||||
$this->assertEquals('No API', $device->location->location);
|
||||
$this->assertNull($device->location->lat);
|
||||
$this->assertNull($device->location->lng);
|
||||
|
||||
Config::set('geoloc.latlng', true);
|
||||
$device->setLocation('API', true);
|
||||
$this->assertEquals('API', $device->location->location);
|
||||
$this->assertEquals($location->lat, $device->location->lat);
|
||||
$this->assertEquals($location->lng, $device->location->lng);
|
||||
|
||||
// preset coord = skip api
|
||||
$device->setLocation('API', true);
|
||||
$this->assertEquals($location->lat, $device->location->lat);
|
||||
$this->assertEquals($location->lng, $device->location->lng);
|
||||
}
|
||||
|
||||
public function testCorrectPrecedence()
|
||||
{
|
||||
$device = Device::factory()->make(); /** @var Device $device */
|
||||
$location_encoded = Location::factory()->withCoordinates()->make();
|
||||
$location_fixed = Location::factory()->withCoordinates()->make();
|
||||
$location_api = Location::factory()->withCoordinates()->make();
|
||||
$location_dns = Location::factory()->withCoordinates()->make();
|
||||
|
||||
Config::set('geoloc.dns', true);
|
||||
$this->mock(Dns::class, function (MockInterface $mock) use ($location_dns) {
|
||||
$mock->shouldReceive('getCoordinates')->times(3)->andReturn(
|
||||
$location_dns->only(['lat', 'lng']),
|
||||
[],
|
||||
[]
|
||||
);
|
||||
});
|
||||
|
||||
Config::set('geoloc.latlng', true);
|
||||
$this->mock(Geocoder::class, function (MockInterface $mock) use ($location_api) {
|
||||
$mock->shouldReceive('getCoordinates')->once()->andReturn($location_api->only(['lat', 'lng']));
|
||||
});
|
||||
|
||||
// fixed first
|
||||
$location_fixed->location = "$location_fixed [-42, 42]"; // encoded should not be used
|
||||
$device->setLocation($location_fixed, true);
|
||||
$this->assertEquals($location_fixed->lat, $device->location->lat);
|
||||
$this->assertEquals($location_fixed->lng, $device->location->lng);
|
||||
|
||||
// then encoded
|
||||
$device->setLocation($location_encoded->display(true), true);
|
||||
$this->assertEquals($location_encoded->lat, $device->location->lat);
|
||||
$this->assertEquals($location_encoded->lng, $device->location->lng);
|
||||
|
||||
// then dns
|
||||
$device->setLocation($location_encoded->location, true);
|
||||
$this->assertEquals($location_dns->lat, $device->location->lat);
|
||||
$this->assertEquals($location_dns->lng, $device->location->lng);
|
||||
|
||||
// then api
|
||||
$device->setLocation($location_encoded->location, true);
|
||||
$this->assertEquals($location_dns->lat, $device->location->lat);
|
||||
$this->assertEquals($location_dns->lng, $device->location->lng);
|
||||
|
||||
$device->location->lat = null; // won't be used if latitude is set
|
||||
$device->setLocation($location_encoded->location, true);
|
||||
$this->assertEquals($location_api->lat, $device->location->lat);
|
||||
$this->assertEquals($location_api->lng, $device->location->lng);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue