Chris@18
|
1 <?php
|
Chris@18
|
2
|
Chris@18
|
3 namespace Drupal\jsonapi\Revisions;
|
Chris@18
|
4
|
Chris@18
|
5 use Drupal\Core\Cache\CacheableMetadata;
|
Chris@18
|
6 use Drupal\Core\Entity\EntityInterface;
|
Chris@18
|
7 use Drupal\Core\Http\Exception\CacheableBadRequestHttpException;
|
Chris@18
|
8 use Drupal\Core\Http\Exception\CacheableNotFoundHttpException;
|
Chris@18
|
9
|
Chris@18
|
10 /**
|
Chris@18
|
11 * Provides a version negotiator manager.
|
Chris@18
|
12 *
|
Chris@18
|
13 * @internal JSON:API maintains no PHP API since its API is the HTTP API. This
|
Chris@18
|
14 * class may change at any time and this will break any dependencies on it.
|
Chris@18
|
15 *
|
Chris@18
|
16 * @see https://www.drupal.org/project/jsonapi/issues/3032787
|
Chris@18
|
17 * @see jsonapi.api.php
|
Chris@18
|
18 *
|
Chris@18
|
19 * @see \Drupal\jsonapi\Revisions\VersionNegotiatorInterface
|
Chris@18
|
20 */
|
Chris@18
|
21 class VersionNegotiator {
|
Chris@18
|
22
|
Chris@18
|
23 /**
|
Chris@18
|
24 * The separator between the version negotiator name and the version argument.
|
Chris@18
|
25 *
|
Chris@18
|
26 * @var string
|
Chris@18
|
27 */
|
Chris@18
|
28 const SEPARATOR = ':';
|
Chris@18
|
29
|
Chris@18
|
30 /**
|
Chris@18
|
31 * An array of named version negotiators.
|
Chris@18
|
32 *
|
Chris@18
|
33 * @var \Drupal\jsonapi\Revisions\VersionNegotiatorInterface[]
|
Chris@18
|
34 */
|
Chris@18
|
35 protected $negotiators = [];
|
Chris@18
|
36
|
Chris@18
|
37 /**
|
Chris@18
|
38 * Adds a version negotiator.
|
Chris@18
|
39 *
|
Chris@18
|
40 * @param \Drupal\jsonapi\Revisions\VersionNegotiatorInterface $version_negotiator
|
Chris@18
|
41 * The version negotiator.
|
Chris@18
|
42 * @param string $negotiator_name
|
Chris@18
|
43 * The name of the negotiation strategy used by the version negotiator.
|
Chris@18
|
44 */
|
Chris@18
|
45 public function addVersionNegotiator(VersionNegotiatorInterface $version_negotiator, $negotiator_name) {
|
Chris@18
|
46 assert(strpos(get_class($version_negotiator), 'Drupal\\jsonapi\\') === 0, 'Version negotiators are not a public API.');
|
Chris@18
|
47 $this->negotiators[$negotiator_name] = $version_negotiator;
|
Chris@18
|
48 }
|
Chris@18
|
49
|
Chris@18
|
50 /**
|
Chris@18
|
51 * Gets a negotiated entity revision.
|
Chris@18
|
52 *
|
Chris@18
|
53 * @param \Drupal\Core\Entity\EntityInterface $entity
|
Chris@18
|
54 * The entity.
|
Chris@18
|
55 * @param string $resource_version_identifier
|
Chris@18
|
56 * A value used to derive a revision for the given entity.
|
Chris@18
|
57 *
|
Chris@18
|
58 * @return \Drupal\Core\Entity\EntityInterface
|
Chris@18
|
59 * The loaded revision.
|
Chris@18
|
60 *
|
Chris@18
|
61 * @throws \Drupal\Core\Http\Exception\CacheableNotFoundHttpException
|
Chris@18
|
62 * When the revision does not exist.
|
Chris@18
|
63 * @throws \Drupal\Core\Http\Exception\CacheableBadRequestHttpException
|
Chris@18
|
64 * When the revision ID cannot be negotiated.
|
Chris@18
|
65 */
|
Chris@18
|
66 public function getRevision(EntityInterface $entity, $resource_version_identifier) {
|
Chris@18
|
67 try {
|
Chris@18
|
68 list($version_negotiator_name, $version_argument) = explode(VersionNegotiator::SEPARATOR, $resource_version_identifier, 2);
|
Chris@18
|
69 if (!isset($this->negotiators[$version_negotiator_name])) {
|
Chris@18
|
70 static::throwBadRequestHttpException($resource_version_identifier);
|
Chris@18
|
71 }
|
Chris@18
|
72 return $this->negotiators[$version_negotiator_name]->getRevision($entity, $version_argument);
|
Chris@18
|
73 }
|
Chris@18
|
74 catch (VersionNotFoundException $exception) {
|
Chris@18
|
75 static::throwNotFoundHttpException($entity, $resource_version_identifier);
|
Chris@18
|
76 }
|
Chris@18
|
77 catch (InvalidVersionIdentifierException $exception) {
|
Chris@18
|
78 static::throwBadRequestHttpException($resource_version_identifier);
|
Chris@18
|
79 }
|
Chris@18
|
80 }
|
Chris@18
|
81
|
Chris@18
|
82 /**
|
Chris@18
|
83 * Throws a cacheable error exception.
|
Chris@18
|
84 *
|
Chris@18
|
85 * @param \Drupal\Core\Entity\EntityInterface $entity
|
Chris@18
|
86 * The entity for which a revision was requested.
|
Chris@18
|
87 * @param string $resource_version_identifier
|
Chris@18
|
88 * The user input for the revision negotiation.
|
Chris@18
|
89 *
|
Chris@18
|
90 * @throws \Drupal\Core\Http\Exception\CacheableNotFoundHttpException
|
Chris@18
|
91 */
|
Chris@18
|
92 protected static function throwNotFoundHttpException(EntityInterface $entity, $resource_version_identifier) {
|
Chris@18
|
93 $cacheability = CacheableMetadata::createFromObject($entity)->addCacheContexts(['url.path', 'url.query_args:' . ResourceVersionRouteEnhancer::RESOURCE_VERSION_QUERY_PARAMETER]);
|
Chris@18
|
94 $reason = sprintf('The requested version, identified by `%s`, could not be found.', $resource_version_identifier);
|
Chris@18
|
95 throw new CacheableNotFoundHttpException($cacheability, $reason);
|
Chris@18
|
96 }
|
Chris@18
|
97
|
Chris@18
|
98 /**
|
Chris@18
|
99 * Throws a cacheable error exception.
|
Chris@18
|
100 *
|
Chris@18
|
101 * @param string $resource_version_identifier
|
Chris@18
|
102 * The user input for the revision negotiation.
|
Chris@18
|
103 *
|
Chris@18
|
104 * @throws \Drupal\Core\Http\Exception\CacheableBadRequestHttpException
|
Chris@18
|
105 */
|
Chris@18
|
106 protected static function throwBadRequestHttpException($resource_version_identifier) {
|
Chris@18
|
107 $cacheability = (new CacheableMetadata())->addCacheContexts(['url.query_args:' . ResourceVersionRouteEnhancer::RESOURCE_VERSION_QUERY_PARAMETER]);
|
Chris@18
|
108 $message = sprintf('An invalid resource version identifier, `%s`, was provided.', $resource_version_identifier);
|
Chris@18
|
109 throw new CacheableBadRequestHttpException($cacheability, $message);
|
Chris@18
|
110 }
|
Chris@18
|
111
|
Chris@18
|
112 }
|