Skip to content

Commit

Permalink
Data discovery service (#3765)
Browse files Browse the repository at this point in the history
  • Loading branch information
dafeder committed Mar 14, 2022
1 parent 827bb91 commit 3f28b46
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 0 deletions.
5 changes: 5 additions & 0 deletions modules/metastore/metastore.services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,8 @@ services:
- '@dkan.metastore.metastore_item_factory'
- '@cache_tags.invalidator'
- '@module_handler'

dkan.metastore.data_dictionary_discovery:
class: \Drupal\metastore\DataDictionary\DataDictionaryDiscovery
arguments:
- '@config.factory'
56 changes: 56 additions & 0 deletions modules/metastore/src/DataDictionary/DataDictionaryDiscovery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace Drupal\metastore\DataDictionary;

use Drupal\Core\Config\ConfigFactoryInterface;

/**
* Data dictionary service.
*
* Find the correct data dictionary for a dataset or distribution.
*/
class DataDictionaryDiscovery implements DataDictionaryDiscoveryInterface {

/**
* Constructor.
*/
public function __construct(ConfigFactoryInterface $configFactory) {
$this->config = $configFactory->get('metastore.settings');
}

/**
* {@inheritdoc}
*/
public function dictionaryIdFromResource(string $resourceId, ?int $resourceIdVersion = NULL): ?string {
$mode = $this->getDataDictionaryMode();
// For now, we only support sitewide!
switch ($mode) {
case self::MODE_NONE:
return NULL;

case self::MODE_SITEWIDE:
return $this->getSitewideDictionaryId();

default:
throw new \OutOfRangeException("Unsupported data dictionary mode " . (string) $mode);
}
}

/**
* {@inheritdoc}
*/
public function getSitewideDictionaryId(): string {
if ($identifier = $this->config->get('data_dictionary_sitewide')) {
return $identifier;
}
throw new \OutOfBoundsException("Attempted to retrieve a sitewide data dictionary, but none was set.");
}

/**
* {@inheritdoc}
*/
public function getDataDictionaryMode(): int {
return $this->config->get('data_dictionary_mode') ?? self::MODE_NONE;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace Drupal\metastore\DataDictionary;

/**
* Provides interface for data dictionary discovery service.
*/
interface DataDictionaryDiscoveryInterface {

const MODE_NONE = 0;
const MODE_SITEWIDE = 1;
const MODE_COLLECTION = 2;
const MODE_GENERATE = 3;

/**
* Return the item ID for the appropriate data dictionary for a resource.
*
* @param string $resourceId
* DKAN datastore resource identifier.
* @param int|null $resourceIdVersion
* DKAN datastore resource version ID.
*
* @return string|null
* The data dictionary identifier or NULL if none exists.
*/
public function dictionaryIdFromResource(string $resourceId, ?int $resourceIdVersion = NULL): ?string;

/**
* Get the current data dictionary "mode" from DKAN config.
*
* Return values should represent modes like "single sitewide dictionary" or
* "distribution-specific dictionaries." The setting will have implications
* for behaviors in both the metastore and datastore sites of data dictionary
* functionality.
*
* @return int
* Data dictionary mode. Returns one of the MODE_* constants.
*/
public function getDataDictionaryMode(): int;

/**
* If a single sitewide data dictionary has been defined, return its ID.
*
* @return string
* Data dictionary identifier.
*/
public function getSitewideDictionaryId(): string;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

namespace Drupal\Tests\metastore\Unit\DataDictionary;

use Drupal\Core\Config\ConfigFactory;
use Drupal\Core\Config\ImmutableConfig;
use Drupal\metastore\DataDictionary\DataDictionaryDiscovery as Discovery;
use MockChain\Chain;
use MockChain\Options;
use PHPUnit\Framework\TestCase;

class DataDictionaryDiscoveryTest extends TestCase {


// If mode is set to "none", we should get NULL no matter what.
public function testModeNone() {
$configFactoryMock = $this->getConfigFactoryMock(Discovery::MODE_NONE, 'abc-123');
$discovery = new Discovery($configFactoryMock);
$id = $discovery->dictionaryIdFromResource('resource1');
$this->assertNull($id);
}

// If mode is sitewide, and we have a sitewide dictionary ID set, it should be
// returned, no matter what resource we pass to the method.
public function testSitewideId() {
$configFactoryMock = $this->getConfigFactoryMock(Discovery::MODE_SITEWIDE, 'abc-123');
$discovery = new Discovery($configFactoryMock);
$id = $discovery->dictionaryIdFromResource('resource1');
$this->assertEquals('abc-123', $id);
$idVersion = $discovery->dictionaryIdFromResource('resource1', '2352643');
$this->assertEquals('abc-123', $idVersion);
}

// If mode is sitewide but sitewide ID unset, we should get an exception.
public function testSitewideIdUnset() {
// Need to use 0 because MockChain\Options doesn't support NULL returns.
$configFactoryMock = $this->getConfigFactoryMock(Discovery::MODE_SITEWIDE, 0);
$discovery = new Discovery($configFactoryMock);

$this->expectException(\OutOfBoundsException::class);
$discovery->dictionaryIdFromResource('resource1');
}

// If mode is set to "collection", at the moment we should throw an exception
// because this is not yet supported.
public function testModeCollection() {
$configFactoryMock = $this->getConfigFactoryMock(Discovery::MODE_COLLECTION, 'abc-123');
$discovery = new Discovery($configFactoryMock);

$this->expectException(\OutOfRangeException::class);
$discovery->dictionaryIdFromResource('resource1');
}

// If mode is set to "generate", at the moment we should throw an exception
// because this is not yet supported.
public function testModeGenerate() {
$configFactoryMock = $this->getConfigFactoryMock(Discovery::MODE_GENERATE, 'abc-123');
$discovery = new Discovery($configFactoryMock);

$this->expectException(\OutOfRangeException::class);
$discovery->dictionaryIdFromResource('resource1');
}

// Build mock config service, based on arguments for mode and sitewide ID.
private function getConfigFactoryMock($mode, $sitewideId) {
$options = (new Options())
->add('data_dictionary_mode', $mode)
->add('data_dictionary_sitewide', $sitewideId)
->index(0);

return (new Chain($this))
->add(ConfigFactory::class, 'get', ImmutableConfig::class)
->add(ImmutableConfig::class, 'get', $options)
->getMock();
}
}

0 comments on commit 3f28b46

Please sign in to comment.