1 | <?php |
||
30 | class EventStatusLinkResponseListener |
||
31 | { |
||
32 | |||
33 | /** |
||
34 | * @var ProducerInterface Producer for publishing messages. |
||
35 | */ |
||
36 | private $rabbitMqProducer = null; |
||
37 | |||
38 | /** |
||
39 | * @var RouterInterface Router to generate resource URLs |
||
40 | */ |
||
41 | private $router = null; |
||
42 | |||
43 | /** |
||
44 | * @var Request request |
||
45 | */ |
||
46 | private $request; |
||
47 | |||
48 | /** |
||
49 | * @var QueueEvent queue event document |
||
50 | */ |
||
51 | private $queueEventDocument; |
||
52 | |||
53 | /** |
||
54 | * @var array |
||
55 | */ |
||
56 | private $eventMap; |
||
57 | |||
58 | /** |
||
59 | * @var ExtReferenceConverter ExtReferenceConverter |
||
60 | */ |
||
61 | private $extRefConverter; |
||
62 | |||
63 | /** |
||
64 | * @var string classname of the EventWorker document |
||
65 | */ |
||
66 | private $eventWorkerClassname; |
||
67 | |||
68 | /** |
||
69 | * @var string classname of the EventStatus document |
||
70 | */ |
||
71 | private $eventStatusClassname; |
||
72 | |||
73 | /** |
||
74 | * @var string classname of the EventStatusStatus document |
||
75 | */ |
||
76 | private $eventStatusStatusClassname; |
||
77 | |||
78 | /** |
||
79 | * @var string classname of the EventStatusEventResource document |
||
80 | */ |
||
81 | private $eventStatusEventResourceClassname; |
||
82 | |||
83 | /** |
||
84 | * @var string route name of the /event/status route |
||
85 | */ |
||
86 | private $eventStatusRouteName; |
||
87 | |||
88 | /** |
||
89 | * @var DocumentManager Document manager |
||
90 | */ |
||
91 | private $documentManager; |
||
92 | |||
93 | /** |
||
94 | * @var SecurityUtils |
||
95 | */ |
||
96 | protected $securityUtils; |
||
97 | |||
98 | /** |
||
99 | * @var Uri |
||
100 | */ |
||
101 | protected $workerRelativeUrl; |
||
102 | |||
103 | /** |
||
104 | * @param ProducerInterface $rabbitMqProducer RabbitMQ dependency |
||
105 | * @param RouterInterface $router Router dependency |
||
106 | * @param RequestStack $requestStack Request stack |
||
107 | * @param DocumentManager $documentManager Doctrine document manager |
||
108 | * @param ExtReferenceConverter $extRefConverter instance of the ExtReferenceConverter service |
||
109 | * @param QueueEvent $queueEventDocument queueevent document |
||
110 | * @param array $eventMap eventmap |
||
111 | * @param string $eventWorkerClassname classname of the EventWorker document |
||
112 | * @param string $eventStatusClassname classname of the EventStatus document |
||
113 | * @param string $eventStatusStatusClassname classname of the EventStatusStatus document |
||
114 | * @param string $eventStatusEventResourceClassname classname of the E*S*E*Resource document |
||
115 | * @param string $eventStatusRouteName name of the route to EventStatus |
||
116 | * @param SecurityUtils $securityUtils Security utils service |
||
117 | * @param string $workerRelativeUrl backend url relative from the workers |
||
118 | */ |
||
119 | 2 | public function __construct( |
|
120 | ProducerInterface $rabbitMqProducer, |
||
121 | RouterInterface $router, |
||
122 | RequestStack $requestStack, |
||
123 | DocumentManager $documentManager, |
||
124 | ExtReferenceConverter $extRefConverter, |
||
125 | QueueEvent $queueEventDocument, |
||
126 | array $eventMap, |
||
127 | $eventWorkerClassname, |
||
128 | $eventStatusClassname, |
||
129 | $eventStatusStatusClassname, |
||
130 | $eventStatusEventResourceClassname, |
||
131 | $eventStatusRouteName, |
||
132 | SecurityUtils $securityUtils, |
||
133 | $workerRelativeUrl |
||
134 | ) { |
||
135 | 2 | $this->rabbitMqProducer = $rabbitMqProducer; |
|
136 | 2 | $this->router = $router; |
|
137 | 2 | $this->request = $requestStack->getCurrentRequest(); |
|
138 | 2 | $this->documentManager = $documentManager; |
|
139 | 2 | $this->extRefConverter = $extRefConverter; |
|
140 | 2 | $this->queueEventDocument = $queueEventDocument; |
|
141 | 2 | $this->eventMap = $eventMap; |
|
142 | 2 | $this->eventWorkerClassname = $eventWorkerClassname; |
|
143 | 2 | $this->eventStatusClassname = $eventStatusClassname; |
|
144 | 2 | $this->eventStatusStatusClassname = $eventStatusStatusClassname; |
|
145 | 2 | $this->eventStatusEventResourceClassname = $eventStatusEventResourceClassname; |
|
146 | 2 | $this->eventStatusRouteName = $eventStatusRouteName; |
|
147 | 2 | $this->securityUtils = $securityUtils; |
|
148 | 2 | if (!is_null($workerRelativeUrl)) { |
|
149 | $this->workerRelativeUrl = new Uri($workerRelativeUrl); |
||
150 | } |
||
151 | 2 | } |
|
152 | |||
153 | /** |
||
154 | * add a rel=eventStatus Link header to the response if necessary |
||
155 | * |
||
156 | * @param FilterResponseEvent $event response listener event |
||
157 | * |
||
158 | * @return void |
||
159 | */ |
||
160 | 2 | public function onKernelResponse(FilterResponseEvent $event) |
|
161 | { |
||
162 | /** |
||
163 | * @var Response $response |
||
164 | */ |
||
165 | 2 | $response = $event->getResponse(); |
|
166 | |||
167 | // exit if not master request, uninteresting method or an error occurred |
||
168 | 2 | if (!$event->isMasterRequest() || $this->isNotConcerningRequest() || !$response->isSuccessful()) { |
|
169 | return; |
||
170 | } |
||
171 | |||
172 | // we can always safely call this, it doesn't need much resources. |
||
173 | // only if we have subscribers, it will create more load as it persists an EventStatus |
||
174 | 2 | $queueEvent = $this->createQueueEventObject(); |
|
175 | |||
176 | 2 | if (!empty($queueEvent->getStatusurl()) && !empty($queueEvent->getEvent())) { |
|
177 | 2 | $linkHeader = LinkHeader::fromResponse($response); |
|
178 | 2 | $linkHeader->add( |
|
179 | 2 | new LinkHeaderItem( |
|
180 | 2 | $queueEvent->getStatusurl(), |
|
181 | 2 | array('rel' => 'eventStatus') |
|
182 | 1 | ) |
|
183 | 1 | ); |
|
184 | |||
185 | 2 | $response->headers->set( |
|
186 | 2 | 'Link', |
|
187 | 1 | (string) $linkHeader |
|
188 | 1 | ); |
|
189 | 1 | } |
|
190 | |||
191 | // let's send it to the queue(s) if appropriate |
||
192 | 2 | if (!empty($queueEvent->getEvent())) { |
|
193 | 2 | $queuesForEvent = $this->getSubscribedWorkerIds($queueEvent); |
|
194 | |||
195 | // if needed and activated, change urls relative to workers |
||
196 | 2 | if (!empty($queuesForEvent) && $this->workerRelativeUrl instanceof Uri) { |
|
197 | $queueEvent = $this->getWorkerQueueEvent($queueEvent); |
||
198 | } |
||
199 | |||
200 | 2 | foreach ($queuesForEvent as $queueForEvent) { |
|
201 | // declare the Queue for the Event if its not there already declared |
||
202 | 2 | $this->rabbitMqProducer->getChannel()->queue_declare($queueForEvent, false, true, false, false); |
|
|
|||
203 | 2 | $this->rabbitMqProducer->publish(json_encode($queueEvent), $queueForEvent); |
|
204 | 1 | } |
|
205 | 1 | } |
|
206 | 2 | } |
|
207 | |||
208 | /** |
||
209 | * we only want to do something if we have a mapped event.. |
||
210 | * |
||
211 | * @return boolean true if it should not concern us, false otherwise |
||
212 | */ |
||
213 | 2 | private function isNotConcerningRequest() |
|
217 | |||
218 | /** |
||
219 | * Creates the structured object that will be sent to the queue (eventually..) |
||
220 | * |
||
221 | * @return QueueEvent event |
||
222 | */ |
||
223 | 2 | private function createQueueEventObject() |
|
233 | |||
234 | /** |
||
235 | * compose our routingKey. this will have the form of 'document.[bundle].[document].[event]' |
||
236 | * rules: |
||
237 | * * always 4 parts divided by points. |
||
238 | * * in this context (doctrine/odm stuff) we prefix with 'document.' |
||
239 | * |
||
240 | * @return string routing key |
||
241 | */ |
||
242 | 2 | private function generateRoutingKey() |
|
243 | { |
||
244 | 2 | $routeParts = explode('.', $this->request->get('_route')); |
|
245 | 2 | $action = array_pop($routeParts); |
|
246 | 2 | $baseRoute = implode('.', $routeParts); |
|
247 | |||
248 | // find our route in the map |
||
249 | 2 | $routingKey = null; |
|
250 | |||
251 | 2 | foreach ($this->eventMap as $mapElement) { |
|
252 | 2 | if ($mapElement['baseRoute'] == $baseRoute && |
|
253 | 2 | isset($mapElement['events'][$action]) |
|
254 | 1 | ) { |
|
255 | 2 | $routingKey = $mapElement['events'][$action]; |
|
256 | 2 | break; |
|
257 | } |
||
258 | 1 | } |
|
259 | |||
260 | 2 | return $routingKey; |
|
261 | } |
||
262 | |||
263 | /** |
||
264 | * Creates a EventStatus object that gets persisted.. |
||
265 | * |
||
266 | * @param QueueEvent $queueEvent queueEvent object |
||
267 | * |
||
268 | * @return string |
||
269 | */ |
||
270 | 2 | private function getStatusUrl($queueEvent) |
|
271 | { |
||
272 | // this has to be checked after cause we should not call getSubscribedWorkerIds() if above is true |
||
273 | 2 | $workerIds = $this->getSubscribedWorkerIds($queueEvent); |
|
274 | 2 | if (empty($workerIds)) { |
|
275 | return ''; |
||
276 | } |
||
277 | |||
278 | // we have subscribers; create the EventStatus entry |
||
279 | /** @var EventStatus $eventStatus **/ |
||
280 | 2 | $eventStatus = new $this->eventStatusClassname(); |
|
281 | 2 | $eventStatus->setCreatedate(new \DateTime()); |
|
282 | 2 | $eventStatus->setEventname($queueEvent->getEvent()); |
|
283 | |||
284 | // if available, transport the ref document to the eventStatus instance |
||
285 | 2 | if (!empty($queueEvent->getDocumenturl())) { |
|
286 | 2 | $eventStatusResource = new $this->eventStatusEventResourceClassname(); |
|
287 | 2 | $eventStatusResource->setRef($this->extRefConverter->getExtReference($queueEvent->getDocumenturl())); |
|
288 | 2 | $eventStatus->setEventresource($eventStatusResource); |
|
289 | 1 | } |
|
290 | |||
291 | 2 | foreach ($workerIds as $workerId) { |
|
292 | /** @var \GravitonDyn\EventStatusBundle\Document\EventStatusStatus $eventStatusStatus **/ |
||
293 | 2 | $eventStatusStatus = new $this->eventStatusStatusClassname(); |
|
294 | 2 | $eventStatusStatus->setWorkerid($workerId); |
|
295 | 2 | $eventStatusStatus->setStatus('opened'); |
|
296 | 2 | $eventStatus->addStatus($eventStatusStatus); |
|
297 | 1 | } |
|
298 | |||
299 | // Set username to Event |
||
300 | 2 | $eventStatus->setUserid($this->getSecurityUsername()); |
|
301 | |||
302 | 2 | $this->documentManager->persist($eventStatus); |
|
303 | 2 | $this->documentManager->flush(); |
|
304 | |||
305 | // get the url.. |
||
306 | 2 | $url = $this->router->generate( |
|
307 | 2 | $this->eventStatusRouteName, |
|
308 | [ |
||
309 | 2 | 'id' => $eventStatus->getId() |
|
310 | 1 | ], |
|
311 | 1 | UrlGeneratorInterface::ABSOLUTE_URL |
|
312 | 1 | ); |
|
313 | |||
314 | 2 | return $url; |
|
315 | } |
||
316 | |||
317 | /** |
||
318 | * Checks EventWorker for worker that are subscribed to our event and returns |
||
319 | * their workerIds as array |
||
320 | |||
321 | * @param QueueEvent $queueEvent queueEvent object |
||
322 | * |
||
323 | * @return array array of worker ids |
||
324 | */ |
||
325 | 2 | private function getSubscribedWorkerIds(QueueEvent $queueEvent) |
|
326 | { |
||
327 | // compose our regex to match stars ;-) |
||
328 | // results in = /((\*|document)+)\.((\*|dude)+)\.((\*|config)+)\.((\*|update)+)/ |
||
329 | 2 | $routingArgs = explode('.', $queueEvent->getEvent()); |
|
330 | $regex = |
||
331 | '/'. |
||
332 | 2 | implode( |
|
333 | 2 | '\.', |
|
334 | 2 | array_map( |
|
335 | 2 | function ($arg) { |
|
336 | 2 | return '((\*|'.$arg.')+)'; |
|
337 | 2 | }, |
|
338 | 1 | $routingArgs |
|
339 | 1 | ) |
|
340 | 1 | ). |
|
341 | 2 | '/'; |
|
342 | |||
343 | // look up workers by class name |
||
344 | 2 | $qb = $this->documentManager->createQueryBuilder($this->eventWorkerClassname); |
|
345 | $data = $qb |
||
346 | 2 | ->select('id') |
|
347 | 2 | ->field('subscription.event') |
|
348 | 2 | ->equals(new \MongoRegex($regex)) |
|
349 | 2 | ->getQuery() |
|
350 | 2 | ->execute() |
|
351 | 2 | ->toArray(); |
|
352 | |||
353 | 2 | return array_keys($data); |
|
354 | } |
||
355 | |||
356 | /** |
||
357 | * Security needs to be enabled to get |
||
358 | * |
||
359 | * @return String |
||
360 | */ |
||
361 | 2 | private function getSecurityUsername() |
|
369 | |||
370 | /** |
||
371 | * Changes the urls in the QueueEvent for the workers |
||
372 | * |
||
373 | * @param QueueEvent $queueEvent queue event |
||
374 | * |
||
375 | * @return QueueEvent altered queue event |
||
376 | */ |
||
377 | private function getWorkerQueueEvent(QueueEvent $queueEvent) |
||
383 | |||
384 | /** |
||
385 | * changes an uri for the workers |
||
386 | * |
||
387 | * @param string $uri uri |
||
388 | * |
||
389 | * @return string changed uri |
||
390 | */ |
||
391 | private function getWorkerRelativeUrl($uri) |
||
400 | } |
||
401 |
Let’s take a look at an example:
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.
Available Fixes
Change the type-hint for the parameter:
Add an additional type-check:
Add the method to the interface: