Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions server/src/Http/Controllers/Internal/v1/GeocoderController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
namespace Fleetbase\FleetOps\Http\Controllers\Internal\v1;

use Fleetbase\FleetOps\Models\Place;
use Fleetbase\FleetOps\Support\Geocoding;
use Fleetbase\FleetOps\Support\Utils;
use Fleetbase\Http\Controllers\Controller;
use Geocoder\Laravel\Facades\Geocoder;
use Geocoder\Query\GeocodeQuery;
use Geocoder\Query\ReverseQuery;
use Illuminate\Http\Request;

class GeocoderController extends Controller
Expand Down Expand Up @@ -97,14 +99,18 @@ function ($googleAddress) {
return response()->json([]);
}

// Both lookups go through the same geocoder places/lookup uses. The
// geocoder-laravel facade built its provider from config/geocoder.php at
// boot, before the settings-backed API key was loaded, and its chain
// provider swallowed the resulting failure into an empty result.
protected function reverseGeocode(float $latitude, float $longitude)
{
return Geocoder::reverse($latitude, $longitude)->get();
return collect(Geocoding::geocoder()->reverseQuery(ReverseQuery::fromCoordinates($latitude, $longitude))->all());
}

protected function forwardGeocode(string $query)
{
return Geocoder::geocode($query)->get();
return collect(Geocoding::geocoder()->geocodeQuery(GeocodeQuery::create($query))->all());
}

protected function placeFromGoogleAddress($googleAddress)
Expand Down
10 changes: 10 additions & 0 deletions server/src/Support/Geocoding.php
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,16 @@ public static function getRegion(): ?string
return config('services.google_maps.region') ?: env('GOOGLE_MAPS_REGION', 'us');
}

/**
* The configured geocoder — Google Maps with the settings-backed API key,
* region and locale — for callers that need raw address results rather
* than places.
*/
public static function geocoder(): object
{
return static::makeGeocoder();
}

protected static function makeGeocoder(): object
{
// Allow an alternative geocoder (or test double) to be injected
Expand Down
67 changes: 67 additions & 0 deletions server/tests/LookupAndGeocoderControllerContractsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,20 @@ protected function placeFromGoogleAddress($googleAddress)
}
}

// Keeps the real forward/reverse lookups so they run against whatever
// geocoder Geocoding resolves, mapping each address to its display string
class FleetOpsGeocoderControllerLookupProbe extends GeocoderController
{
public function __construct()
{
}

protected function placeFromGoogleAddress($googleAddress)
{
return ['street' => $googleAddress->getStreetName()];
}
}

class FleetOpsLookupControllerProbe extends FleetOpsLookupController
{
public Collection $contacts;
Expand Down Expand Up @@ -262,6 +276,59 @@ public function toArray(): array
->and($controller->forwardCalls)->toBe(['depot', 'depot']);
});

test('geocoder controller resolves lookups through the fleet ops geocoder rather than the facade', function () {
$geocoder = new class {
public array $queries = [];

public function geocodeQuery($query)
{
$this->queries[] = $query;

return new Geocoder\Model\AddressCollection([
Geocoder\Model\Address::createFromArray(['providedBy' => 'google', 'streetName' => 'улица Зенкова', 'latitude' => 43.2551875, 'longitude' => 76.9567153]),
]);
}

public function reverseQuery($query)
{
$this->queries[] = $query;

return new Geocoder\Model\AddressCollection([
Geocoder\Model\Address::createFromArray(['providedBy' => 'google', 'streetName' => 'Reverse Way', 'latitude' => 1.3, 'longitude' => 103.8]),
]);
}
};
app()->instance('fleetops.geocoder', $geocoder);

// A facade-bound geocoder must not be consulted: it is the one built
// before the settings-backed API key is available
app()->instance('geocoder', new class {
public function __call($method, $arguments)
{
throw new RuntimeException('geocoder facade should not be used');
}
});
Geocoder\Laravel\Facades\Geocoder::clearResolvedInstance('geocoder');

try {
$controller = new FleetOpsGeocoderControllerLookupProbe();
$forward = $controller->geocode(new Request(['query' => 'зенкова 21 алматы', 'single' => true]));
$reverse = $controller->reverse(new Request(['coordinates' => '1.3,103.8']));
} finally {
app()->forgetInstance('fleetops.geocoder');
app()->forgetInstance('geocoder');
Geocoder\Laravel\Facades\Geocoder::clearResolvedInstance('geocoder');
}

expect($forward->getData(true))->toBe(['street' => 'улица Зенкова'])
->and($reverse->getData(true))->toBe([['street' => 'Reverse Way']])
->and($geocoder->queries[0])->toBeInstanceOf(Geocoder\Query\GeocodeQuery::class)
->and($geocoder->queries[0]->getText())->toBe('зенкова 21 алматы')
->and($geocoder->queries[1])->toBeInstanceOf(Geocoder\Query\ReverseQuery::class)
->and($geocoder->queries[1]->getCoordinates()->getLatitude())->toBe(1.3)
->and($geocoder->queries[1]->getCoordinates()->getLongitude())->toBe(103.8);
});

test('fleet ops lookup controller merges polymorphic contacts vendors and integrated facilitators', function () {
session(['company' => 'company-uuid']);

Expand Down
17 changes: 6 additions & 11 deletions server/tests/Unit/Support/MoreQuerySeamsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -305,30 +305,25 @@ public function listTransactions(Fleetbase\FleetOps\Models\FuelProviderConnectio

test('geocoder controller seams call the geocoder and build places', function () {
fleetopsMoreSeamBoot();
app()->instance('geocoder', new class {
public function geocode($address)
app()->instance('fleetops.geocoder', new class {
public function geocodeQuery($query)
{
return $this;
return new Geocoder\Model\AddressCollection([]);
}

public function reverse($latitude, $longitude)
public function reverseQuery($query)
{
return $this;
}

public function get()
{
return collect();
return new Geocoder\Model\AddressCollection([]);
}
});
Geocoder\Laravel\Facades\Geocoder::clearResolvedInstances();

$class = Fleetbase\FleetOps\Http\Controllers\Internal\v1\GeocoderController::class;
$controller = (new ReflectionClass($class))->newInstanceWithoutConstructor();

// Both directions delegate to the configured geocoder
expect(fleetopsMoreSeamInvoke($controller, $class, 'reverseGeocode', [1.30, 103.80]))->toHaveCount(0)
->and(fleetopsMoreSeamInvoke($controller, $class, 'forwardGeocode', ['1 Marina Bay']))->toHaveCount(0);
app()->forgetInstance('fleetops.geocoder');

// And a resolved address is turned into an unsaved place
$address = Geocoder\Provider\GoogleMaps\Model\GoogleAddress::createFromArray([
Expand Down
Loading