Open Data Catalog v2.0.0
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  private 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 get($schema_id, $identifier): RootedJsonData {
226  $json_string = $this->getStorage($schema_id)->retrievePublished($identifier);
227  $data = $this->validMetadataFactory->get($json_string, $schema_id);
228 
229  $data = $this->dispatchEvent(self::EVENT_DATA_GET, $data);
230  return $data;
231  }
232 
246  public function getResources($schema_id, $identifier): array {
247  $json_string = $this->getStorage($schema_id)->retrieve($identifier);
248  $data = $this->validMetadataFactory->get($json_string, $schema_id);
249 
250  /* @todo decouple from POD. */
251  $resources = $data->{"$.distribution"};
252 
253  return $resources;
254  }
255 
262  public function getValidMetadataFactory() {
263  return $this->validMetadataFactory;
264  }
265 
277  public function post($schema_id, RootedJsonData $data): string {
278  $identifier = NULL;
279 
280  // If resource already exists, return HTTP 409 Conflict and existing uri.
281  if (!empty($data->{'$.identifier'})) {
282  $identifier = $data->{'$.identifier'};
283  if ($this->objectExists($schema_id, $identifier)) {
284  throw new ExistingObjectException("{$schema_id}/{$identifier} already exists.");
285  }
286  }
287 
288  return $this->getStorage($schema_id)->store($data, $identifier);
289  }
290 
302  public function publish(string $schema_id, string $identifier): bool {
303  if ($this->objectExists($schema_id, $identifier)) {
304  return $this->getStorage($schema_id)->publish($identifier);
305  }
306 
307  throw new MissingObjectException("No data with the identifier {$identifier} was found.");
308  }
309 
323  public function put($schema_id, $identifier, RootedJsonData $data): array {
324  if (!empty($data->{'$.identifier'}) && $data->{'$.identifier'} != $identifier) {
325  throw new CannotChangeUuidException("Identifier cannot be modified");
326  }
327  elseif ($this->objectExists($schema_id, $identifier) && $this->objectIsEquivalent($schema_id, $identifier, $data)) {
328  throw new UnmodifiedObjectException("No changes to {$schema_id} with identifier {$identifier}.");
329  }
330  else {
331  return $this->proceedWithPut($schema_id, $identifier, $data);
332  }
333  }
334 
348  private function proceedWithPut($schema_id, $identifier, RootedJsonData $data): array {
349  if ($this->objectExists($schema_id, $identifier)) {
350  $this->getStorage($schema_id)->store($data, $identifier);
351  return ['identifier' => $identifier, 'new' => FALSE];
352  }
353  else {
354  $this->getStorage($schema_id)->store($data);
355  return ['identifier' => $identifier, 'new' => TRUE];
356  }
357  }
358 
372  public function patch($schema_id, $identifier, $json_data) {
373  $storage = $this->getStorage($schema_id);
374  if ($this->objectExists($schema_id, $identifier)) {
375 
376  $json_data_original = $storage->retrieve($identifier);
377  if ($json_data_original) {
378  $patched = (new Patch())->apply(
379  json_decode($json_data_original),
380  json_decode($json_data)
381  );
382 
383  $new = $this->validMetadataFactory->get(json_encode($patched), $schema_id);
384  $storage->store($new, "{$identifier}");
385  return $identifier;
386  }
387 
388  }
389 
390  throw new MissingObjectException("No data with the identifier {$identifier} was found.");
391  }
392 
404  public function delete($schema_id, $identifier) {
405  $storage = $this->getStorage($schema_id);
406 
407  $storage->remove($identifier);
408 
409  return $identifier;
410  }
411 
418  public function getCatalog() {
419  $catalog = $this->getSchema('catalog');
420  $catalog->dataset = array_map(function ($object) {
421  $modified_object = $this->removeReferences($object);
422  return (object) $modified_object->get('$');
423  }, $this->getAll('dataset'));
424 
425  return $catalog;
426  }
427 
440  public function swapReferences(RootedJsonData $object): RootedJsonData {
441  $no_schema_object = $this->getValidMetadataFactory()->get("$object", NULL);
442  foreach ($no_schema_object->get('$') as $property => $value) {
443  if (substr_count($property, "%Ref:") > 0) {
444  $no_schema_object = $this->swapReference($property, $value, $no_schema_object);
445  }
446  }
447 
448  return self::removeReferences($no_schema_object, "%Ref");
449  }
450 
454  private function swapReference($property, $value, RootedJsonData $object): RootedJsonData {
455  $original = str_replace("%Ref:", "", $property);
456  if ($object->__isset("$.{$original}")) {
457  $object->set("$.{$original}", $value);
458  }
459  return $object;
460  }
461 
465  private function objectExists($schemaId, $identifier) {
466  try {
467  $this->getStorage($schemaId)->retrieve($identifier);
468  return TRUE;
469  }
470  catch (\Exception $e) {
471  return FALSE;
472  }
473  }
474 
492  private function objectIsEquivalent(string $schema_id, string $identifier, RootedJsonData $metadata) {
493  $existingMetadata = $this->getStorage($schema_id)->retrieve($identifier);
494  $existing = $this->getValidMetadataFactory()->get($existingMetadata, $schema_id);
495  $existing = self::removeReferences($existing);
496  return $metadata->get('$') == $existing->get('$');
497  }
498 
512  public static function removeReferences(RootedJsonData $object, $prefix = "%"): RootedJsonData {
513  $array = $object->get('$');
514 
515  foreach ($array as $property => $value) {
516  if (substr_count($property, $prefix) > 0) {
517  unset($array[$property]);
518  }
519  }
520 
521  if (isset($array['distribution'][0]['%Ref:downloadURL'])) {
522  unset($array['distribution'][0]['%Ref:downloadURL']);
523  }
524 
525  $object->set('$', $array);
526  return $object;
527  }
528 
540  public static function metadataHash($data) {
541  if ($data instanceof RootedJsonData) {
542  $normalizedData = $data;
543  self::removeReferences($normalizedData);
544  }
545  elseif (is_object($data)) {
546  $normalizedData = new RootedJsonData(json_encode($data));
547  self::removeReferences($normalizedData);
548  }
549  elseif (is_string($data)) {
550  $normalizedData = $data;
551  }
552  else {
553  throw new \InvalidArgumentException("Invalid metadata argument.");
554  }
555 
556  return md5((string) $normalizedData);
557  }
558 
559 }
Drupal\common\LoggerTrait
trait LoggerTrait
Definition: LoggerTrait.php:11
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:277
Drupal\metastore\Service\publish
publish(string $schema_id, string $identifier)
Definition: Service.php:302
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:540
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:512
Drupal\metastore\Exception\CannotChangeUuidException
Definition: CannotChangeUuidException.php:10
Drupal\metastore\Service\swapReferences
swapReferences(RootedJsonData $object)
Definition: Service.php:440
Drupal\metastore\Storage\DataFactory
Definition: DataFactory.php:11
Drupal\metastore\Service\patch
patch($schema_id, $identifier, $json_data)
Definition: Service.php:372
Drupal\metastore\Exception\MissingObjectException
Definition: MissingObjectException.php:10
Drupal\metastore\Service\getResources
getResources($schema_id, $identifier)
Definition: Service.php:246
Drupal\metastore\Service\getCatalog
getCatalog()
Definition: Service.php:418
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:262
Drupal\metastore\Service\put
put($schema_id, $identifier, RootedJsonData $data)
Definition: Service.php:323
Drupal\metastore\Exception\UnmodifiedObjectException
Definition: UnmodifiedObjectException.php:10
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