Skip to content

Commit

Permalink
Merge pull request #8 from festivalslab/add-venue-search
Browse files Browse the repository at this point in the history
Support venue search requests
  • Loading branch information
craig410 authored Nov 28, 2024
2 parents 5d4aa90 + 96ac2dd commit a80c2cb
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 19 deletions.
4 changes: 2 additions & 2 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ README text eol=lf
*.sqlite binary

# Ignore paths that should not be included in an archive (eg for a distribution version)
/.github export-ignore
/test export-ignore
phpunit.xml export-ignore
.travis.yml export-ignore
/phpunit.xml export-ignore
src/ClientVersion.php export-subst
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
### Unreleased

### v2.3.0 (2024-11-28)

* Add support for venue search endpoint
* Remove CI config from export package

### v2.2.0 (2024-10-01)

* Support PHP 8.3
Expand Down
29 changes: 29 additions & 0 deletions src/FestivalsApiClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,20 @@

use FestivalsApi\Result\EventSearchResult;
use FestivalsApi\Result\SingleEventResult;
use FestivalsApi\Result\VenueSearchResult;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Psr7\Request;
use InvalidArgumentException;
use Psr\Http\Message\ResponseInterface;
use function http_build_query;

class FestivalsApiClient
{
const BASE_URL = 'https://api.edinburghfestivalcity.com';
const EVENTS_ENDPOINT = '/events';
const VENUES_ENDPOINT = '/venues';

protected string $access_key;

Expand Down Expand Up @@ -73,6 +76,32 @@ public function searchEvents(array $query): EventSearchResult
return new EventSearchResult($events, (string) $request->getUri(), $total_results);
}

/**
* Search API for venues matching query
*
* @throws FestivalsApiClientException
* @throws GuzzleException
*/
public function searchVenues(array $query): VenueSearchResult
{
$this->throwEmptyCredentials();

$url = self::VENUES_ENDPOINT;
if ( ! empty($query)) {
$url .= '?'.http_build_query($query);
}

$request = $this->createRequest($url);
$response = $this->sendRequest($request);

return new VenueSearchResult(
venues: $this->decodeJsonResponse($response),
url: $request->getUri(),
total_results: (int) $response->getHeaderLine('x-total-results') ?: 0
);
}


public function setBaseUrl(string $base_url): void
{
$this->base_url = rtrim($base_url, '/');
Expand Down
34 changes: 34 additions & 0 deletions src/Result/VenueSearchResult.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php
/**
* @author Festivals Edinburgh <[email protected]>
* @licence BSD-3-Clause
*/


namespace FestivalsApi\Result;


class VenueSearchResult
{
public function __construct(
protected array $venues,
protected string $url,
protected int $total_results
) {
}

public function getVenues(): array
{
return $this->venues;
}

public function getTotalResults(): int
{
return $this->total_results;
}

public function getUrl(): string
{
return $this->url;
}
}
54 changes: 37 additions & 17 deletions test/unit/FestivalsApiClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,24 @@ public function test_it_is_initialisable(): void

#[TestWith(['loadEvent', 1234])]
#[TestWith(['searchEvents', ['title' => 'Foo']])]
public function test_it_throws_if_you_attempt_to_use_it_without_setting_credentials(string $method, $args): void
#[TestWith(['searchVenues', ['name' => 'Theatre']])]
public function test_it_throws_if_you_attempt_to_use_it_without_setting_credentials(string $method, mixed $args): void
{
$subject = $this->newSubject();
$this->expectException(FestivalsApiClientException::class);
$this->expectExceptionMessage('Missing credentials');
$subject->$method($args);
}

public function test_it_performs_requests_with_initialised_key_and_secret(): void
#[TestWith(['searchEvents', 'key=mykey&signature=9c638d91ba50f39da6ecc1d4e8846ae30c318f55'])]
#[TestWith(['searchVenues', 'key=mykey&signature=71a425569de5646f9d42d73d1f1ba524fe5e9051'])]
public function test_it_performs_requests_with_initialised_key_and_secret(string $method, string $expect): void
{
$this->mockGuzzleWithEmptySuccessResponse();
$subject = $this->newSubject();
$subject->setCredentials('mykey', 'mysecret');
$subject->searchEvents([]);
$this->assertSame(
'key=mykey&signature=9c638d91ba50f39da6ecc1d4e8846ae30c318f55',
$this->getRequest(0)->getUri()->getQuery()
);
$subject->$method([]);
$this->assertSame($expect, $this->getRequest(0)->getUri()->getQuery());
}

public static function provider_constructor_base_urls(): array
Expand Down Expand Up @@ -105,20 +105,20 @@ public function test_it_calls_the_api_with_the_event_id_specified(): void
$result = $subject->loadEvent('1234');

// calls the API only once
$this->assertEquals(1, count($this->history));
$this->assertSame(1, count($this->history));

$expected = 'https://api.edinburghfestivalcity.com/events/1234?key=test-key&signature=93604dba44ed6a988bb6b25f480c66f1d7978ec1';
// it calls the API correctly
$this->assertEquals($expected, (string) $this->getRequest(0)->getUri());
$this->assertSame($expected, (string) $this->getRequest(0)->getUri());
//it records the same url in the result object
$this->assertEquals($expected, $result->getUrl());
$this->assertSame($expected, $result->getUrl());
}

public function test_load_event_returns_single_event_from_response(): void
{
$this->mockGuzzleWithResponse(new Response(200, [], '{"title": "Foo Bar", "id":4321}'));
$subject = $this->newSubjectWithValidCredentials();
$this->assertEquals($subject->loadEvent('4321')->getEvent(), ['title' => 'Foo Bar', 'id' => 4321]);
$this->assertSame($subject->loadEvent('4321')->getEvent(), ['title' => 'Foo Bar', 'id' => 4321]);
}

#[TestWith([['title' => "\"Foo Bar\""], 'title=%22Foo+Bar%22&key=test-key&signature=b313169e84c3922f07c6010e2191486a5192c039'])]
Expand All @@ -132,9 +132,9 @@ public function test_it_correctly_url_encodes_search_query(array $query, string

$expected = 'https://api.edinburghfestivalcity.com/events?'.$expected;
//it queries the API with the correct URL
$this->assertEquals($expected, (string) $this->getRequest(0)->getUri());
$this->assertSame($expected, (string) $this->getRequest(0)->getUri());
//it records the same url in the result object
$this->assertEquals($expected, $result->getUrl());
$this->assertSame($expected, $result->getUrl());
}

#[TestWith([[], 0])]
Expand All @@ -145,7 +145,7 @@ public function test_event_search_result_holds_total_result_count_from_header(ar
$this->mockGuzzleWithResponse(new Response(200, $headers, "[]"));
$subject = $this->newSubjectWithValidCredentials();
$result = $subject->searchEvents([]);
$this->assertEquals($expected, $result->getTotalResults());
$this->assertSame($expected, $result->getTotalResults());
}

public function test_search_event_returns_events_from_response(): void
Expand All @@ -159,7 +159,7 @@ public function test_search_event_returns_events_from_response(): void
);

$subject = $this->newSubjectWithValidCredentials();
$this->assertEquals(
$this->assertSame(
$subject->searchEvents(['title' => 'Test'])->getEvents(),
[
['title' => 'Test event 1', 'id' => 101],
Expand All @@ -168,6 +168,26 @@ public function test_search_event_returns_events_from_response(): void
);
}

public function test_search_venue_returns_events_from_response(): void
{
$this->mockGuzzleWithResponse(
new Response(
200,
[],
'[{"name": "Theatre", "id":101},{"name": "Studio", "id":102}]'
)
);

$subject = $this->newSubjectWithValidCredentials();
$this->assertSame(
$subject->searchVenues(['title' => 'Test'])->getVenues(),
[
['name' => 'Theatre', 'id' => 101],
['name' => 'Studio', 'id' => 102],
]
);
}

#[TestWith([200, '<p>HTML</p>', 'API responded with invalid JSON'])]
#[TestWith([200, '', 'API responded with invalid JSON'])]
#[TestWith([404, '{"error":"Event not found"}', 'Event not found'])]
Expand All @@ -192,8 +212,8 @@ public function test_client_exception_contains_url_requested(): void
try {
$subject->searchEvents([]);
} catch (FestivalsApiClientException $e) {
$this->assertEquals('Something went wrong', $e->getMessage());
$this->assertEquals(
$this->assertSame('Something went wrong', $e->getMessage());
$this->assertSame(
"https://api.edinburghfestivalcity.com/events?key=test-key&signature=7793ae3038669f76de954f197f1818727a12a037",
$e->getUrl()
);
Expand Down

0 comments on commit a80c2cb

Please sign in to comment.