├── .githooks ├── pre-commit └── pre-push ├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE-MIT ├── README.md ├── assets └── obdb.png ├── composer.json ├── examples ├── builder.php └── simple.php ├── justfile ├── phpstan.neon.dist ├── phpunit.xml.dist ├── pint.json ├── rector.php ├── src ├── Builder.php ├── Client.php ├── Contracts │ ├── Concerns │ │ └── Arrayable.php │ ├── ConnectorContract.php │ ├── Resources │ │ └── BreweriesContract.php │ └── ResponseContract.php ├── Enums │ ├── HttpMethod.php │ └── MediaType.php ├── Exceptions │ ├── ConnectorException.php │ ├── ErrorException.php │ └── UnserializableResponseException.php ├── Http │ └── Connector.php ├── OpenBreweryDb.php ├── Resources │ └── Breweries.php ├── Responses │ ├── Breweries │ │ ├── AutocompleteResponse.php │ │ ├── FindResponse.php │ │ ├── ListResponse.php │ │ └── MetadataResponse.php │ └── Concerns │ │ └── ArrayAccessible.php └── ValueObjects │ ├── Connector │ ├── BaseUri.php │ ├── Headers.php │ ├── QueryParams.php │ └── Response.php │ ├── Payload.php │ ├── ResourceUri.php │ └── Version.php └── tests ├── Architecture.php ├── Pest.php ├── Resources ├── AutocompleteBreweries.php ├── FindBrewery.php ├── ListBreweries.php ├── MetadataBreweries.php ├── RandomBreweries.php └── SearchBreweries.php └── Version.php /.githooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | composer run fmt 4 | -------------------------------------------------------------------------------- /.githooks/pre-push: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | composer run ci -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: 'composer' 5 | directory: '/' 6 | schedule: 7 | interval: 'weekly' 8 | open-pull-requests-limit: 5 9 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [ 'push', 'pull_request' ] 4 | 5 | jobs: 6 | ci: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | fail-fast: true 10 | matrix: 11 | os: [ ubuntu-latest ] 12 | php: [ 8.3 ] 13 | dependency-version: [ prefer-stable ] 14 | 15 | name: PHP ${{ matrix.php }} | OS ${{ matrix.os }} | Dependency Resolution ${{ matrix.dependency-version }} 16 | 17 | steps: 18 | 19 | - name: Checkout 20 | uses: actions/checkout@v2 21 | 22 | - name: Cache dependencies 23 | uses: actions/cache@v1 24 | with: 25 | path: ~/.composer/cache/files 26 | key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} 27 | 28 | - name: Setup PHP 29 | uses: shivammathur/setup-php@v2 30 | with: 31 | php-version: ${{ matrix.php }} 32 | 33 | - name: Install Composer dependencies 34 | run: composer update --${{ matrix.dependency-version }} --no-interaction --prefer-dist 35 | 36 | - name: Run CI checks 37 | run: composer run ci 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | vendor 3 | phpunit.xml 4 | .DS_STORE 5 | composer.lock 6 | .phpdoc 7 | .vscode 8 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Joey McKenzie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Logo](./assets/obdb.png) 2 | 3 |
4 |
5 | 6 | 7 |
8 |
9 | 10 | (Un)official PHP bindings for the [Open Brewery DB API](https://openbrewerydb.org/). Open Brewery DB provides a public 11 | dataset for breweries around the world, as well as offering an API to retrieve data in various forms. This library aims to 12 | provide straight and easy-to-use PHP bindings for querying the API. The library is inspired by the [Open AI client for PHP](https://github.com/openai-php/client) - please give it star as well! 13 | 14 | To get started, first install the package with composer: 15 | 16 | ```shell 17 | $ composer require joeymckenzie/openbrewerydb-php-client 18 | ``` 19 | 20 | Next, spin up a new client within your code and fire away! 21 | 22 | ```php 23 | breweries()->list([ 35 | 'by_city' => 'Sacramento', 36 | ]); 37 | var_dump($breweries); 38 | 39 | // Retrieve various metadata about breweries from the API 40 | $metadata = $client->breweries()->metadata(); 41 | var_dump($metadata); 42 | 43 | // Get a random brewery with a specified page size 44 | $randomBrewery = $client->breweries()->random(5); 45 | var_dump($randomBrewery); 46 | ``` 47 | 48 | The library relies on autodiscovery and will use whichever package that implements PSR-17 within your composer 49 | dependencies. You are free to use the HTTP client of you choice, though a popular package 50 | is [Guzzle](https://docs.guzzlephp.org/en/stable/). 51 | 52 | Though I am not affiliated with organization itself, check out the entire set of APIs offered by Open Brewery DB, check 53 | out the docs on their [website](https://openbrewerydb.org/documentation). 54 | -------------------------------------------------------------------------------- /assets/obdb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JoeyMckenzie/openbrewerydb-php-client/81f7bc75261b23339fd609c5cf3b753e6463cfdf/assets/obdb.png -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "joeymckenzie/openbrewerydb-php-client", 3 | "description": "An Open Brewery DB API client for PHP.", 4 | "type": "library", 5 | "license": "MIT", 6 | "version": "0.9.0", 7 | "homepage": "https://github.com/joeymckenzie/openbrewerydb-php-client", 8 | "autoload": { 9 | "psr-4": { 10 | "OpenBreweryDb\\": "src/" 11 | }, 12 | "files": [ 13 | "src/OpenBreweryDb.php" 14 | ] 15 | }, 16 | "autoload-dev": { 17 | "psr-4": { 18 | "Tests\\": "tests/" 19 | } 20 | }, 21 | "authors": [ 22 | { 23 | "name": "Joey McKenzie", 24 | "email": "joey.mckenzie27@gmail.com", 25 | "homepage": "https://github.com/joeymckenzie" 26 | } 27 | ], 28 | "minimum-stability": "stable", 29 | "require": { 30 | "php": ">=8.3", 31 | "php-http/discovery": "^1.19.2", 32 | "php-http/multipart-stream-builder": "^1.3.0", 33 | "psr/http-client": "^1.0.3", 34 | "psr/http-client-implementation": "*", 35 | "psr/http-factory-implementation": "*", 36 | "psr/http-message": "^2.0.0" 37 | }, 38 | "require-dev": { 39 | "guzzlehttp/guzzle": "^7.8", 40 | "laravel/pint": "^1.13", 41 | "pestphp/pest": "^2.32", 42 | "pestphp/pest-plugin-type-coverage": "^2.8", 43 | "pestphp/pest-plugin-watch": "^2.0", 44 | "phpstan/phpstan": "^1.10", 45 | "phpstan/phpstan-strict-rules": "^1.5", 46 | "rector/rector": "^1.0", 47 | "symfony/http-client": "^7.0" 48 | }, 49 | "scripts": { 50 | "test:integration": "./vendor/bin/pest --parallel --colors=always", 51 | "test:watch": "./vendor/bin/pest --watch --parallel", 52 | "test:types": "./vendor/bin/pest --type-coverage --min=100", 53 | "test": [ 54 | "@test:types", 55 | "@test:integration" 56 | ], 57 | "lint": "./vendor/bin/phpstan analyze", 58 | "fmt": "./vendor/bin/pint -v", 59 | "check": "./vendor/bin/pint --test", 60 | "prepare": "git config core.hookspath .githooks", 61 | "ci": [ 62 | "@check", 63 | "@rector:dry", 64 | "@lint", 65 | "@test" 66 | ], 67 | "rector": "vendor/bin/rector process", 68 | "rector:dry": "vendor/bin/rector process --dry-run", 69 | "refactor": [ 70 | "@rector", 71 | "@fmt" 72 | ] 73 | }, 74 | "config": { 75 | "allow-plugins": { 76 | "pestphp/pest-plugin": true, 77 | "php-http/discovery": true 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /examples/builder.php: -------------------------------------------------------------------------------- 1 | 5, 19 | ]); 20 | 21 | $openBreweryDbClient = OpenBreweryDb::builder() 22 | ->withHttpClient($guzzleClient) 23 | ->withHeader('foo', 'bar') 24 | ->build(); 25 | 26 | // Get a list of breweries, based on all types of different search criteria 27 | $breweries = $openBreweryDbClient->breweries()->list([ 28 | 'by_city' => 'Sacramento', 29 | ]); 30 | var_dump($breweries); 31 | 32 | // Retrieve various metadata about breweries from the API 33 | $metadata = $openBreweryDbClient->breweries()->metadata(); 34 | var_dump($metadata); 35 | 36 | // Get a random brewery with a specified page size 37 | $randomBrewery = $openBreweryDbClient->breweries()->random(5); 38 | var_dump($randomBrewery); 39 | 40 | /** 41 | * Since we're not limited to a specific HTTP client, we can mix and match 42 | * depending on what client you have installed or want to use. 43 | */ 44 | $symfonyClient = (new Symfony\Component\HttpClient\Psr18Client())->withOptions([ 45 | 'headers' => ['symfony' => 'is-awesome'], 46 | ]); 47 | 48 | $openBreweryDbClientWithSymfony = OpenBreweryDb::builder() 49 | ->withHttpClient($symfonyClient) 50 | ->withHeader('foo', 'bar') 51 | ->build(); 52 | 53 | // Get a list of breweries, based on all types of different search criteria 54 | $breweries = $openBreweryDbClientWithSymfony->breweries()->list([ 55 | 'by_city' => 'Sacramento', 56 | ]); 57 | var_dump($breweries); 58 | 59 | // Retrieve various metadata about breweries from the API 60 | $metadata = $openBreweryDbClientWithSymfony->breweries()->metadata(); 61 | var_dump($metadata); 62 | 63 | // Get a random brewery with a specified page size 64 | $randomBrewery = $openBreweryDbClientWithSymfony->breweries()->random(5); 65 | var_dump($randomBrewery); 66 | -------------------------------------------------------------------------------- /examples/simple.php: -------------------------------------------------------------------------------- 1 | breweries()->list([ 13 | 'by_city' => 'Sacramento', 14 | ]); 15 | var_dump($breweries); 16 | 17 | // Retrieve various metadata about breweries from the API 18 | $metadata = $client->breweries()->metadata(); 19 | var_dump($metadata); 20 | 21 | // Get a random brewery with a specified page size 22 | $randomBrewery = $client->breweries()->random(5); 23 | var_dump($randomBrewery); 24 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | default: lint 2 | 3 | # Continuously test and lint, helpful for local development 4 | dev: 5 | just lint & just test 6 | 7 | # Check types on any file change 8 | lint: 9 | find src/ tests/ | entr -s 'composer run lint' 10 | 11 | # Run tests in parallel 12 | test: 13 | find src/ tests/ | entr -s 'composer run test' 14 | 15 | # Run tests in parallel 16 | fmt: 17 | find src/ tests/ | entr -s 'composer run refactor' 18 | -------------------------------------------------------------------------------- /phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | includes: 2 | - vendor/phpstan/phpstan-strict-rules/rules.neon 3 | 4 | parameters: 5 | level: max 6 | paths: 7 | - src 8 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | ./tests 10 | 11 | 12 | 13 | 14 | ./src 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /pint.json: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "laravel", 3 | "rules": { 4 | "array_push": true, 5 | "backtick_to_shell_exec": true, 6 | "declare_strict_types": true, 7 | "final_class": true, 8 | "fully_qualified_strict_types": true, 9 | "global_namespace_import": { 10 | "import_classes": true, 11 | "import_constants": true, 12 | "import_functions": true 13 | }, 14 | "ordered_class_elements": { 15 | "order": [ 16 | "use_trait", 17 | "case", 18 | "constant", 19 | "constant_public", 20 | "constant_protected", 21 | "constant_private", 22 | "property_public", 23 | "property_protected", 24 | "property_private", 25 | "construct", 26 | "destruct", 27 | "magic", 28 | "phpunit", 29 | "method_abstract", 30 | "method_public_static", 31 | "method_public", 32 | "method_protected_static", 33 | "method_protected", 34 | "method_private_static", 35 | "method_private" 36 | ], 37 | "sort_algorithm": "none" 38 | }, 39 | "ordered_interfaces": true, 40 | "ordered_traits": true 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /rector.php: -------------------------------------------------------------------------------- 1 | withPaths([ 12 | __DIR__.'/src', 13 | ]) 14 | ->withPhpSets(php83: true) 15 | ->withRules([ 16 | AddVoidReturnTypeWhereNoReturnRector::class, 17 | InlineConstructorDefaultToPropertyRector::class, 18 | ]) 19 | ->withSets([ 20 | SetList::CODE_QUALITY, 21 | SetList::DEAD_CODE, 22 | SetList::EARLY_RETURN, 23 | SetList::TYPE_DECLARATION, 24 | SetList::PRIVATIZATION, 25 | ]); 26 | -------------------------------------------------------------------------------- /src/Builder.php: -------------------------------------------------------------------------------- 1 | 28 | */ 29 | private array $headers = []; 30 | 31 | /** 32 | * The query parameters to be included on each outgoing request. 33 | * 34 | * @var array 35 | */ 36 | private array $queryParams = []; 37 | 38 | /** 39 | * Sets the HTTP client for the requests. If no client is provided the 40 | * factory will try to find one using PSR-18 HTTP Client Discovery. 41 | */ 42 | public function withHttpClient(ClientInterface $client): self 43 | { 44 | $this->httpClient = $client; 45 | 46 | return $this; 47 | } 48 | 49 | /** 50 | * Adds a custom header to each outgoing request. 51 | */ 52 | public function withHeader(string $name, string $value): self 53 | { 54 | $this->headers[$name] = $value; 55 | 56 | return $this; 57 | } 58 | 59 | /** 60 | * Adds a custom query parameter to the request url for each outgoing request. 61 | */ 62 | public function withQueryParam(string $name, string $value): self 63 | { 64 | $this->queryParams[$name] = $value; 65 | 66 | return $this; 67 | } 68 | 69 | /** 70 | * Creates a new Open Brewery DB client based on the provided builder options. 71 | */ 72 | public function build(): Client 73 | { 74 | $headers = Headers::create(); 75 | 76 | // For any default headers configured for the client, we'll add those to each outbound request 77 | foreach ($this->headers as $name => $value) { 78 | $headers = $headers->withCustomHeader($name, $value); 79 | } 80 | 81 | $baseUri = BaseUri::from(Client::API_BASE_URL); 82 | $queryParams = QueryParams::create(); 83 | 84 | // As with the headers, we'll also include any query params configured on each request 85 | foreach ($this->queryParams as $name => $value) { 86 | $queryParams = $queryParams->withParam($name, $value); 87 | } 88 | 89 | $client = $this->httpClient ??= Psr18ClientDiscovery::find(); 90 | $connector = new Connector($client, $baseUri, $headers, $queryParams); 91 | 92 | return new Client($connector); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/Client.php: -------------------------------------------------------------------------------- 1 | connector); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Contracts/Concerns/Arrayable.php: -------------------------------------------------------------------------------- 1 | > 24 | * 25 | * @throws ErrorException|UnserializableResponseException|ConnectorException 26 | */ 27 | public function requestData(Payload $payload): Response; 28 | } 29 | -------------------------------------------------------------------------------- /src/Contracts/Resources/BreweriesContract.php: -------------------------------------------------------------------------------- 1 | $parameters 29 | * 30 | * @see https://openbrewerydb.org/documentation#search-breweries 31 | */ 32 | public function list(array $parameters = []): ListResponse; 33 | 34 | /** 35 | * Retrieves one or more random breweries. 36 | * 37 | * @see https://openbrewerydb.org/documentation#random 38 | */ 39 | public function random(int $size = 1): ListResponse; 40 | 41 | /** 42 | * Searches for breweries that meet the query criteria. 43 | * 44 | * @see https://openbrewerydb.org/documentation#list-breweries 45 | */ 46 | public function search(string $query, int $perPage = Client::PER_PAGE): ListResponse; 47 | 48 | /** 49 | * Lists breweries with only ID and name based on the query criteria, useful for drop down lists and the like. 50 | * 51 | * @see https://openbrewerydb.org/documentation#autocomplete 52 | */ 53 | public function autocomplete(string $query): AutocompleteResponse; 54 | 55 | /** 56 | * Lists metadata about breweries based on the optional query criteria. 57 | * 58 | * @param array $parameters 59 | * 60 | * @see https://openbrewerydb.org/documentation#autocomplete 61 | */ 62 | public function metadata(array $parameters = []): MetadataResponse; 63 | } 64 | -------------------------------------------------------------------------------- /src/Contracts/ResponseContract.php: -------------------------------------------------------------------------------- 1 | , value-of> 16 | * @extends Arrayable 17 | * 18 | * @internal 19 | */ 20 | interface ResponseContract extends Arrayable, ArrayAccess 21 | { 22 | /** 23 | * @param key-of $offset 24 | */ 25 | public function offsetExists(mixed $offset): bool; 26 | 27 | /** 28 | * @template TOffsetKey of key-of 29 | * 30 | * @param TOffsetKey $offset 31 | * @return TArray[TOffsetKey] 32 | */ 33 | public function offsetGet(mixed $offset): mixed; 34 | 35 | /** 36 | * @template TOffsetKey of key-of 37 | * 38 | * @param TOffsetKey|null $offset 39 | * @param TArray[TOffsetKey] $value 40 | */ 41 | public function offsetSet(mixed $offset, mixed $value): never; 42 | 43 | /** 44 | * @template TOffsetKey of key-of 45 | * 46 | * @param TOffsetKey $offset 47 | */ 48 | public function offsetUnset(mixed $offset): never; 49 | } 50 | -------------------------------------------------------------------------------- /src/Enums/HttpMethod.php: -------------------------------------------------------------------------------- 1 | getMessage(), 0, $exception); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Exceptions/ErrorException.php: -------------------------------------------------------------------------------- 1 | getMessage(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Exceptions/UnserializableResponseException.php: -------------------------------------------------------------------------------- 1 | getMessage(), 0, $exception); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Http/Connector.php: -------------------------------------------------------------------------------- 1 | toRequest($this->baseUri, $this->headers, $this->queryParams); 52 | $response = $this->sendRequest(fn (): ResponseInterface => $this->client->sendRequest($request)); 53 | $contents = $response->getBody()->getContents(); 54 | 55 | $this->throwIfJsonError($response, $contents); 56 | 57 | try { 58 | $data = json_decode($contents, true, flags: JSON_THROW_ON_ERROR); 59 | } catch (JsonException $jsonException) { 60 | throw new UnserializableResponseException($jsonException); 61 | } 62 | 63 | // @phpstan-ignore-next-line: we'll assume the $data in the response model is a valid model 64 | return Response::from($data); 65 | } 66 | 67 | /** 68 | * Sends the composed request to the server. 69 | * 70 | * @throws ConnectorException|ErrorException|UnserializableResponseException 71 | */ 72 | private function sendRequest(Closure $callable): ResponseInterface 73 | { 74 | try { 75 | return $callable(); 76 | } catch (ClientExceptionInterface $clientException) { 77 | if ($clientException instanceof ClientException) { 78 | $this->throwIfJsonError($clientException->getResponse(), $clientException->getResponse()->getBody()->getContents()); 79 | } 80 | 81 | throw new ConnectorException($clientException); 82 | } 83 | } 84 | 85 | /** 86 | * Analyzes the current error response to determine if the server sent us something we cannot deserialize. 87 | * 88 | * @throws ErrorException|UnserializableResponseException 89 | */ 90 | private function throwIfJsonError(ResponseInterface $response, string|ResponseInterface $contents): void 91 | { 92 | // If we received a successful status despite sending the request throwing an exception, 93 | // bypass the checking for unserializable responses and propagate an connector exception 94 | if ($response->getStatusCode() < 400) { 95 | return; 96 | } 97 | 98 | // In the case the content type returned from the service is not JSON, bypass checking 99 | if (! str_contains($response->getHeaderLine('Content-Type'), MediaType::JSON->value)) { 100 | return; 101 | } 102 | 103 | if ($contents instanceof ResponseInterface) { 104 | $contents = $contents->getBody()->getContents(); 105 | } 106 | 107 | try { 108 | /** @var array{message: ?string} $response */ 109 | $response = json_decode($contents, true, flags: JSON_THROW_ON_ERROR); 110 | 111 | // Open Brewery DB will send back a "message" property in the JSON, so we'll 112 | // throw whatever is returned in those cases that it's detected on the response 113 | if (isset($response['message'])) { 114 | throw new ErrorException($response['message']); 115 | } 116 | } catch (JsonException $jsonException) { 117 | throw new UnserializableResponseException($jsonException); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/OpenBreweryDb.php: -------------------------------------------------------------------------------- 1 | withHeader('User-Agent', "openbrewerydb-php-client/$version") 24 | ->build(); 25 | } 26 | 27 | /** 28 | * Creates a new client builder to configure with custom options. 29 | */ 30 | public static function builder(): Builder 31 | { 32 | return new Builder(); 33 | } 34 | 35 | /** 36 | * Gets the current version information for the library. 37 | */ 38 | public function getVersion(): string 39 | { 40 | return Version::current(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Resources/Breweries.php: -------------------------------------------------------------------------------- 1 | $response 37 | */ 38 | $response = $this->connector->requestData($payload); 39 | 40 | return FindResponse::from($response->data()); 41 | } 42 | 43 | /** 44 | * {@inheritDoc} 45 | */ 46 | #[Override] 47 | public function random(int $size = 1): ListResponse 48 | { 49 | $parameters = [ 50 | 'size' => $size, 51 | ]; 52 | 53 | $payload = Payload::list('breweries', $parameters, 'random'); 54 | 55 | /** 56 | * @var Response> $response 57 | */ 58 | $response = $this->connector->requestData($payload); 59 | 60 | return ListResponse::from($response->data()); 61 | } 62 | 63 | /** 64 | * {@inheritDoc} 65 | */ 66 | #[Override] 67 | public function list(array $parameters = []): ListResponse 68 | { 69 | $payload = Payload::list('breweries', $parameters); 70 | 71 | /** 72 | * @var Response> $response 73 | */ 74 | $response = $this->connector->requestData($payload); 75 | 76 | return ListResponse::from($response->data()); 77 | } 78 | 79 | /** 80 | * {@inheritDoc} 81 | */ 82 | #[Override] 83 | public function search(string $query, int $perPage = Client::PER_PAGE): ListResponse 84 | { 85 | $parameters = [ 86 | 'per_page' => $perPage, 87 | 'query' => urlencode($query), 88 | ]; 89 | 90 | $payload = Payload::list('breweries', $parameters, 'search'); 91 | 92 | /** 93 | * @var Response> $response 94 | */ 95 | $response = $this->connector->requestData($payload); 96 | 97 | return ListResponse::from($response->data()); 98 | } 99 | 100 | /** 101 | * {@inheritDoc} 102 | */ 103 | #[Override] 104 | public function autocomplete(string $query): AutocompleteResponse 105 | { 106 | $parameters = [ 107 | 'query' => urlencode($query), 108 | ]; 109 | 110 | $payload = Payload::list('breweries', $parameters, 'autocomplete'); 111 | 112 | /** 113 | * @var Response> $response 114 | */ 115 | $response = $this->connector->requestData($payload); 116 | 117 | return AutocompleteResponse::from($response->data()); 118 | } 119 | 120 | /** 121 | * {@inheritDoc} 122 | */ 123 | #[Override] 124 | public function metadata(array $parameters = []): MetadataResponse 125 | { 126 | $payload = Payload::list('breweries', $parameters, 'meta'); 127 | 128 | /** 129 | * @var Response> $response 130 | */ 131 | $response = $this->connector->requestData($payload); 132 | 133 | return MetadataResponse::from($response->data()); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/Responses/Breweries/AutocompleteResponse.php: -------------------------------------------------------------------------------- 1 | > 18 | */ 19 | final readonly class AutocompleteResponse implements ResponseContract 20 | { 21 | /** 22 | * @use ArrayAccessible> 23 | */ 24 | use ArrayAccessible; 25 | 26 | /** 27 | * @param array $data 28 | */ 29 | private function __construct(public array $data) 30 | { 31 | } 32 | 33 | /** 34 | * Acts as static factory, and returns a new Response instance. 35 | * 36 | * @param array $attributes 37 | */ 38 | public static function from(array $attributes): self 39 | { 40 | return new self($attributes); 41 | } 42 | 43 | /** 44 | * {@inheritDoc} 45 | */ 46 | #[Override] 47 | public function toArray(): array 48 | { 49 | return $this->data; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Responses/Breweries/FindResponse.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | final readonly class FindResponse implements ResponseContract 19 | { 20 | /** 21 | * @use ArrayAccessible 22 | */ 23 | use ArrayAccessible; 24 | 25 | private function __construct( 26 | public string $id, 27 | public string $name, 28 | public ?string $brewery_type, 29 | public ?string $address_1, 30 | public ?string $address_2, 31 | public ?string $address_3, 32 | public string $city, 33 | public string $state_province, 34 | public string $postal_code, 35 | public string $country, 36 | public ?string $longitude, 37 | public ?string $latitude, 38 | public ?string $phone, 39 | public ?string $website_url, 40 | public string $state, 41 | public ?string $street, 42 | ) { 43 | } 44 | 45 | /** 46 | * Acts as static factory, and returns a new Response instance. 47 | * 48 | * @param array{id: string, name: string, brewery_type: ?string, address_1: ?string, address_2: ?string, address_3: ?string, city: string, state_province: string, postal_code: string, country: string, longitude: ?string, latitude: ?string, phone: ?string, website_url: ?string, state: string, street: ?string} $attributes 49 | */ 50 | public static function from(array $attributes): self 51 | { 52 | return new self( 53 | $attributes['id'], 54 | $attributes['name'], 55 | $attributes['brewery_type'], 56 | $attributes['address_1'], 57 | $attributes['address_2'], 58 | $attributes['address_3'], 59 | $attributes['city'], 60 | $attributes['state_province'], 61 | $attributes['postal_code'], 62 | $attributes['country'], 63 | $attributes['longitude'], 64 | $attributes['latitude'], 65 | $attributes['phone'], 66 | $attributes['website_url'], 67 | $attributes['state'], 68 | $attributes['street'], 69 | ); 70 | } 71 | 72 | /** 73 | * {@inheritDoc} 74 | */ 75 | #[Override] 76 | public function toArray(): array 77 | { 78 | return [ 79 | 'id' => $this->id, 80 | 'name' => $this->name, 81 | 'brewery_type' => $this->brewery_type, 82 | 'address_1' => $this->address_1, 83 | 'address_2' => $this->address_2, 84 | 'address_3' => $this->address_3, 85 | 'city' => $this->city, 86 | 'state_province' => $this->state_province, 87 | 'postal_code' => $this->postal_code, 88 | 'country' => $this->country, 89 | 'longitude' => $this->longitude, 90 | 'latitude' => $this->latitude, 91 | 'phone' => $this->phone, 92 | 'website_url' => $this->website_url, 93 | 'state' => $this->state, 94 | 'street' => $this->street, 95 | ]; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/Responses/Breweries/ListResponse.php: -------------------------------------------------------------------------------- 1 | > 18 | */ 19 | final readonly class ListResponse implements ResponseContract 20 | { 21 | /** 22 | * @use ArrayAccessible> 23 | */ 24 | use ArrayAccessible; 25 | 26 | /** 27 | * @param FindResponse[] $data 28 | */ 29 | private function __construct(public array $data) 30 | { 31 | } 32 | 33 | /** 34 | * Acts as static factory, and returns a new Response instance. 35 | * 36 | * @param array $attributes 37 | */ 38 | public static function from(array $attributes): self 39 | { 40 | $mappedData = array_map(fn (array $result): FindResponse => FindResponse::from( 41 | $result, 42 | ), $attributes); 43 | 44 | return new self($mappedData); 45 | } 46 | 47 | /** 48 | * {@inheritDoc} 49 | */ 50 | #[Override] 51 | public function toArray(): array 52 | { 53 | return array_map( 54 | static fn (FindResponse $response): array => $response->toArray(), 55 | $this->data, 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Responses/Breweries/MetadataResponse.php: -------------------------------------------------------------------------------- 1 | > 17 | */ 18 | final readonly class MetadataResponse implements ResponseContract 19 | { 20 | /** 21 | * @use ArrayAccessible> 22 | */ 23 | use ArrayAccessible; 24 | 25 | /** 26 | * @param array $data 27 | */ 28 | private function __construct(public array $data) 29 | { 30 | } 31 | 32 | /** 33 | * Acts as static factory, and returns a new Response instance. 34 | * 35 | * @param array $attributes 36 | */ 37 | public static function from(array $attributes): self 38 | { 39 | return new self($attributes); 40 | } 41 | 42 | /** 43 | * {@inheritDoc} 44 | */ 45 | #[Override] 46 | public function toArray(): array 47 | { 48 | return $this->data; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Responses/Concerns/ArrayAccessible.php: -------------------------------------------------------------------------------- 1 | 16 | * 17 | * @internal 18 | */ 19 | trait ArrayAccessible 20 | { 21 | /** 22 | * {@inheritDoc} 23 | */ 24 | public function offsetExists(mixed $offset): bool 25 | { 26 | return array_key_exists($offset, $this->toArray()); 27 | } 28 | 29 | /** 30 | * {@inheritDoc} 31 | */ 32 | public function offsetGet(mixed $offset): mixed 33 | { 34 | return $this->toArray()[$offset]; 35 | } 36 | 37 | /** 38 | * {@inheritDoc} 39 | */ 40 | public function offsetSet(mixed $offset, mixed $value): never 41 | { 42 | throw new BadMethodCallException('Responses are immutable. Values are not allowed to be set on responses.'); 43 | } 44 | 45 | /** 46 | * {@inheritDoc} 47 | */ 48 | public function offsetUnset(mixed $offset): never 49 | { 50 | throw new BadMethodCallException('Responses are immutable. Values are not allowed to be removed on responses.'); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/ValueObjects/Connector/BaseUri.php: -------------------------------------------------------------------------------- 1 | baseUri, $protocol)) { 29 | return "$this->baseUri/"; 30 | } 31 | } 32 | 33 | return "https://$this->baseUri/"; 34 | } 35 | 36 | /** 37 | * Creates a new Base URI value object. 38 | */ 39 | public static function from(string $baseUri): self 40 | { 41 | return new self($baseUri); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/ValueObjects/Connector/Headers.php: -------------------------------------------------------------------------------- 1 | > 15 | * 16 | * @internal 17 | */ 18 | final readonly class Headers implements Arrayable 19 | { 20 | /** 21 | * Creates a new Headers value object. 22 | * 23 | * @param array $headers 24 | */ 25 | private function __construct(private array $headers) 26 | { 27 | } 28 | 29 | /** 30 | * Creates a new Headers value object. 31 | */ 32 | public static function create(): self 33 | { 34 | return new self([]); 35 | } 36 | 37 | /** 38 | * Creates a new Headers value object, with the given content type, and the existing headers. 39 | */ 40 | public function withAccept(MediaType $mediaType, string $suffix = ''): self 41 | { 42 | return new self([ 43 | ...$this->headers, 44 | 'Accept' => $mediaType->value.$suffix, 45 | ]); 46 | } 47 | 48 | /** 49 | * Creates a new Headers value object, with the newly added header, and the existing headers. 50 | */ 51 | public function withCustomHeader(string $name, string $value): self 52 | { 53 | return new self([ 54 | ...$this->headers, 55 | $name => $value, 56 | ]); 57 | } 58 | 59 | #[Override] 60 | public function toArray(): array 61 | { 62 | return $this->headers; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/ValueObjects/Connector/QueryParams.php: -------------------------------------------------------------------------------- 1 | > 14 | * 15 | * @internal 16 | */ 17 | final readonly class QueryParams implements Arrayable 18 | { 19 | /** 20 | * Creates a new Query Params value object. 21 | * 22 | * @param array $params 23 | */ 24 | private function __construct(private array $params) 25 | { 26 | } 27 | 28 | /** 29 | * Creates a new Query Params value object. 30 | */ 31 | public static function create(): self 32 | { 33 | return new self([]); 34 | } 35 | 36 | /** 37 | * Creates a new Query Params value object, with the newly added param, and the existing params. 38 | */ 39 | public function withParam(string $name, string|int $value): self 40 | { 41 | return new self([ 42 | ...$this->params, 43 | $name => $value, 44 | ]); 45 | } 46 | 47 | #[Override] 48 | public function toArray(): array 49 | { 50 | return $this->params; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/ValueObjects/Connector/Response.php: -------------------------------------------------------------------------------- 1 | 32 | */ 33 | public static function from(array $attributes): self 34 | { 35 | return new self($attributes); 36 | } 37 | 38 | /** 39 | * Returns the response data. 40 | * 41 | * @return TData 42 | */ 43 | public function data(): mixed 44 | { 45 | return $this->data; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/ValueObjects/Payload.php: -------------------------------------------------------------------------------- 1 | $parameters 26 | */ 27 | private function __construct( 28 | private MediaType $accept, 29 | private HttpMethod $method, 30 | private ResourceUri $uri, 31 | private array $parameters = [], 32 | ) { 33 | } 34 | 35 | /** 36 | * Creates a new Payload value object from the given parameters. 37 | * 38 | * @param array $parameters 39 | */ 40 | public static function list(string $resource, array $parameters = [], ?string $suffix = null): self 41 | { 42 | $accept = MediaType::JSON; 43 | $method = HttpMethod::GET; 44 | $uri = ResourceUri::list($resource, $suffix); 45 | 46 | return new self($accept, $method, $uri, $parameters); 47 | } 48 | 49 | /** 50 | * Creates a new Payload value object from the given parameters. 51 | * 52 | * @param array $parameters 53 | */ 54 | public static function retrieve(string $resource, string $id, array $parameters = []): self 55 | { 56 | $accept = MediaType::JSON; 57 | $method = HttpMethod::GET; 58 | $uri = ResourceUri::retrieve($resource, $id); 59 | 60 | return new self($accept, $method, $uri, $parameters); 61 | } 62 | 63 | /** 64 | * Creates a new Psr 7 Request instance based on information passed on the request payload. 65 | * In the case of query parameters, if the client is constructed with any parameters, 66 | * we'll append them to each request that is sent to the server. 67 | */ 68 | public function toRequest(BaseUri $baseUri, Headers $headers, QueryParams $queryParams): RequestInterface 69 | { 70 | $psr17Factory = new Psr17Factory(); 71 | $uri = "$baseUri$this->uri"; 72 | $queryParams = [...$queryParams->toArray(), ...$this->parameters]; 73 | 74 | if ($queryParams !== []) { 75 | $uri .= '?'.http_build_query($queryParams); 76 | } 77 | 78 | $headers = $headers->withAccept($this->accept); 79 | $request = $psr17Factory->createRequest($this->method->value, $uri); 80 | 81 | foreach ($headers->toArray() as $name => $value) { 82 | $request = $request->withHeader($name, $value); 83 | } 84 | 85 | return $request; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/ValueObjects/ResourceUri.php: -------------------------------------------------------------------------------- 1 | uri; 28 | } 29 | 30 | /** 31 | * Creates a new resource URI value object that lists the given resource. 32 | */ 33 | public static function list(string $resource, ?string $suffix = null): self 34 | { 35 | $uri = isset($suffix) 36 | ? "$resource/$suffix" 37 | : $resource; 38 | 39 | return new self($uri); 40 | } 41 | 42 | /** 43 | * Creates a new resource URI value object that retrieves the given resource. 44 | */ 45 | public static function retrieve(string $resource, string $id): self 46 | { 47 | return new self("$resource/$id"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/ValueObjects/Version.php: -------------------------------------------------------------------------------- 1 | expect('OpenBreweryDb\\') 9 | ->toUseStrictTypes(); 10 | 11 | test('All tests files are strictly typed') 12 | ->expect('Tests\\') 13 | ->toUseStrictTypes(); 14 | 15 | test('Value objects should be immutable') 16 | ->expect('OpenBreweryDb\\ValueObjects\\') 17 | ->toBeFinal() 18 | ->and('OpenBreweryDb\\ValueObjects\\') 19 | ->toBeReadonly(); 20 | 21 | test('Responses should be immutable') 22 | ->expect('OpenBreweryDb\\Responses\\Breweries\\') 23 | ->toBeFinal() 24 | ->and('OpenBreweryDb\\Responses\\Breweries\\') 25 | ->toBeReadonly(); 26 | 27 | test('Contracts should be abstract') 28 | ->expect('OpenBreweryDb\\Contracts\\') 29 | ->toBeInterfaces(); 30 | 31 | test('All Enums are backed') 32 | ->expect('OpenBreweryDb\\Enums\\') 33 | ->toBeStringBackedEnums(); 34 | -------------------------------------------------------------------------------- /tests/Pest.php: -------------------------------------------------------------------------------- 1 | breweries()->autocomplete('dog'); 16 | 17 | // Assert 18 | expect(count($breweries->toArray()))->toBe(15); 19 | foreach ($breweries->toArray() as $brewery) { 20 | expect($brewery)->not()->toBeNull() 21 | ->and($brewery['id'])->not()->toBeNull() 22 | ->and($brewery['name'])->not()->toBeNull() 23 | ->and(strtolower($brewery['name']))->toContain('dog'); 24 | } 25 | }); 26 | 27 | it('returns no breweries for autocomplete if the search criteria is not met', function () { 28 | // Arrange 29 | $client = OpenBreweryDb::client(); 30 | 31 | // Act 32 | $breweries = $client->breweries()->autocomplete('pneumonoultramicroscopicsilicovolcanoconiosis'); 33 | 34 | // Assert 35 | expect($breweries->toArray())->toBeEmpty(); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /tests/Resources/FindBrewery.php: -------------------------------------------------------------------------------- 1 | breweries()->find('b54b16e1-ac3b-4bff-a11f-f7ae9ddc27e0'); 17 | 18 | // Assert 19 | expect($brewery)->not()->toBeNull() 20 | ->and($brewery['id'])->not()->toBeNull() 21 | ->and($brewery['brewery_type'])->not()->toBeNull() 22 | ->and($brewery['state'])->not()->toBeNull() 23 | ->and($brewery['state_province'])->not()->toBeNull() 24 | ->and($brewery['postal_code'])->not()->toBeNull() 25 | ->and($brewery['country'])->not()->toBeNull() 26 | ->and($brewery['city'])->not()->toBeNull() 27 | ->and($brewery['name'])->not()->toBeNull(); 28 | }); 29 | 30 | it('returns an error if no brewery is found', function () { 31 | // Arrange 32 | $client = OpenBreweryDb::client(); 33 | 34 | // Act/Assert 35 | expect(fn () => $client->breweries()->find('not-a-brewery'))->toThrow(ErrorException::class, 'Couldn\'t find Brewery'); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /tests/Resources/ListBreweries.php: -------------------------------------------------------------------------------- 1 | breweries()->list(); 17 | 18 | // Assert 19 | expect(count($breweries->toArray()))->toBe(Client::PER_PAGE); 20 | foreach ($breweries->toArray() as $brewery) { 21 | expect($brewery)->not()->toBeNull() 22 | ->and($brewery['id'])->not()->toBeNull() 23 | ->and($brewery['brewery_type'])->not()->toBeNull() 24 | ->and($brewery['state'])->not()->toBeNull() 25 | ->and($brewery['state_province'])->not()->toBeNull() 26 | ->and($brewery['postal_code'])->not()->toBeNull() 27 | ->and($brewery['country'])->not()->toBeNull() 28 | ->and($brewery['city'])->not()->toBeNull() 29 | ->and($brewery['name'])->not()->toBeNull(); 30 | } 31 | }); 32 | 33 | it('returns a valid list of breweries with properties set to the number of paginated results', function () { 34 | // Arrange 35 | $client = OpenBreweryDb::client(); 36 | $pagedResults = rand(50, 200); 37 | 38 | // Act 39 | $breweries = $client->breweries()->list([ 40 | 'per_page' => $pagedResults, 41 | ]); 42 | 43 | // Assert 44 | expect(count($breweries->toArray()))->toBe($pagedResults); 45 | foreach ($breweries->toArray() as $brewery) { 46 | expect($brewery)->not()->toBeNull() 47 | ->and($brewery['id'])->not()->toBeNull() 48 | ->and($brewery['brewery_type'])->not()->toBeNull() 49 | ->and($brewery['state'])->not()->toBeNull() 50 | ->and($brewery['state_province'])->not()->toBeNull() 51 | ->and($brewery['postal_code'])->not()->toBeNull() 52 | ->and($brewery['country'])->not()->toBeNull() 53 | ->and($brewery['city'])->not()->toBeNull() 54 | ->and($brewery['name'])->not()->toBeNull(); 55 | } 56 | }); 57 | 58 | it('retrieves a list of breweries by name', function () { 59 | // Arrange 60 | $client = OpenBreweryDb::client(); 61 | 62 | // Act 63 | $breweries = $client->breweries()->list([ 64 | 'by_name' => 'Dog', 65 | ]); 66 | 67 | // Assert 68 | expect($breweries->toArray())->toBeGreaterThan(1); 69 | foreach ($breweries->toArray() as $brewery) { 70 | expect($brewery)->not()->toBeNull() 71 | ->and($brewery['id'])->not()->toBeNull() 72 | ->and($brewery['brewery_type'])->not()->toBeNull() 73 | ->and($brewery['state'])->not()->toBeNull() 74 | ->and($brewery['state_province'])->not()->toBeNull() 75 | ->and($brewery['postal_code'])->not()->toBeNull() 76 | ->and($brewery['country'])->not()->toBeNull() 77 | ->and($brewery['city'])->not()->toBeNull() 78 | ->and($brewery['name'])->not()->toBeNull() 79 | ->and(strtolower($brewery['name']))->toContain('dog'); 80 | } 81 | }); 82 | 83 | it('retrieves a list of breweries by state', function () { 84 | // Arrange 85 | $client = OpenBreweryDb::client(); 86 | 87 | // Act 88 | $breweries = $client->breweries()->list([ 89 | 'by_state' => 'Oregon', 90 | ]); 91 | 92 | // Assert 93 | expect($breweries->toArray())->toBeGreaterThan(1); 94 | foreach ($breweries->toArray() as $brewery) { 95 | expect($brewery)->not()->toBeNull() 96 | ->and($brewery['id'])->not()->toBeNull() 97 | ->and($brewery['brewery_type'])->not()->toBeNull() 98 | ->and($brewery['state'])->not()->toBeNull() 99 | ->and($brewery['state_province'])->not()->toBeNull() 100 | ->and($brewery['postal_code'])->not()->toBeNull() 101 | ->and($brewery['country'])->not()->toBeNull() 102 | ->and($brewery['city'])->not()->toBeNull() 103 | ->and($brewery['name'])->not()->toBeNull() 104 | ->and($brewery['state'])->toBe('Oregon'); 105 | } 106 | }); 107 | 108 | it('retrieves a list of breweries by multiple state names', function () { 109 | // Arrange 110 | $client = OpenBreweryDb::client(); 111 | 112 | // Act 113 | $breweries = $client->breweries()->list([ 114 | 'by_state' => 'New York', 115 | ]); 116 | 117 | // Assert 118 | expect($breweries->toArray())->toBeGreaterThan(1); 119 | foreach ($breweries->toArray() as $brewery) { 120 | expect($brewery)->not()->toBeNull() 121 | ->and($brewery['id'])->not()->toBeNull() 122 | ->and($brewery['brewery_type'])->not()->toBeNull() 123 | ->and($brewery['state'])->not()->toBeNull() 124 | ->and($brewery['state_province'])->not()->toBeNull() 125 | ->and($brewery['postal_code'])->not()->toBeNull() 126 | ->and($brewery['country'])->not()->toBeNull() 127 | ->and($brewery['city'])->not()->toBeNull() 128 | ->and($brewery['name'])->not()->toBeNull() 129 | ->and($brewery['state'])->toBe('New York'); 130 | } 131 | }); 132 | 133 | it('retrieves a list of breweries by type', function () { 134 | // Arrange 135 | $client = OpenBreweryDb::client(); 136 | 137 | // Act 138 | $breweries = $client->breweries()->list([ 139 | 'by_type' => 'micro', 140 | ]); 141 | 142 | // Assert 143 | expect($breweries->toArray())->toBeGreaterThan(1); 144 | foreach ($breweries->toArray() as $brewery) { 145 | expect($brewery)->not()->toBeNull() 146 | ->and($brewery['id'])->not()->toBeNull() 147 | ->and($brewery['brewery_type'])->not()->toBeNull() 148 | ->and($brewery['state'])->not()->toBeNull() 149 | ->and($brewery['state_province'])->not()->toBeNull() 150 | ->and($brewery['postal_code'])->not()->toBeNull() 151 | ->and($brewery['country'])->not()->toBeNull() 152 | ->and($brewery['city'])->not()->toBeNull() 153 | ->and($brewery['name'])->not()->toBeNull() 154 | ->and($brewery['brewery_type'])->toBe('micro'); 155 | } 156 | }); 157 | 158 | it('returns a valid list of breweries', function () { 159 | // Arrange 160 | $client = OpenBreweryDb::client(); 161 | 162 | // Act 163 | $breweries = $client->breweries()->list([ 164 | 'by_name' => 'dog', 165 | 'by_state' => 'California', 166 | 'by_type' => 'micro', 167 | ]); 168 | 169 | // Assert 170 | expect($breweries->toArray())->toBeGreaterThan(1); 171 | foreach ($breweries->toArray() as $brewery) { 172 | expect($brewery)->not()->toBeNull() 173 | ->and($brewery['id'])->not()->toBeNull() 174 | ->and($brewery['brewery_type'])->not()->toBeNull() 175 | ->and($brewery['state'])->not()->toBeNull() 176 | ->and($brewery['state_province'])->not()->toBeNull() 177 | ->and($brewery['postal_code'])->not()->toBeNull() 178 | ->and($brewery['country'])->not()->toBeNull() 179 | ->and($brewery['city'])->not()->toBeNull() 180 | ->and($brewery['name'])->not()->toBeNull() 181 | ->and(strtolower($brewery['name']))->toContain('dog') 182 | ->and($brewery['state'])->toContain('California'); 183 | } 184 | }); 185 | }); 186 | -------------------------------------------------------------------------------- /tests/Resources/MetadataBreweries.php: -------------------------------------------------------------------------------- 1 | breweries()->metadata(); 16 | 17 | // Assert 18 | expect($meta)->not()->toBeNull() 19 | ->and($meta['total'])->toBe('8257') 20 | ->and($meta['page'])->toBe('1') 21 | ->and($meta['per_page'])->toBe('50'); 22 | }); 23 | 24 | it('returns metadata for all breweries when parameters provided', function () { 25 | // Arrange 26 | $client = OpenBreweryDb::client(); 27 | 28 | // Act 29 | $meta = $client->breweries()->metadata([ 30 | 'by_type' => 'micro', 31 | ]); 32 | 33 | // Assert 34 | expect($meta)->not()->toBeNull() 35 | ->and($meta['total'])->toBe('4282') 36 | ->and($meta['page'])->toBe('1') 37 | ->and($meta['per_page'])->toBe('50'); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /tests/Resources/RandomBreweries.php: -------------------------------------------------------------------------------- 1 | breweries()->random(); 17 | $brewery = $breweries[0]; 18 | 19 | // Assert 20 | expect(count($breweries->toArray()))->toBe(1) 21 | ->and($brewery)->not()->toBeNull() 22 | ->and($brewery['id'])->not()->toBeNull() 23 | ->and($brewery['brewery_type'])->not()->toBeNull() 24 | ->and($brewery['state'])->not()->toBeNull() 25 | ->and($brewery['state_province'])->not()->toBeNull() 26 | ->and($brewery['postal_code'])->not()->toBeNull() 27 | ->and($brewery['country'])->not()->toBeNull() 28 | ->and($brewery['city'])->not()->toBeNull() 29 | ->and($brewery['name'])->not()->toBeNull(); 30 | }); 31 | 32 | it('returns specified number of random breweries', function () { 33 | // Arrange 34 | $client = OpenBreweryDb::client(); 35 | $randomBreweries = rand(2, Client::PER_PAGE); 36 | 37 | // Act 38 | $breweries = $client->breweries()->random($randomBreweries); 39 | 40 | // Assert 41 | expect(count($breweries->toArray()))->toBe($randomBreweries); 42 | foreach ($breweries->toArray() as $brewery) { 43 | expect($brewery)->not()->toBeNull() 44 | ->and($brewery['id'])->not()->toBeNull() 45 | ->and($brewery['brewery_type'])->not()->toBeNull() 46 | ->and($brewery['state'])->not()->toBeNull() 47 | ->and($brewery['state_province'])->not()->toBeNull() 48 | ->and($brewery['postal_code'])->not()->toBeNull() 49 | ->and($brewery['country'])->not()->toBeNull() 50 | ->and($brewery['city'])->not()->toBeNull() 51 | ->and($brewery['name'])->not()->toBeNull(); 52 | } 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /tests/Resources/SearchBreweries.php: -------------------------------------------------------------------------------- 1 | breweries()->search('dog', 3); 16 | 17 | // Assert 18 | foreach ($breweries->toArray() as $brewery) { 19 | expect($brewery)->not()->toBeNull() 20 | ->and($brewery['id'])->not()->toBeNull() 21 | ->and($brewery['brewery_type'])->not()->toBeNull() 22 | ->and($brewery['state'])->not()->toBeNull() 23 | ->and($brewery['state_province'])->not()->toBeNull() 24 | ->and($brewery['postal_code'])->not()->toBeNull() 25 | ->and($brewery['country'])->not()->toBeNull() 26 | ->and($brewery['city'])->not()->toBeNull() 27 | ->and($brewery['name'])->not()->toBeNull() 28 | ->and(strtolower($brewery['name']))->toContain('dog'); 29 | } 30 | }); 31 | 32 | it('returns no breweries if the search criteria is not met', function () { 33 | // Arrange 34 | $client = OpenBreweryDb::client(); 35 | 36 | // Act 37 | $breweries = $client->breweries()->search('not a brewery'); 38 | 39 | // Assert 40 | expect($breweries->toArray())->toBeEmpty(); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /tests/Version.php: -------------------------------------------------------------------------------- 1 | expect(Version::current())->toBe('0.9.0')); 10 | --------------------------------------------------------------------------------