{"action":"created","comment":{"url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/pulls/comments/89657956","pull_request_review_id":10203030,"id":89657956,"diff_hunk":"@@ -0,0 +1,121 @@\n+<?php\n+\n+declare(strict_types=1);\n+\n+namespace LizardsAndPumpkins\\ProductSearch\\ContentDelivery;\n+\n+use LizardsAndPumpkins\\Context\\Context;\n+use LizardsAndPumpkins\\DataPool\\DataPoolReader;\n+use LizardsAndPumpkins\\DataPool\\SearchEngine\\FacetFiltersToIncludeInResult;\n+use LizardsAndPumpkins\\DataPool\\SearchEngine\\Query\\SortOrderConfig;\n+use LizardsAndPumpkins\\Http\\ContentDelivery\\ProductJsonService\\ProductJsonService;\n+use LizardsAndPumpkins\\ProductListing\\Exception\\InvalidNumberOfProductsPerPageException;\n+use LizardsAndPumpkins\\ProductSearch\\ContentDelivery\\Exception\\UnsupportedSortOrderException;\n+use LizardsAndPumpkins\\ProductSearch\\QueryOptions;\n+\n+class ProductSearchService\n+{\n+    /**\n+     * @var DataPoolReader\n+     */\n+    private $dataPoolReader;\n+\n+    /**\n+     * @var ProductJsonService\n+     */\n+    private $productJsonService;\n+\n+    /**\n+     * @var int\n+     */\n+    private $maxAllowedProductsPerPage;\n+\n+    /**\n+     * @var string[]\n+     */\n+    private $sortableAttributeCodes;\n+\n+    public function __construct(\n+        DataPoolReader $dataPoolReader,\n+        ProductJsonService $productJsonService,\n+        int $maxAllowedProductsPerPage,\n+        string ...$sortableAttributeCodes\n+    ) {\n+        $this->dataPoolReader = $dataPoolReader;\n+        $this->productJsonService = $productJsonService;\n+        $this->maxAllowedProductsPerPage = $maxAllowedProductsPerPage;\n+        $this->sortableAttributeCodes = $sortableAttributeCodes;\n+    }\n+\n+    /**\n+     * @param string $queryString\n+     * @param Context $context\n+     * @param int $rowsPerPage\n+     * @param int $pageNumber\n+     * @param SortOrderConfig $sortOrderConfig\n+     * @return array[]\n+     */\n+    public function getData(\n+        string $queryString,\n+        Context $context,\n+        int $rowsPerPage,\n+        int $pageNumber,\n+        SortOrderConfig $sortOrderConfig\n+    ) : array {\n+        $queryOptions = $this->createQueryOptions($context, $rowsPerPage, $pageNumber, $sortOrderConfig);\n+        $searchEngineResponse = $this->dataPoolReader->getSearchResultsMatchingString($queryString, $queryOptions);\n+        $productIds = $searchEngineResponse->getProductIds();\n+\n+        if ([] === $productIds) {\n+            return ['total' => 0, 'data' => []];\n+        }\n+\n+        return [\n+            'total' => $searchEngineResponse->getTotalNumberOfResults(),\n+            'data' => $this->productJsonService->get($context, ...$productIds)\n+        ];\n+    }\n+\n+    private function createQueryOptions(\n+        Context $context,\n+        int $rowsPerPage,\n+        int $pageNumber,\n+        SortOrderConfig $sortOrderConfig\n+    ) : QueryOptions\n+    {\n+        $this->validateSortOrderConfig($sortOrderConfig);\n+        $this->validateRowsPerPage($rowsPerPage);\n+\n+        $filterSelection = [];\n+        $facetFiltersToIncludeInResult = new FacetFiltersToIncludeInResult();\n+\n+        return QueryOptions::create(\n+            $filterSelection,\n+            $context,\n+            $facetFiltersToIncludeInResult,\n+            $rowsPerPage,\n+            $pageNumber,\n+            $sortOrderConfig\n+        );\n+    }\n+\n+    private function validateSortOrderConfig(SortOrderConfig $sortOrderConfig)\n+    {\n+        if (!in_array((string) $sortOrderConfig->getAttributeCode(), $this->sortableAttributeCodes)) {\n+            throw new UnsupportedSortOrderException(\n+                sprintf('Sorting by \"%s\" is not supported', $sortOrderConfig->getAttributeCode())\n+            );\n+        }\n+    }\n+\n+    private function validateRowsPerPage(int $rowsPerPage)\n+    {\n+        if ($rowsPerPage > $this->maxAllowedProductsPerPage) {\n+            throw new InvalidNumberOfProductsPerPageException(sprintf(","path":"src/ProductSearch/ContentDelivery/ProductSearchService.php","position":114,"original_position":114,"commit_id":"92e64910e506330b8ad59c911d87f649b672db06","original_commit_id":"f46d4c2622ff63cc2256b30ee09ab9e638e68926","user":{"login":"Vinai","id":72463,"avatar_url":"https://avatars.githubusercontent.com/u/72463?v=3","gravatar_id":"","url":"https://api.github.com/users/Vinai","html_url":"https://github.com/Vinai","followers_url":"https://api.github.com/users/Vinai/followers","following_url":"https://api.github.com/users/Vinai/following{/other_user}","gists_url":"https://api.github.com/users/Vinai/gists{/gist_id}","starred_url":"https://api.github.com/users/Vinai/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Vinai/subscriptions","organizations_url":"https://api.github.com/users/Vinai/orgs","repos_url":"https://api.github.com/users/Vinai/repos","events_url":"https://api.github.com/users/Vinai/events{/privacy}","received_events_url":"https://api.github.com/users/Vinai/received_events","type":"User","site_admin":false},"body":"We should distinguish the frontend product search with the ProductSearchRequestHandler and ProductListingPageContentBuilder and PageRequest, and the and the product search API. The former can depend on ProductListing, the latter should not. Ideally the former would be moved into it's own module.\r\nThat would give us:\r\n`ProductSearch`- the underlying search API without any frontend rendering\r\n`ProductListing` and `FrontendProductSearch` - using the `ProductSearch` to render results.\r\nActually, the latter two could probably be merged, since there isn't a big difference. What do you think?\r\n","created_at":"2016-11-25T21:25:19Z","updated_at":"2016-11-25T21:25:20Z","html_url":"https://github.com/lizards-and-pumpkins/catalog/pull/952#discussion_r89657956","pull_request_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/pulls/952","_links":{"self":{"href":"https://api.github.com/repos/lizards-and-pumpkins/catalog/pulls/comments/89657956"},"html":{"href":"https://github.com/lizards-and-pumpkins/catalog/pull/952#discussion_r89657956"},"pull_request":{"href":"https://api.github.com/repos/lizards-and-pumpkins/catalog/pulls/952"}}},"pull_request":{"url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/pulls/952","id":94532679,"html_url":"https://github.com/lizards-and-pumpkins/catalog/pull/952","diff_url":"https://github.com/lizards-and-pumpkins/catalog/pull/952.diff","patch_url":"https://github.com/lizards-and-pumpkins/catalog/pull/952.patch","issue_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/issues/952","number":952,"state":"open","locked":false,"title":"Issue #649: Search API","user":{"login":"tim-bezhashvyly","id":462687,"avatar_url":"https://avatars.githubusercontent.com/u/462687?v=3","gravatar_id":"","url":"https://api.github.com/users/tim-bezhashvyly","html_url":"https://github.com/tim-bezhashvyly","followers_url":"https://api.github.com/users/tim-bezhashvyly/followers","following_url":"https://api.github.com/users/tim-bezhashvyly/following{/other_user}","gists_url":"https://api.github.com/users/tim-bezhashvyly/gists{/gist_id}","starred_url":"https://api.github.com/users/tim-bezhashvyly/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tim-bezhashvyly/subscriptions","organizations_url":"https://api.github.com/users/tim-bezhashvyly/orgs","repos_url":"https://api.github.com/users/tim-bezhashvyly/repos","events_url":"https://api.github.com/users/tim-bezhashvyly/events{/privacy}","received_events_url":"https://api.github.com/users/tim-bezhashvyly/received_events","type":"User","site_admin":false},"body":"Missing so far:\r\n\r\n- Filters\r\n- ~~Total number of search results in JSON~~\r\n- PHP exceptions must be converted into JSON errors with corresponding response code and meaningful messages\r\n- Include the search query string and the query options in the returned result","created_at":"2016-11-20T21:13:24Z","updated_at":"2016-11-25T21:25:20Z","closed_at":null,"merged_at":null,"merge_commit_sha":"8a9117a79ff64bd997b6efed09481e430800bd21","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/pulls/952/commits","review_comments_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/pulls/952/comments","review_comment_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/pulls/comments{/number}","comments_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/issues/952/comments","statuses_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/statuses/92e64910e506330b8ad59c911d87f649b672db06","head":{"label":"lizards-and-pumpkins:issue-649-search-api","ref":"issue-649-search-api","sha":"92e64910e506330b8ad59c911d87f649b672db06","user":{"login":"lizards-and-pumpkins","id":8191923,"avatar_url":"https://avatars.githubusercontent.com/u/8191923?v=3","gravatar_id":"","url":"https://api.github.com/users/lizards-and-pumpkins","html_url":"https://github.com/lizards-and-pumpkins","followers_url":"https://api.github.com/users/lizards-and-pumpkins/followers","following_url":"https://api.github.com/users/lizards-and-pumpkins/following{/other_user}","gists_url":"https://api.github.com/users/lizards-and-pumpkins/gists{/gist_id}","starred_url":"https://api.github.com/users/lizards-and-pumpkins/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lizards-and-pumpkins/subscriptions","organizations_url":"https://api.github.com/users/lizards-and-pumpkins/orgs","repos_url":"https://api.github.com/users/lizards-and-pumpkins/repos","events_url":"https://api.github.com/users/lizards-and-pumpkins/events{/privacy}","received_events_url":"https://api.github.com/users/lizards-and-pumpkins/received_events","type":"Organization","site_admin":false},"repo":{"id":25209535,"name":"catalog","full_name":"lizards-and-pumpkins/catalog","owner":{"login":"lizards-and-pumpkins","id":8191923,"avatar_url":"https://avatars.githubusercontent.com/u/8191923?v=3","gravatar_id":"","url":"https://api.github.com/users/lizards-and-pumpkins","html_url":"https://github.com/lizards-and-pumpkins","followers_url":"https://api.github.com/users/lizards-and-pumpkins/followers","following_url":"https://api.github.com/users/lizards-and-pumpkins/following{/other_user}","gists_url":"https://api.github.com/users/lizards-and-pumpkins/gists{/gist_id}","starred_url":"https://api.github.com/users/lizards-and-pumpkins/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lizards-and-pumpkins/subscriptions","organizations_url":"https://api.github.com/users/lizards-and-pumpkins/orgs","repos_url":"https://api.github.com/users/lizards-and-pumpkins/repos","events_url":"https://api.github.com/users/lizards-and-pumpkins/events{/privacy}","received_events_url":"https://api.github.com/users/lizards-and-pumpkins/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/lizards-and-pumpkins/catalog","description":"Lizards and Pumpkins","fork":false,"url":"https://api.github.com/repos/lizards-and-pumpkins/catalog","forks_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/forks","keys_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/keys{/key_id}","collaborators_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/teams","hooks_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/hooks","issue_events_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/issues/events{/number}","events_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/events","assignees_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/assignees{/user}","branches_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/branches{/branch}","tags_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/tags","blobs_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/git/refs{/sha}","trees_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/git/trees{/sha}","statuses_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/statuses/{sha}","languages_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/languages","stargazers_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/stargazers","contributors_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/contributors","subscribers_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/subscribers","subscription_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/subscription","commits_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/commits{/sha}","git_commits_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/git/commits{/sha}","comments_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/comments{/number}","issue_comment_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/issues/comments{/number}","contents_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/contents/{+path}","compare_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/compare/{base}...{head}","merges_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/merges","archive_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/downloads","issues_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/issues{/number}","pulls_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/pulls{/number}","milestones_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/milestones{/number}","notifications_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/labels{/name}","releases_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/releases{/id}","deployments_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/deployments","created_at":"2014-10-14T13:57:56Z","updated_at":"2016-11-24T10:24:54Z","pushed_at":"2016-11-25T20:13:09Z","git_url":"git://github.com/lizards-and-pumpkins/catalog.git","ssh_url":"git@github.com:lizards-and-pumpkins/catalog.git","clone_url":"https://github.com/lizards-and-pumpkins/catalog.git","svn_url":"https://github.com/lizards-and-pumpkins/catalog","homepage":"lizardsandpumpkins.com","size":19278,"stargazers_count":16,"watchers_count":16,"language":"PHP","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":60,"forks":1,"open_issues":60,"watchers":16,"default_branch":"master"}},"base":{"label":"lizards-and-pumpkins:master","ref":"master","sha":"28b4d86aaed832f1f8a2886c20943820e3cbd2ee","user":{"login":"lizards-and-pumpkins","id":8191923,"avatar_url":"https://avatars.githubusercontent.com/u/8191923?v=3","gravatar_id":"","url":"https://api.github.com/users/lizards-and-pumpkins","html_url":"https://github.com/lizards-and-pumpkins","followers_url":"https://api.github.com/users/lizards-and-pumpkins/followers","following_url":"https://api.github.com/users/lizards-and-pumpkins/following{/other_user}","gists_url":"https://api.github.com/users/lizards-and-pumpkins/gists{/gist_id}","starred_url":"https://api.github.com/users/lizards-and-pumpkins/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lizards-and-pumpkins/subscriptions","organizations_url":"https://api.github.com/users/lizards-and-pumpkins/orgs","repos_url":"https://api.github.com/users/lizards-and-pumpkins/repos","events_url":"https://api.github.com/users/lizards-and-pumpkins/events{/privacy}","received_events_url":"https://api.github.com/users/lizards-and-pumpkins/received_events","type":"Organization","site_admin":false},"repo":{"id":25209535,"name":"catalog","full_name":"lizards-and-pumpkins/catalog","owner":{"login":"lizards-and-pumpkins","id":8191923,"avatar_url":"https://avatars.githubusercontent.com/u/8191923?v=3","gravatar_id":"","url":"https://api.github.com/users/lizards-and-pumpkins","html_url":"https://github.com/lizards-and-pumpkins","followers_url":"https://api.github.com/users/lizards-and-pumpkins/followers","following_url":"https://api.github.com/users/lizards-and-pumpkins/following{/other_user}","gists_url":"https://api.github.com/users/lizards-and-pumpkins/gists{/gist_id}","starred_url":"https://api.github.com/users/lizards-and-pumpkins/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lizards-and-pumpkins/subscriptions","organizations_url":"https://api.github.com/users/lizards-and-pumpkins/orgs","repos_url":"https://api.github.com/users/lizards-and-pumpkins/repos","events_url":"https://api.github.com/users/lizards-and-pumpkins/events{/privacy}","received_events_url":"https://api.github.com/users/lizards-and-pumpkins/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/lizards-and-pumpkins/catalog","description":"Lizards and Pumpkins","fork":false,"url":"https://api.github.com/repos/lizards-and-pumpkins/catalog","forks_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/forks","keys_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/keys{/key_id}","collaborators_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/teams","hooks_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/hooks","issue_events_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/issues/events{/number}","events_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/events","assignees_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/assignees{/user}","branches_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/branches{/branch}","tags_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/tags","blobs_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/git/refs{/sha}","trees_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/git/trees{/sha}","statuses_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/statuses/{sha}","languages_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/languages","stargazers_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/stargazers","contributors_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/contributors","subscribers_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/subscribers","subscription_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/subscription","commits_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/commits{/sha}","git_commits_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/git/commits{/sha}","comments_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/comments{/number}","issue_comment_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/issues/comments{/number}","contents_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/contents/{+path}","compare_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/compare/{base}...{head}","merges_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/merges","archive_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/downloads","issues_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/issues{/number}","pulls_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/pulls{/number}","milestones_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/milestones{/number}","notifications_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/labels{/name}","releases_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/releases{/id}","deployments_url":"https://api.github.com/repos/lizards-and-pumpkins/catalog/deployments","created_at":"2014-10-14T13:57:56Z","updated_at":"2016-11-24T10:24:54Z","pushed_at":"2016-11-25T20:13:09Z","git_url":"git://github.com/lizards-and-pumpkins/catalog.git","ssh_url":"git@github.com:lizards-and-pumpkins/catalog.git","clone_url":"https://github.com/lizards-and-pumpkins/catalog.git","svn_url":"https://github.com/lizards-and-pumpkins/catalog","homepage":"lizardsandpumpkins.com","size":19278,"stargazers_count":16,"watchers_count":16,"language":"PHP","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":60,"forks":1,"open_issues":60,"watchers":16,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/lizards-and-pumpkins/catalog/pulls/952"},"html":{"href":"https://github.com/lizards-and-pumpkins/catalog/pull/952"},"issue":{"href":"https://api.github.com/repos/lizards-and-pumpkins/catalog/issues/952"},"comments":{"href":"https://api.github.com/repos/lizards-and-pumpkins/catalog/issues/952/comments"},"review_comments":{"href":"https://api.github.com/repos/lizards-and-pumpkins/catalog/pulls/952/comments"},"review_comment":{"href":"https://api.github.com/repos/lizards-and-pumpkins/catalog/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/lizards-and-pumpkins/catalog/pulls/952/commits"},"statuses":{"href":"https://api.github.com/repos/lizards-and-pumpkins/catalog/statuses/92e64910e506330b8ad59c911d87f649b672db06"}}}}
{"actor":{"display_login":"Vinai"}}