Open Data Catalog v
Service.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Drupal\metastore;
4 
5 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
6 use Drupal\common\EventDispatcherTrait;
14 use RootedData\RootedJsonData;
15 use Rs\Json\Merge\Patch;
16 use Symfony\Component\DependencyInjection\ContainerInterface;
17 
21 class Service implements ContainerInjectionInterface {
22  use EventDispatcherTrait;
23  use LoggerTrait;
24 
25  const EVENT_DATA_GET = 'dkan_metastore_data_get';
26  const EVENT_DATA_GET_ALL = 'dkan_metastore_data_get_all';
27 
33  private $schemaRetriever;
34 
40  private $storageFactory;
41 
47  private $storages;
48 
54  private $validMetadataFactory;
55 
61  public static function create(ContainerInterface $container) {
62  return new Service(
63  $container->get('dkan.metastore.schema_retriever'),
64  $container->get('dkan.metastore.storage'),
65  $container->get('dkan.metastore.valid_metadata')
66  );
67  }
68 
72  public function __construct(SchemaRetriever $schemaRetriever, DataFactory $factory, ValidMetadataFactory $validMetadataFactory) {
73  $this->schemaRetriever = $schemaRetriever;
74  $this->storageFactory = $factory;
75  $this->validMetadataFactory = $validMetadataFactory;
76  }
77 
81  public function getSchemas() {
82  $schemas = [];
83  foreach ($this->schemaRetriever->getAllIds() as $id) {
84  $schema = $this->schemaRetriever->retrieve($id);
85  $schemas[$id] = json_decode($schema);
86  }
87  return $schemas;
88  }
89 
93  public function getSchema($identifier) {
94  $schema = $this->schemaRetriever->retrieve($identifier);
95  $schema = json_decode($schema);
96 
97  return $schema;
98  }
99 
109  protected function getStorage(string $schema_id): MetastoreStorageInterface {
110  if (!isset($this->storages[$schema_id])) {
111  $this->storages[$schema_id] = $this->storageFactory->getInstance($schema_id);
112  }
113  return $this->storages[$schema_id];
114  }
115 
127  public function count(string $schema_id, bool $unpublished = FALSE): int {
128  return $this->getStorage($schema_id)->count($unpublished);
129  }
130 
148  public function getIdentifiers(string $schema_id, ?int $start = NULL, ?int $length = NULL, $unpublished = FALSE): array {
149  return $this->getStorage($schema_id)->retrieveIds($start, $length, $unpublished);
150  }
151 
167  public function getAll(string $schema_id, ?int $start = NULL, ?int $length = NULL, $unpublished = FALSE):array {
168  $jsonStringsArray = $this->getStorage($schema_id)->retrieveAll($start, $length, $unpublished);
169  $objects = array_filter($this->jsonStringsArrayToObjects($jsonStringsArray, $schema_id));
170 
171  return $this->dispatchEvent(self::EVENT_DATA_GET_ALL, $objects, function ($data) {
172  if (!is_array($data)) {
173  return FALSE;
174  }
175  if (count($data) == 0) {
176  return TRUE;
177  }
178  return reset($data) instanceof RootedJsonData;
179  });
180 
181  }
182 
196  private function jsonStringsArrayToObjects(array $jsonStringsArray, string $schema_id) {
197  return array_map(
198  function ($jsonString) use ($schema_id) {
199  try {
200  $data = $this->validMetadataFactory->get($jsonString, $schema_id);
201  return $this->dispatchEvent(self::EVENT_DATA_GET, $data, function ($data) {
202  return $data instanceof RootedJsonData;
203  });
204  }
205  catch (\Exception $e) {
206  $this->log('metastore', 'A JSON string failed validation.',
207  ['@schema_id' => $schema_id, '@json' => $jsonString]
208  );
209  return NULL;
210  }
211  }, $jsonStringsArray);
212  }
213 
225  public function isPublished(string $schema_id, string $identifier): bool {
226  return $this->getStorage($schema_id)->isPublished($identifier);
227  }
228 
240  public function get(string $schema_id, string $identifier): RootedJsonData {
241  $json_string = $this->getStorage($schema_id)->retrieve($identifier, TRUE);
242  $data = $this->validMetadataFactory->get($json_string, $schema_id);
243 
244  $data = $this->dispatchEvent(self::EVENT_DATA_GET, $data);
245  return $data;
246  }
247 
254  public function getValidMetadataFactory() {
255  return $this->validMetadataFactory;
256  }
257 
269  public function post($schema_id, RootedJsonData $data): string {
270  $identifier = NULL;
271 
272  // If resource already exists, return HTTP 409 Conflict and existing uri.
273  if (!empty($data->{'$.identifier'})) {
274  $identifier = $data->{'$.identifier'};
275  if ($this->objectExists($schema_id, $identifier)) {
276  throw new ExistingObjectException("{$schema_id}/{$identifier} already exists.");
277  }
278  }
279 
280  return $this->getStorage($schema_id)->store($data, $identifier);
281  }
282 
294  public function publish(string $schema_id, string $identifier): bool {
295  if ($this->objectExists($schema_id, $identifier)) {
296  return $this->getStorage($schema_id)->publish($identifier);
297  }
298 
299  throw new MissingObjectException("No data with the identifier {$identifier} was found.");
300  }
301 
313  public function archive(string $schema_id, string $identifier): bool {
314  if ($this->objectExists($schema_id, $identifier)) {
315  return $this->getStorage($schema_id)->archive($identifier);
316  }
317 
318  throw new MissingObjectException("No data with the identifier {$identifier} was found.");
319  }
320 
334  public function put($schema_id, $identifier, RootedJsonData $data): array {
335  if (!empty($data->{'$.identifier'}) && $data->{'$.identifier'} != $identifier) {
336  throw new CannotChangeUuidException("Identifier cannot be modified");
337  }
338  elseif ($this->objectExists($schema_id, $identifier) && $this->objectIsEquivalent($schema_id, $identifier, $data)) {
339  throw new UnmodifiedObjectException("No changes to {$schema_id} with identifier {$identifier}.");
340  }
341  else {
342  return $this->proceedWithPut($schema_id, $identifier, $data);
343  }
344  }
345 
359  private function proceedWithPut($schema_id, $identifier, RootedJsonData $data): array {
360  if ($this->objectExists($schema_id, $identifier)) {
361  $this->getStorage($schema_id)->store($data, $identifier);
362  return ['identifier' => $identifier, 'new' => FALSE];
363  }
364  else {
365  $this->getStorage($schema_id)->store($data);
366  return ['identifier' => $identifier, 'new' => TRUE];
367  }
368  }
369 
383  public function patch($schema_id, $identifier, $json_data) {
384  $storage = $this->getStorage($schema_id);
385  if ($this->objectExists($schema_id, $identifier)) {
386 
387  $json_data_original = $storage->retrieve($identifier);
388  if ($json_data_original) {
389  $patched = (new Patch())->apply(
390  json_decode($json_data_original),
391  json_decode($json_data)
392  );
393 
394  $new = $this->validMetadataFactory->get(json_encode($patched), $schema_id);
395  $storage->store($new, "{$identifier}");
396  return $identifier;
397  }
398 
399  }
400 
401  throw new MissingObjectException("No data with the identifier {$identifier} was found.");
402  }
403 
415  public function delete($schema_id, $identifier) {
416  $storage = $this->getStorage($schema_id);
417 
418  $storage->remove($identifier);
419 
420  return $identifier;
421  }
422 
429  public function getCatalog() {
430  $catalog = $this->getSchema('catalog');
431  $catalog->dataset = array_map(function ($object) {
432  $modified_object = $this->removeReferences($object);
433  return (object) $modified_object->get('$');
434  }, $this->getAll('dataset'));
435 
436  return $catalog;
437  }
438 
451  public function swapReferences(RootedJsonData $object): RootedJsonData {
452  $no_schema_object = $this->getValidMetadataFactory()->get("$object", NULL);
453  foreach ($no_schema_object->get('$') as $property => $value) {
454  if (substr_count($property, "%Ref:") > 0) {
455  $no_schema_object = $this->swapReference($property, $value, $no_schema_object);
456  }
457  }
458 
459  return self::removeReferences($no_schema_object, "%Ref");
460  }
461 
465  private function swapReference($property, $value, RootedJsonData $object): RootedJsonData {
466  $original = str_replace("%Ref:", "", $property);
467  if ($object->__isset("$.{$original}")) {
468  $object->set("$.{$original}", $value);
469  }
470  return $object;
471  }
472 
476  private function objectExists($schemaId, $identifier) {
477  try {
478  $this->getStorage($schemaId)->retrieve($identifier);
479  return TRUE;
480  }
481  catch (\Exception $e) {
482  return FALSE;
483  }
484  }
485 
503  private function objectIsEquivalent(string $schema_id, string $identifier, RootedJsonData $metadata) {
504  $existingMetadata = $this->getStorage($schema_id)->retrieve($identifier);
505  $existing = $this->getValidMetadataFactory()->get($existingMetadata, $schema_id);
506  $existing = self::removeReferences($existing);
507  return $metadata->get('$') == $existing->get('$');
508  }
509 
523  public static function removeReferences(RootedJsonData $object, $prefix = "%"): RootedJsonData {
524  $array = $object->get('$');
525 
526  foreach ($array as $property => $value) {
527  if (substr_count($property, $prefix) > 0) {
528  unset($array[$property]);
529  }
530  }
531 
532  if (!empty($array['distribution'])) {
533  $array['distribution'] = array_map(function ($dist) {
534  unset($dist['%Ref:downloadURL']);
535  return $dist;
536  }, $array['distribution']);
537  }
538 
539  $object->set('$', $array);
540  return $object;
541  }
542 
554  public static function metadataHash($data) {
555  if ($data instanceof RootedJsonData) {
556  $normalizedData = $data;
557  self::removeReferences($normalizedData);
558  }
559  elseif (is_object($data)) {
560  $normalizedData = new RootedJsonData(json_encode($data));
561  self::removeReferences($normalizedData);
562  }
563  elseif (is_string($data)) {
564  $normalizedData = $data;
565  }
566  else {
567  throw new \InvalidArgumentException("Invalid metadata argument.");
568  }
569 
570  return md5((string) $normalizedData);
571  }
572 
573 }
Drupal\common\LoggerTrait
trait LoggerTrait
Definition: LoggerTrait.php:11
Drupal\metastore\Service\getStorage
getStorage(string $schema_id)
Definition: Service.php:109
Drupal\metastore\Service\EVENT_DATA_GET
const EVENT_DATA_GET
Definition: Service.php:25
Drupal\metastore\ValidMetadataFactory
Definition: ValidMetadataFactory.php:13
Drupal\metastore
Drupal\metastore\Service\EVENT_DATA_GET_ALL
const EVENT_DATA_GET_ALL
Definition: Service.php:26
Drupal\metastore\Service\post
post($schema_id, RootedJsonData $data)
Definition: Service.php:269
Drupal\metastore\Service\publish
publish(string $schema_id, string $identifier)
Definition: Service.php:294
Drupal\metastore\Service
Definition: Service.php:21
Drupal\metastore\Service\getSchemas
getSchemas()
Definition: Service.php:81
Drupal\metastore\Service\getAll
getAll(string $schema_id, ?int $start=NULL, ?int $length=NULL, $unpublished=FALSE)
Definition: Service.php:167
Drupal\metastore\Service\metadataHash
static metadataHash($data)
Definition: Service.php:554
Drupal\metastore\Service\count
count(string $schema_id, bool $unpublished=FALSE)
Definition: Service.php:127
Drupal\metastore\Service\create
static create(ContainerInterface $container)
Definition: Service.php:61
Drupal\metastore\Storage\MetastoreStorageInterface
Definition: MetastoreStorageInterface.php:8
Drupal\metastore\Service\removeReferences
static removeReferences(RootedJsonData $object, $prefix="%")
Definition: Service.php:523
Drupal\metastore\Exception\CannotChangeUuidException
Definition: CannotChangeUuidException.php:10
Drupal\metastore\Service\swapReferences
swapReferences(RootedJsonData $object)
Definition: Service.php:451
Drupal\metastore\Storage\DataFactory
Definition: DataFactory.php:11
Drupal\metastore\Service\isPublished
isPublished(string $schema_id, string $identifier)
Definition: Service.php:225
Drupal\metastore\Service\patch
patch($schema_id, $identifier, $json_data)
Definition: Service.php:383
Drupal\metastore\Exception\MissingObjectException
Definition: MissingObjectException.php:10
Drupal\metastore\Service\getCatalog
getCatalog()
Definition: Service.php:429
Drupal\metastore\SchemaRetriever
Definition: SchemaRetriever.php:13
Drupal\metastore\Service\getIdentifiers
getIdentifiers(string $schema_id, ?int $start=NULL, ?int $length=NULL, $unpublished=FALSE)
Definition: Service.php:148
Drupal\metastore\Service\getValidMetadataFactory
getValidMetadataFactory()
Definition: Service.php:254
Drupal\metastore\Service\put
put($schema_id, $identifier, RootedJsonData $data)
Definition: Service.php:334
Drupal\metastore\Exception\UnmodifiedObjectException
Definition: UnmodifiedObjectException.php:10
Drupal\metastore\Service\archive
archive(string $schema_id, string $identifier)
Definition: Service.php:313
Drupal\metastore\Service\__construct
__construct(SchemaRetriever $schemaRetriever, DataFactory $factory, ValidMetadataFactory $validMetadataFactory)
Definition: Service.php:72
Drupal\metastore\Exception\ExistingObjectException
Definition: ExistingObjectException.php:10
Drupal\metastore\Service\getSchema
getSchema($identifier)
Definition: Service.php:93