Skip to content

Commit

Permalink
Add an asset.logs service for retrieving logs that reference an asset #…
Browse files Browse the repository at this point in the history
  • Loading branch information
mstenta committed Sep 19, 2024
1 parent f61df68 commit b956a19
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [Add an "Export Quantity CSV" bulk action to Log Views](https://github.com/farmOS/farmOS/pull/861)
- [Allow modules to alter dashboard panes #868](https://github.com/farmOS/farmOS/pull/868)
- [Add geometry/location fields to CSV importers #815](https://github.com/farmOS/farmOS/pull/815)
- [Add an asset.logs service for retrieving logs that reference an asset #850](https://github.com/farmOS/farmOS/pull/850)

### Changed

Expand Down
26 changes: 26 additions & 0 deletions docs/development/module/services.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,32 @@ farmOS provides some [services](https://symfony.com/doc/current/service_containe
that encapsulate common logic like querying logs and getting an asset's current
location. Some of these services are documented here.

## Asset logs service

**Service name**: `asset.logs`

The asset logs service provides methods for retrieving logs that reference
assets.

**Methods**:

`getLogs($asset, $log_type = NULL, $access_check = TRUE)` - Load a list of logs
that reference an asset, optionally filtered by log type. Access checking is
performed by default but can be optionally disabled. Returns a list of log
entities.

`getFirstLog($asset, $log_type = NULL, $access_check = TRUE)` - Load the first
log that references an asset, optionally filtered by log type. Access checking
is performed by default but can be optionally disabled. Returns a log entity, or
`NULL` if no logs were found.

**Example usage**:

```php
// Get all observation logs that reference an asset.
$observation_logs = \Drupal::service('asset.logs')->getLogs($asset, 'observation');
```

## Asset location service

**Service name**: `asset.location`
Expand Down
4 changes: 4 additions & 0 deletions modules/core/log/farm_log.services.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
services:
asset.logs:
class: Drupal\farm_log\AssetLogs
arguments:
[ '@entity_type.manager', '@farm.log_query' ]
farm.log_query:
class: Drupal\farm_log\LogQueryFactory
arguments:
Expand Down
93 changes: 93 additions & 0 deletions modules/core/log/src/AssetLogs.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

namespace Drupal\farm_log;

use Drupal\asset\Entity\AssetInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;

/**
* Service for loading logs that reference assets.
*/
class AssetLogs implements AssetLogsInterface {

/**
* Entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected EntityTypeManagerInterface $entityTypeManager;

/**
* Log query factory.
*
* @var \Drupal\farm_log\LogQueryFactoryInterface
*/
protected LogQueryFactoryInterface $logQueryFactory;

/**
* Class constructor.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* Entity type manager.
* @param \Drupal\farm_log\LogQueryFactoryInterface $log_query_factory
* Log query factory.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, LogQueryFactoryInterface $log_query_factory) {
$this->entityTypeManager = $entity_type_manager;
$this->logQueryFactory = $log_query_factory;
}

/**
* {@inheritdoc}
*/
public function getLogs(AssetInterface $asset, string $log_type = NULL, bool $access_check = TRUE): array {
$log_ids = $this->query($asset, $log_type, $access_check)->execute();
if (empty($log_ids)) {
return [];
}
return $this->entityTypeManager->getStorage('log')->loadMultiple($log_ids);
}

/**
* {@inheritdoc}
*/
public function getFirstLog(AssetInterface $asset, string $log_type = NULL, bool $access_check = TRUE) {
$log_ids = $this->query($asset, $log_type, $access_check, 1)->execute();
if (empty($log_ids)) {
return NULL;
}
return $this->entityTypeManager->getStorage('log')->load(reset($log_ids));
}

/**
* Build a log query.
*
* @param \Drupal\asset\Entity\AssetInterface $asset
* The asset entity.
* @param string|null $log_type
* Optionally filter by log type.
* @param bool $access_check
* Whether to check log entity access.
* @param int|null $limit
* The number of logs to return.
*
* @return \Drupal\Core\Entity\Query\QueryInterface
* A query object.
*/
protected function query(AssetInterface $asset, string $log_type = NULL, bool $access_check = TRUE, int $limit = NULL) {
$options = [
'asset' => $asset,
'direction' => 'ASC',
];
if (!empty($limit)) {
$options['limit'] = $limit;
}
$query = $this->logQueryFactory->getQuery($options);
if (!empty($log_type)) {
$query->condition('type', $log_type);
}
$query->accessCheck($access_check);
return $query;
}

}
42 changes: 42 additions & 0 deletions modules/core/log/src/AssetLogsInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace Drupal\farm_log;

use Drupal\asset\Entity\AssetInterface;

/**
* The interface for asset logs service.
*/
interface AssetLogsInterface {

/**
* Get all logs for an asset.
*
* @param \Drupal\asset\Entity\AssetInterface $asset
* The asset entity.
* @param string|null $log_type
* Optionally filter by log type.
* @param bool $access_check
* Whether to check log entity access (defaults to TRUE).
*
* @return \Drupal\log\Entity\LogInterface[]
* Returns an array of Log entities.
*/
public function getLogs(AssetInterface $asset, string $log_type = NULL, bool $access_check = TRUE): array;

/**
* Get the first log of an asset.
*
* @param \Drupal\asset\Entity\AssetInterface $asset
* The asset entity.
* @param string|null $log_type
* Optionally filter by log type.
* @param bool $access_check
* Whether to check log entity access.
*
* @return \Drupal\log\Entity\LogInterface|null
* Returns a log entity or NULL if no logs were found.
*/
public function getFirstLog(AssetInterface $asset, string $log_type = NULL, bool $access_check = TRUE);

}
48 changes: 48 additions & 0 deletions modules/core/log/tests/src/Kernel/LogTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ class LogTest extends KernelTestBase {
*/
protected $logQueryFactory;

/**
* The asset logs service.
*
* @var \Drupal\farm_log\AssetLogsInterface
*/
protected $assetLogs;

/**
* {@inheritdoc}
*/
Expand All @@ -38,6 +45,7 @@ class LogTest extends KernelTestBase {
protected function setUp(): void {
parent::setUp();
$this->logQueryFactory = \Drupal::service('farm.log_query');
$this->assetLogs = \Drupal::service('asset.logs');
$this->installEntitySchema('asset');
$this->installEntitySchema('log');
$this->installEntitySchema('user');
Expand Down Expand Up @@ -123,6 +131,46 @@ public function testLogQueryFactory() {
$this->assertEquals(1, count($log_ids), 'Log query results can be limited.');
}

/**
* Test asset.logs service.
*/
public function testAssetLogsService() {

// Get asset and log storage.
$asset_storage = \Drupal::service('entity_type.manager')->getStorage('asset');
$log_storage = \Drupal::service('entity_type.manager')->getStorage('log');

// Create one asset and two logs of different types that reference it.
$asset = $asset_storage->create(['type' => 'test']);
$asset->save();
$timestamp = time();
$foo_log = $log_storage->create([
'timestamp' => $timestamp + 1,
'type' => 'foo',
'asset' => [$asset],
]);
$foo_log->save();
$bar_log = $log_storage->create([
'timestamp' => $timestamp,
'type' => 'bar',
'asset' => [$asset],
]);
$bar_log->save();

// Test that the asset.logs service returns both logs.
$logs = $this->assetLogs->getLogs($asset);
$this->assertCount(2, $logs);

// Test that logs can be filtered by type.
$logs = $this->assetLogs->getLogs($asset, 'bar');
$this->assertCount(1, $logs);
$this->assertEquals($bar_log->id(), reset($logs)->id());

// Test that we can get the first log.
$first_log = $this->assetLogs->getFirstLog($asset);
$this->assertEquals($bar_log->id(), $first_log->id());
}

/**
* Test log status workflow.
*/
Expand Down

0 comments on commit b956a19

Please sign in to comment.