From ae687fd483a238a3517c8a5fb8b461ce8ed05f45 Mon Sep 17 00:00:00 2001
From: Tobias Gross-Vogt <tgros@tugraz.at>
Date: Tue, 15 Nov 2022 10:46:14 +0100
Subject: [PATCH] using data provider from core; new pagination handling -> new
 provider interface --> BREAKING CHANGE

---
 composer.lock                                 |  8 +-
 src/API/OrganizationProviderInterface.php     | 17 ++--
 .../OrganizationCollectionDataProvider.php    | 93 -------------------
 src/DataProvider/OrganizationDataProvider.php | 90 ++++++++++++++++++
 .../OrganizationItemDataProvider.php          | 52 -----------
 src/Resources/config/services.yaml            |  3 +-
 src/Service/DummyOrganizationProvider.php     |  6 +-
 7 files changed, 106 insertions(+), 163 deletions(-)
 delete mode 100644 src/DataProvider/OrganizationCollectionDataProvider.php
 create mode 100644 src/DataProvider/OrganizationDataProvider.php
 delete mode 100644 src/DataProvider/OrganizationItemDataProvider.php

diff --git a/composer.lock b/composer.lock
index 372096b..07269ba 100644
--- a/composer.lock
+++ b/composer.lock
@@ -169,11 +169,11 @@
         },
         {
             "name": "dbp/relay-core-bundle",
-            "version": "v0.1.55",
+            "version": "v0.1.56",
             "source": {
                 "type": "git",
                 "url": "https://gitlab.tugraz.at/dbp/relay/dbp-relay-core-bundle",
-                "reference": "b1d1eaa5e9efc58a09e6176215c243041df168f3"
+                "reference": "0183be15e0c582d935420fb558ea293ab6af1b8b"
             },
             "require": {
                 "api-platform/core": "^2.6.8 <2.7.0",
@@ -237,7 +237,7 @@
                 "AGPL-3.0-or-later"
             ],
             "description": "The core bundle of the Relay API gateway",
-            "time": "2022-11-10T10:15:38+00:00"
+            "time": "2022-11-15T08:43:56+00:00"
         },
         {
             "name": "doctrine/annotations",
@@ -10043,5 +10043,5 @@
     "platform-overrides": {
         "php": "7.3"
     },
-    "plugin-api-version": "2.3.0"
+    "plugin-api-version": "2.2.0"
 }
diff --git a/src/API/OrganizationProviderInterface.php b/src/API/OrganizationProviderInterface.php
index 7f02650..52013cc 100644
--- a/src/API/OrganizationProviderInterface.php
+++ b/src/API/OrganizationProviderInterface.php
@@ -6,13 +6,12 @@ namespace Dbp\Relay\BaseOrganizationBundle\API;
 
 use Dbp\Relay\BaseOrganizationBundle\Entity\Organization;
 use Dbp\Relay\CoreBundle\Exception\ApiError;
-use Dbp\Relay\CoreBundle\Pagination\Paginator;
 
 interface OrganizationProviderInterface
 {
     /**
      * @param array $options Available options:
-     *                       * 'lang' ('de' or 'en')
+     *                       * Locale::LANGUAGE_OPTION (language in ISO 639‑1 format)
      *                       * LocalData::INCLUDE_PARAMETER_NAME
      *
      * @throws ApiError
@@ -21,13 +20,15 @@ interface OrganizationProviderInterface
 
     /**
      * @param array $options Available options:
-     *                       * string 'lang' ('de' or 'en')
-     *                       * array 'identifiers' The list of organizations to return
-     *                       * string Organization::SEARCH_PARAMETER_NAME (partial, case-insensitive text search on 'name' attribute)
-     *                       * string LocalData::INCLUDE_PARAMETER_NAME
-     *                       * string LocalData::QUERY_PARAMETER_NAME
+     *                       * Locale::LANGUAGE_OPTION (language in ISO 639‑1 format)
+     *                       * 'identifiers' The list of organizations to return
+     *                       * Organization::SEARCH_PARAMETER_NAME (partial, case-insensitive text search on 'name' attribute)
+     *                       * LocalData::INCLUDE_PARAMETER_NAME
+     *                       * LocalData::QUERY_PARAMETER_NAME
+     *
+     * @return Organization[]
      *
      * @throws ApiError
      */
-    public function getOrganizations(array $options = []): Paginator;
+    public function getOrganizations(int $currentPageNumber, int $maxNumItemsPerPage, array $options = []): array;
 }
diff --git a/src/DataProvider/OrganizationCollectionDataProvider.php b/src/DataProvider/OrganizationCollectionDataProvider.php
deleted file mode 100644
index 2a85afc..0000000
--- a/src/DataProvider/OrganizationCollectionDataProvider.php
+++ /dev/null
@@ -1,93 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Dbp\Relay\BaseOrganizationBundle\DataProvider;
-
-use ApiPlatform\Core\DataProvider\ContextAwareCollectionDataProviderInterface;
-use ApiPlatform\Core\DataProvider\PaginatorInterface;
-use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
-use Dbp\Relay\BaseOrganizationBundle\API\OrganizationProviderInterface;
-use Dbp\Relay\BaseOrganizationBundle\API\OrganizationsByPersonProviderInterface;
-use Dbp\Relay\BaseOrganizationBundle\Entity\Organization;
-use Dbp\Relay\CoreBundle\Exception\ApiError;
-use Dbp\Relay\CoreBundle\Helpers\Locale;
-use Dbp\Relay\CoreBundle\LocalData\LocalData;
-use Dbp\Relay\CoreBundle\Pagination\Pagination;
-use Dbp\Relay\CoreBundle\Pagination\Paginator;
-use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
-use Symfony\Component\HttpFoundation\Response;
-
-final class OrganizationCollectionDataProvider extends AbstractController implements ContextAwareCollectionDataProviderInterface, RestrictedDataProviderInterface
-{
-    public const MAX_ITEMS_PER_PAGE = 250;
-
-    /** @var OrganizationProviderInterface */
-    private $organizationProvider;
-
-    /** @var OrganizationsByPersonProviderInterface */
-    private $organizationsByPersonProvider;
-
-    /** @var Locale */
-    private $locale;
-
-    public function __construct(OrganizationProviderInterface $organizationProvider, Locale $locale, OrganizationsByPersonProviderInterface $organizationsByPersonProvider)
-    {
-        $this->organizationProvider = $organizationProvider;
-        $this->locale = $locale;
-        $this->organizationsByPersonProvider = $organizationsByPersonProvider;
-    }
-
-    public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
-    {
-        return Organization::class === $resourceClass;
-    }
-
-    public function getCollection(string $resourceClass, string $operationName = null, array $context = []): Paginator
-    {
-        $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
-
-        $filters = $context['filters'] ?? [];
-        $options = [];
-
-        if ($search = ($filters['search'] ?? null)) {
-            $options['search'] = $search;
-        }
-
-        LocalData::addOptions($options, $filters);
-
-        $filters['partialPagination'] = true;
-        Pagination::addOptions($options, $filters, self::MAX_ITEMS_PER_PAGE);
-
-        // @deprecate 'lang' filter is deprecate, use 'Accept-Language' header instead
-        if (($lang = $filters['lang'] ?? null) !== null) {
-            $options[Locale::LANGUAGE_OPTION] = $lang;
-        } else {
-            $this->locale->addLanguageOption($options);
-        }
-
-        //-------------------------------------------------------------------------
-        // @deprecate The 'person' filter is deprecate. Use the 'identifiers' filter in your custom organizations wrapper.
-        $personId = $filters['person'] ?? '';
-        if ($personId !== '') {
-            if ($personId !== $this->getUser()->getUserIdentifier()) {
-                throw new ApiError(Response::HTTP_UNAUTHORIZED, 'only allowed with ID of currently logged-in person');
-            }
-
-            $organizations = [];
-            $orgIdPaginator = $this->organizationsByPersonProvider->getOrganizationsByPerson($personId, $options);
-            foreach ($orgIdPaginator as $organizationId) {
-                $organizations[] = $this->organizationProvider->getOrganizationById($organizationId, $options);
-            }
-
-            if ($orgIdPaginator instanceof PaginatorInterface) {
-                return Pagination::createFullPaginator($organizations, $options, intval($orgIdPaginator->getTotalItems()));
-            } else {
-                return Pagination::createPartialPaginator($organizations, $options);
-            }
-        }
-        //-------------------------------------------------------------------------
-
-        return $this->organizationProvider->getOrganizations($options);
-    }
-}
diff --git a/src/DataProvider/OrganizationDataProvider.php b/src/DataProvider/OrganizationDataProvider.php
new file mode 100644
index 0000000..a2bc68d
--- /dev/null
+++ b/src/DataProvider/OrganizationDataProvider.php
@@ -0,0 +1,90 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Dbp\Relay\BaseOrganizationBundle\DataProvider;
+
+use Dbp\Relay\BaseOrganizationBundle\API\OrganizationProviderInterface;
+use Dbp\Relay\BaseOrganizationBundle\API\OrganizationsByPersonProviderInterface;
+use Dbp\Relay\BaseOrganizationBundle\Entity\Organization;
+use Dbp\Relay\CoreBundle\DataProvider\AbstractDataProvider;
+use Dbp\Relay\CoreBundle\Exception\ApiError;
+use Dbp\Relay\CoreBundle\Locale\Locale;
+use Symfony\Component\HttpFoundation\RequestStack;
+use Symfony\Component\HttpFoundation\Response;
+
+class OrganizationDataProvider extends AbstractDataProvider
+{
+    /** @var OrganizationProviderInterface */
+    private $organizationProvider;
+
+    /** @var OrganizationsByPersonProviderInterface */
+    private $organizationsByPersonProvider;
+
+    /** @var RequestStack */
+    private $requestStack;
+
+    public function __construct(OrganizationProviderInterface $organizationProvider, OrganizationsByPersonProviderInterface $organizationsByPersonProvider, RequestStack $requestStack)
+    {
+        $this->organizationProvider = $organizationProvider;
+        $this->organizationsByPersonProvider = $organizationsByPersonProvider;
+        $this->requestStack = $requestStack;
+    }
+
+    protected function getResourceClass(): string
+    {
+        return Organization::class;
+    }
+
+    protected function getItemById($id, array $options = []): object
+    {
+        //-------------------------------------------------------------------------
+        // @deprecate 'lang' query parameter is deprecate, use 'Accept-Language' header instead
+        $queryParameters = $this->requestStack->getCurrentRequest()->query->all();
+        $this->tryAddDeprecatedLangQueryParameter($options, $queryParameters);
+        //-------------------------------------------------------------------------
+
+        return $this->organizationProvider->getOrganizationById($id, $options);
+    }
+
+    protected function getPage(int $currentPageNumber, int $maxNumItemsPerPage, array $filters = [], array $options = []): array
+    {
+        //-------------------------------------------------------------------------
+        // @deprecate 'lang' query parameter is deprecate, use 'Accept-Language' header instead
+        $this->tryAddDeprecatedLangQueryParameter($options, $filters);
+        //-------------------------------------------------------------------------
+
+        //-------------------------------------------------------------------------
+        // @deprecate The 'person' filter is deprecate. Use the 'identifiers' filter in your custom organization wrapper.
+        $personId = $filters['person'] ?? '';
+        if ($personId !== '') {
+            if ($personId !== $this->getUser()->getUserIdentifier()) {
+                throw new ApiError(Response::HTTP_UNAUTHORIZED, 'only allowed with ID of currently logged-in person');
+            }
+
+            $organizations = [];
+            foreach ($this->organizationsByPersonProvider->getOrganizationsByPerson($personId, $options) as $organizationId) {
+                $organizations[] = $this->organizationProvider->getOrganizationById($organizationId, $options);
+            }
+
+            return $organizations;
+        }
+        //-------------------------------------------------------------------------
+
+        if ($search = ($filters['search'] ?? null)) {
+            $options['search'] = $search;
+        }
+
+        return $this->organizationProvider->getOrganizations($currentPageNumber, $maxNumItemsPerPage, $options);
+    }
+
+    /**
+     * @deprecate 'lang' query parameter is deprecated, use 'Accept-Language' header instead
+     */
+    private function tryAddDeprecatedLangQueryParameter(array &$targetOptions, array $filters)
+    {
+        if (($lang = $filters['lang'] ?? null) !== null) {
+            $targetOptions[Locale::LANGUAGE_OPTION] = $lang;
+        }
+    }
+}
diff --git a/src/DataProvider/OrganizationItemDataProvider.php b/src/DataProvider/OrganizationItemDataProvider.php
deleted file mode 100644
index 515a5da..0000000
--- a/src/DataProvider/OrganizationItemDataProvider.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Dbp\Relay\BaseOrganizationBundle\DataProvider;
-
-use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
-use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
-use Dbp\Relay\BaseOrganizationBundle\API\OrganizationProviderInterface;
-use Dbp\Relay\BaseOrganizationBundle\Entity\Organization;
-use Dbp\Relay\CoreBundle\Helpers\Locale;
-use Dbp\Relay\CoreBundle\LocalData\LocalData;
-use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
-
-final class OrganizationItemDataProvider extends AbstractController implements ItemDataProviderInterface, RestrictedDataProviderInterface
-{
-    /** @var OrganizationProviderInterface */
-    private $api;
-
-    /** @var Locale */
-    private $locale;
-
-    public function __construct(OrganizationProviderInterface $api, Locale $locale)
-    {
-        $this->api = $api;
-        $this->locale = $locale;
-    }
-
-    public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
-    {
-        return Organization::class === $resourceClass;
-    }
-
-    public function getItem(string $resourceClass, $id, string $operationName = null, array $context = []): ?Organization
-    {
-        $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
-
-        $filters = $context['filters'] ?? [];
-        $options = [];
-
-        LocalData::addOptions($options, $filters);
-
-        // @deprecate 'lang' filter is deprecate, use 'Accept-Language' header instead
-        if (($lang = $filters['lang'] ?? null) !== null) {
-            $options[Locale::LANGUAGE_OPTION] = $lang;
-        } else {
-            $this->locale->addLanguageOption($options);
-        }
-
-        return $this->api->getOrganizationById($id, $options);
-    }
-}
diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml
index b799db5..0e66309 100644
--- a/src/Resources/config/services.yaml
+++ b/src/Resources/config/services.yaml
@@ -1,6 +1,5 @@
 services:
-  Dbp\Relay\BaseOrganizationBundle\DataProvider\:
-    resource: '../../DataProvider'
+  Dbp\Relay\BaseOrganizationBundle\DataProvider\OrganizationDataProvider:
     autowire: true
     autoconfigure: true
 
diff --git a/src/Service/DummyOrganizationProvider.php b/src/Service/DummyOrganizationProvider.php
index 9a85591..bbb7e6c 100644
--- a/src/Service/DummyOrganizationProvider.php
+++ b/src/Service/DummyOrganizationProvider.php
@@ -6,8 +6,6 @@ namespace Dbp\Relay\BaseOrganizationBundle\Service;
 
 use Dbp\Relay\BaseOrganizationBundle\API\OrganizationProviderInterface;
 use Dbp\Relay\BaseOrganizationBundle\Entity\Organization;
-use Dbp\Relay\CoreBundle\Pagination\Paginator;
-use Dbp\Relay\CoreBundle\Pagination\WholeResultPaginator;
 
 class DummyOrganizationProvider implements OrganizationProviderInterface
 {
@@ -20,8 +18,8 @@ class DummyOrganizationProvider implements OrganizationProviderInterface
         return $org;
     }
 
-    public function getOrganizations(array $options = []): Paginator
+    public function getOrganizations(int $currentPageNumber, int $maxNumItemsPerPage, array $options = []): array
     {
-        return new WholeResultPaginator([$this->getOrganizationById('foo', $options)], 1, 30);
+        return [$this->getOrganizationById('foo', $options)];
     }
 }
-- 
GitLab