From dd7ec6dcb1f70f2e085e8d4a7a3f75e0b636e726 Mon Sep 17 00:00:00 2001
From: Eric Bremner <ebremner@uwaterloo.ca>
Date: Tue, 18 Jul 2023 16:33:44 -0400
Subject: [PATCH] ISTWCMS-5954: adding subscriber for better formatting of api

---
 src/EventSubscriber/UwResponseSubscriber.php | 201 +++++++++++++++++++
 uw_cfg_common.services.yml                   |   7 +
 2 files changed, 208 insertions(+)
 create mode 100644 src/EventSubscriber/UwResponseSubscriber.php

diff --git a/src/EventSubscriber/UwResponseSubscriber.php b/src/EventSubscriber/UwResponseSubscriber.php
new file mode 100644
index 00000000..2ff741d3
--- /dev/null
+++ b/src/EventSubscriber/UwResponseSubscriber.php
@@ -0,0 +1,201 @@
+<?php
+
+namespace Drupal\uw_cfg_common\EventSubscriber;
+
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Entity\EntityTypeManager;
+use Drupal\Core\Entity\EntityRepository;
+use Drupal\Core\File\FileUrlGeneratorInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\jsonapi\Routing\Routes;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpKernel\Event\ResponseEvent;
+use Symfony\Component\HttpKernel\KernelEvents;
+
+/**
+ * Class UwResponseSubscriber.
+ *
+ * Implements the alter hook for JSON:API responses.
+ *
+ * @package Drupal\uw_cfg_common\EventSubscriber
+ */
+class UwResponseSubscriber implements EventSubscriberInterface, ContainerInjectionInterface {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The entity repository.
+   *
+   * @var \Drupal\Core\Entity\EntityRepository
+   */
+  protected $entityRepository;
+
+  /**
+   * The file url generator.
+   *
+   * @var \Drupal\Core\File\FileUrlGeneratorInterface
+   */
+  protected $fileUrlGenerator;
+
+  /**
+   * The route match service.
+   *
+   * @var \Drupal\Core\Routing\RouteMatchInterface
+   */
+  protected $routeMatch;
+
+  /**
+   * ResponseSubscriber constructor.
+   *
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $entityTypeManager
+   *   The entity type manager.
+   * @param \Drupal\Core\Entity\EntityRepository $entityRepository
+   *   The entity repository.
+   * @param \Drupal\Core\Routing\RouteMatchInterface $fileUrlGenerator
+   *   The file url generator.
+   */
+  public function __construct(
+    EntityTypeManager $entityTypeManager,
+    EntityRepository $entityRepository,
+    FileUrlGeneratorInterface $fileUrlGenerator
+  ) {
+
+    $this->entityTypeManager = $entityTypeManager;
+    $this->entityRepository = $entityRepository;
+    $this->fileUrlGenerator = $fileUrlGenerator;
+  }
+
+  /**
+   * Create a new instance.
+   *
+   * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
+   *   The container.
+   *
+   * @return \Drupal\jsonapi_response_alter\EventSubscriber\ResponseSubscriber
+   *   The new instance.
+   */
+  public static function create(ContainerInterface $container) {
+    return new self(
+      $container->get('entity_type.manager'),
+      $container->get('entity.repository'),
+      $container->get('file_url_generator')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events[KernelEvents::RESPONSE] = ['onResponse'];
+
+    return $events;
+  }
+
+  /**
+   * Set route match service.
+   *
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The  route match service.
+   */
+  public function setRouteMatch(RouteMatchInterface $route_match) {
+    $this->routeMatch = $route_match;
+  }
+
+  /**
+   * This method is called the KernelEvents::RESPONSE event is dispatched.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\ResponseEvent $event
+   *   The filter event.
+   */
+  public function onResponse(ResponseEvent $event) {
+
+    // If there is no route object, i.e. non-routed pages,
+    // simply return nothing.
+    if (!$this->routeMatch->getRouteObject()) {
+      return;
+    }
+
+    // If this is a jsonapi request, set some values.
+    if (
+      $this->routeMatch->getRouteName() === 'jsonapi.resource_list' ||
+      Routes::isJsonApiRequest($this->routeMatch->getRouteObject()
+        ->getDefaults())
+    ) {
+
+      // Get the response and content.
+      $response = $event->getResponse();
+      $content = $response->getContent();
+
+      // Get the json response into a readable format.
+      $jsonapi_response = json_decode($content, TRUE);
+
+      // If there is no json response, simply return.
+      if (!is_array($jsonapi_response)) {
+        return;
+      }
+
+      // If there is a listing image, set it to the
+      // info we need.
+      if (isset($jsonapi_response['data'][0]['relationships']['listing_page_image'])) {
+        $this->setUwListingImage($jsonapi_response);
+      }
+
+      // Now set the new json response.
+      $response->setContent(json_encode($jsonapi_response));
+    }
+  }
+
+  /**
+   * Function to sub in the file info for listing image.
+   *
+   * @param array $jsonapi_response
+   *   The json api response.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
+   */
+  private function setUwListingImage(array &$jsonapi_response): void {
+
+    // Step through each of the data and set the listing image.
+    for ($i = 0; $i < count($jsonapi_response['data']); $i++) {
+
+      // If there is a uuid for the listing image, then set the data.
+      if ($uuid = $jsonapi_response['data'][$i]['relationships']['listing_page_image']['data']['id']) {
+
+        // Load the media entity using the uuid.
+        $entity = $this->entityRepository->loadEntityByUuid('media', $uuid);
+
+        // Get the image field from the media entity.
+        $image = $entity->field_media_image;
+
+        // Load the file entity using the media target id.
+        // Need to do this because the mid could be different
+        // than the fid.
+        $file = $this->entityTypeManager
+          ->getStorage('file')
+          ->load($image->target_id);
+
+        // Set the values for the listing image.
+        $jsonapi_response['data'][$i]['relationships']['listing_page_image'] = [
+          'fid' => $file->id(),
+          'filename' => $file->getFilename(),
+          'uri' => $file->getFileUri(),
+          'url' => $this->fileUrlGenerator->generateAbsoluteString($file->getFileUri()),
+          'filemime' => $file->getMimeType(),
+          'filesize' => $file->getSize(),
+          'uuid' => $file->uuid(),
+          'alt' => $image->alt,
+          'width' => $image->width,
+          'height' => $image->height,
+        ];
+      }
+    }
+  }
+
+}
diff --git a/uw_cfg_common.services.yml b/uw_cfg_common.services.yml
index f3b10415..cabbbb8f 100644
--- a/uw_cfg_common.services.yml
+++ b/uw_cfg_common.services.yml
@@ -49,3 +49,10 @@ services:
     class: '\Drupal\uw_cfg_common\EventSubscriber\UwWebformEventSubscriber'
     tags:
       - { name: 'event_subscriber' }
+  uw_cfg_common.response_subscriber:
+    class: Drupal\uw_cfg_common\EventSubscriber\UwResponseSubscriber
+    arguments: [ '@entity_type.manager', '@entity.repository', '@file_url_generator' ]
+    tags:
+      - { name: event_subscriber }
+    calls:
+      - [ setRouteMatch, [ '@current_route_match' ] ]
-- 
GitLab