mirror of
https://github.com/farmOS/farmOS.git
synced 2024-02-23 11:37:38 +01:00
Merge branch '2.x-location_api_caching' into 2.x
This commit is contained in:
commit
ee6e485525
|
@ -8,6 +8,6 @@ services:
|
|||
log.presave:
|
||||
class: Drupal\farm_location\EventSubscriber\LogPresaveEventSubscriber
|
||||
arguments:
|
||||
[ '@log.location', '@asset.location' ]
|
||||
[ '@log.location', '@asset.location', '@cache_tags.invalidator' ]
|
||||
tags:
|
||||
- { name: 'event_subscriber' }
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Drupal\farm_location\EventSubscriber;
|
||||
|
||||
use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
|
||||
use Drupal\farm_location\AssetLocationInterface;
|
||||
use Drupal\farm_location\LogLocationInterface;
|
||||
use Drupal\farm_location\Traits\WktTrait;
|
||||
|
@ -30,6 +31,13 @@ class LogPresaveEventSubscriber implements EventSubscriberInterface {
|
|||
*/
|
||||
private AssetLocationInterface $assetLocation;
|
||||
|
||||
/**
|
||||
* Cache tag invalidator service.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface
|
||||
*/
|
||||
private CacheTagsInvalidatorInterface $cacheTagsInvalidator;
|
||||
|
||||
/**
|
||||
* LogPresaveEventSubscriber Constructor.
|
||||
*
|
||||
|
@ -37,10 +45,13 @@ class LogPresaveEventSubscriber implements EventSubscriberInterface {
|
|||
* Log location service.
|
||||
* @param \Drupal\farm_location\AssetLocationInterface $asset_locaiton
|
||||
* Asset location service.
|
||||
* @param \Drupal\Core\Cache\CacheTagsInvalidatorInterface $cache_tags_invalidator
|
||||
* Cache tag invalidator service.
|
||||
*/
|
||||
public function __construct(LogLocationInterface $log_location, AssetLocationInterface $asset_locaiton) {
|
||||
public function __construct(LogLocationInterface $log_location, AssetLocationInterface $asset_locaiton, CacheTagsInvalidatorInterface $cache_tags_invalidator) {
|
||||
$this->logLocation = $log_location;
|
||||
$this->assetLocation = $asset_locaiton;
|
||||
$this->cacheTagsInvalidator = $cache_tags_invalidator;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -66,6 +77,21 @@ class LogPresaveEventSubscriber implements EventSubscriberInterface {
|
|||
// Get the log entity from the event.
|
||||
$log = $event->log;
|
||||
|
||||
// Populate the log geometry from the location geometry.
|
||||
$this->populateGeometryFromLocation($log);
|
||||
|
||||
// Invalidate asset caches when assets are moved.
|
||||
$this->invalidateAssetCacheOnMovement($log);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate a log's geometry based on its location.
|
||||
*
|
||||
* @param \Drupal\log\Entity\LogInterface $log
|
||||
* The Log entity.
|
||||
*/
|
||||
protected function populateGeometryFromLocation(LogInterface $log): void {
|
||||
|
||||
// If the log does not reference any location assets, we will have nothing
|
||||
// to copy from, so do nothing.
|
||||
if (!$this->logLocation->hasLocation($log)) {
|
||||
|
@ -95,8 +121,16 @@ class LogPresaveEventSubscriber implements EventSubscriberInterface {
|
|||
}
|
||||
}
|
||||
|
||||
// Populate the log geometry from the location geometry.
|
||||
$this->populateGeometryFromLocation($log);
|
||||
// Load location assets referenced by the log.
|
||||
$assets = $this->logLocation->getLocation($log);
|
||||
|
||||
// Get the combined location asset geometry.
|
||||
$wkt = $this->getCombinedAssetGeometry($assets);
|
||||
|
||||
// If the WKT is not empty, set the log geometry.
|
||||
if (!empty($wkt)) {
|
||||
$this->logLocation->setGeometry($log, $wkt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -132,26 +166,6 @@ class LogPresaveEventSubscriber implements EventSubscriberInterface {
|
|||
return $log_geometry != $location_geometry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate a log's geometry based on its location.
|
||||
*
|
||||
* @param \Drupal\log\Entity\LogInterface $log
|
||||
* The Log entity.
|
||||
*/
|
||||
protected function populateGeometryFromLocation(LogInterface $log): void {
|
||||
|
||||
// Load location assets referenced by the log.
|
||||
$assets = $this->logLocation->getLocation($log);
|
||||
|
||||
// Get the combined location asset geometry.
|
||||
$wkt = $this->getCombinedAssetGeometry($assets);
|
||||
|
||||
// If the WKT is not empty, set the log geometry.
|
||||
if (!empty($wkt)) {
|
||||
$this->logLocation->setGeometry($log, $wkt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a combined set of location asset geometries.
|
||||
*
|
||||
|
@ -177,4 +191,52 @@ class LogPresaveEventSubscriber implements EventSubscriberInterface {
|
|||
return $wkt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate asset caches when assets are moved.
|
||||
*
|
||||
* @param \Drupal\log\Entity\LogInterface $log
|
||||
* The Log entity.
|
||||
*/
|
||||
protected function invalidateAssetCacheOnMovement(LogInterface $log): void {
|
||||
|
||||
// Keep track if we need to invalidate the cache of referenced assets so
|
||||
// the computed 'location' and 'geometry' fields are updated.
|
||||
$update_asset_cache = FALSE;
|
||||
|
||||
// If the log is a 'done' movement log, invalidate the cache.
|
||||
if ($log->get('status')->value == 'done' && $log->get('is_movement')->value) {
|
||||
$update_asset_cache = TRUE;
|
||||
}
|
||||
|
||||
// If updating an existing 'done' movement log, invalidate the cache.
|
||||
// This catches any movement logs changing from done to pending.
|
||||
if (!empty($log->original) && $log->original->get('status')->value == 'done' && $log->original->get('is_movement')->value) {
|
||||
$update_asset_cache = TRUE;
|
||||
}
|
||||
|
||||
// If an update is not necessary, bail.
|
||||
if (!$update_asset_cache) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build a list of cache tags.
|
||||
// @todo Only invalidate cache if the movement log changes the asset's current location. This might be different for each asset.
|
||||
$tags = [];
|
||||
|
||||
// Include assets that were previously referenced.
|
||||
if (!empty($log->original)) {
|
||||
foreach ($log->original->get('asset')->referencedEntities() as $asset) {
|
||||
array_push($tags, ...$asset->getCacheTags());
|
||||
}
|
||||
}
|
||||
|
||||
// Include assets currently referenced by the log.
|
||||
foreach ($log->get('asset')->referencedEntities() as $asset) {
|
||||
array_push($tags, ...$asset->getCacheTags());
|
||||
}
|
||||
|
||||
// Invalidate the cache tags.
|
||||
$this->cacheTagsInvalidator->invalidateTags($tags);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,9 +2,14 @@
|
|||
|
||||
namespace Drupal\Tests\farm_entity\Functional;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\farm_location\Traits\WktTrait;
|
||||
use Drupal\Tests\farm\Functional\FarmBrowserTestBase;
|
||||
use Drupal\Tests\jsonapi\Functional\JsonApiRequestTestTrait;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
|
||||
/**
|
||||
* Tests for farmOS location logic.
|
||||
|
@ -15,6 +20,7 @@ class LocationTest extends FarmBrowserTestBase {
|
|||
|
||||
use StringTranslationTrait;
|
||||
use WktTrait;
|
||||
use JsonApiRequestTestTrait;
|
||||
|
||||
/**
|
||||
* Test user.
|
||||
|
@ -50,6 +56,7 @@ class LocationTest extends FarmBrowserTestBase {
|
|||
protected static $modules = [
|
||||
'farm_location',
|
||||
'farm_location_test',
|
||||
'farm_api',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -145,4 +152,104 @@ class LocationTest extends FarmBrowserTestBase {
|
|||
$this->assertSession()->fieldExists('intrinsic_geometry[0][value]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test retrieving asset geometry and location fields via API.
|
||||
*/
|
||||
public function testApiLocation() {
|
||||
|
||||
$location_uuid = $this->location->uuid();
|
||||
$geometry = $this->location->get('intrinsic_geometry')->value;
|
||||
|
||||
$this->assertApiAssetLocationEquals($location_uuid, $geometry);
|
||||
|
||||
// Test that removing the log movement flag removes the asset location.
|
||||
$this->log->is_movement = FALSE;
|
||||
$this->log->save();
|
||||
$this->assertApiAssetLocationEquals(NULL, NULL);
|
||||
|
||||
// Test that setting the log movement flag sets the asset location.
|
||||
$this->log->is_movement = TRUE;
|
||||
$this->log->save();
|
||||
$this->assertApiAssetLocationEquals($location_uuid, $geometry);
|
||||
|
||||
// Test that changing the log status to pending removes the asset location.
|
||||
$this->log->status = 'pending';
|
||||
$this->log->save();
|
||||
$this->assertApiAssetLocationEquals(NULL, NULL);
|
||||
|
||||
// Test that changing the log status to done sets the asset location.
|
||||
$this->log->status = 'done';
|
||||
$this->log->save();
|
||||
$this->assertApiAssetLocationEquals($location_uuid, $geometry);
|
||||
|
||||
// Test that removing the asset from the log removes the asset location.
|
||||
$this->log->asset = [];
|
||||
$this->log->save();
|
||||
$this->assertApiAssetLocationEquals(NULL, NULL);
|
||||
|
||||
// Test that adding the asset to the log sets the asset location.
|
||||
$this->log->asset = ['target_id' => $this->asset->id()];
|
||||
$this->log->save();
|
||||
$this->assertApiAssetLocationEquals($location_uuid, $geometry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to test asset location returned by the API.
|
||||
*
|
||||
* @param string|null $location_uuid
|
||||
* The expected location asset uuid.
|
||||
* @param string|null $geometry
|
||||
* The expected geometry.
|
||||
*/
|
||||
private function assertApiAssetLocationEquals($location_uuid, $geometry) {
|
||||
|
||||
// Fetch the asset from the API.
|
||||
$response = $this->requestApiEntity($this->asset);
|
||||
|
||||
// Test that the location field is included in the response.
|
||||
$this->assertArrayHasKey('data', $response['data']['relationships']['location']);
|
||||
|
||||
// If no uuid is provided, test that the asset has no location.
|
||||
if (empty($location_uuid)) {
|
||||
$this->assertEquals(0, count($response['data']['relationships']['location']['data']));
|
||||
}
|
||||
|
||||
// Ia a location_uuid is provided, test that the location matches.
|
||||
if (!empty($location_uuid)) {
|
||||
$this->assertEquals(1, count($response['data']['relationships']['location']['data']));
|
||||
$this->assertArrayHasKey('id', $response['data']['relationships']['location']['data'][0]);
|
||||
$this->assertEquals($location_uuid, $response['data']['relationships']['location']['data'][0]['id']);
|
||||
}
|
||||
|
||||
// Test that the geometry field is included in the response.
|
||||
$this->assertArrayHasKey('geometry', $response['data']['attributes']);
|
||||
|
||||
// If no geometry is provided, test that the asset has no geometry.
|
||||
if (empty($geometry)) {
|
||||
$this->assertEquals(NULL, $response['data']['attributes']['geometry']);
|
||||
}
|
||||
|
||||
// Ia a geometry is provided, test that the geometry matches.
|
||||
if (!empty($geometry)) {
|
||||
$this->assertArrayHasKey('value', $response['data']['attributes']['geometry']);
|
||||
$this->assertEquals($geometry, $response['data']['attributes']['geometry']['value']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to request an entity from the API.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity to request.
|
||||
*
|
||||
* @return array
|
||||
* The json-decoded response.
|
||||
*/
|
||||
private function requestApiEntity(EntityInterface $entity) {
|
||||
$request_options[RequestOptions::HEADERS]['Accept'] = 'application/vnd.api+json';
|
||||
$asset_uri = "base://api/{$entity->getEntityType()->id()}/{$entity->bundle()}/{$entity->uuid()}";
|
||||
$response = $this->request('GET', Url::fromUri($asset_uri), $request_options);
|
||||
return Json::decode((string) $response->getBody());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue