diff --git a/app/Http/Controllers/AirportController.php b/app/Http/Controllers/AirportController.php index 4bc26d8..0d1aca1 100644 --- a/app/Http/Controllers/AirportController.php +++ b/app/Http/Controllers/AirportController.php @@ -38,15 +38,18 @@ class AirportController extends Controller */ public function resourceList(Request $request) { - $client = IotaClient::create(); - $collection = $client->listAirports($request->input('autocomplete')); + $iotaCodesClient = app('IotaCodesClient'); + $airportsCollection = $iotaCodesClient->listAirports($request->input('autocomplete')); + if (! $airportsCollection) { + return $this->returnErrorMessage('failed to fetch airport list from IOTA api', 400); + } //add pagination $per_page = $request->input('per_page', 10); $page = $request->input('page', 1); $paginator = new LengthAwarePaginator( - $collection->forPage($page, $per_page), - $collection->count(), + $airportsCollection->forPage($page, $per_page), + $airportsCollection->count(), $per_page, $page ); @@ -74,9 +77,9 @@ class AirportController extends Controller */ public function getAirport(Request $request, $code) { - $client = IotaClient::create(); + $iotaCodesClient = app('IotaCodesClient'); - $airport = $client->getAirport($code); + $airport = $iotaCodesClient->getAirport($code); if (is_null($airport)) { return $this->returnErrorMessage('not a valid airport code', 400); } diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 6a4e13c..531bf9e 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -19,7 +19,7 @@ class Controller extends BaseController */ protected function JsonApiResponse(ResourceInterface $resource, $statusCode, $includes = '') { - $manager = new Manager(); + $manager = app('FractalManager'); $manager->setSerializer(new JsonApiSerializer('http://docker.dev:8080')); $manager->parseIncludes($includes); diff --git a/app/Http/Controllers/FlightController.php b/app/Http/Controllers/FlightController.php index 596af7f..e240dc4 100644 --- a/app/Http/Controllers/FlightController.php +++ b/app/Http/Controllers/FlightController.php @@ -46,11 +46,9 @@ class FlightController extends Controller $page ); - $result = (new Collection($paginator, new TripTransformer(), 'trips')) + $result = (new Collection($paginator, new FlightTransform(), 'trips')) ->setPaginator(new IlluminatePaginatorAdapter($paginator)); - $result = new Collection($flightCollection, new FlightTransform(), 'flights'); - return $this->JsonApiResponse($result, 200); } @@ -98,10 +96,10 @@ class FlightController extends Controller { /** @var \App\Libraries\Trips\Models\Flights $flight */ $flight = Flights::find($id); - $deleted = $flight->delete(); + if (! is_null($flight)) { + $deleted = $flight->delete(); + } - $result = new Item($flight, new FlightTransform(), 'flights'); - - return $this->JsonApiResponse($result, 200); + return response()->json(null, 204); } } diff --git a/app/Http/Controllers/TripController.php b/app/Http/Controllers/TripController.php index d565c46..c9b8512 100644 --- a/app/Http/Controllers/TripController.php +++ b/app/Http/Controllers/TripController.php @@ -115,7 +115,7 @@ class TripController extends Controller return $this->returnErrorMessage('destination not set', 400); } - $client = IotaClient::create(); + $client = app('IotaCodesClient'); $airport = $client->getAirport($destination); if (is_null($airport)) { return $this->returnErrorMessage('destination not a valid airport code', 400); diff --git a/app/Libraries/IotaCodes/Airport.php b/app/Libraries/IotaCodes/Airport.php index 1a78987..74e07b6 100644 --- a/app/Libraries/IotaCodes/Airport.php +++ b/app/Libraries/IotaCodes/Airport.php @@ -3,17 +3,33 @@ namespace App\Libraries\IotaCodes; class Airport { - + /** + * @var string + */ public $name; + /** + * @var string + */ public $code; + /** + * Airport constructor. + * @param $name + * @param $code + */ protected function __construct($name, $code) { $this->name = $name; $this->code = $code; } + /** + * Airport factory method + * @param $name + * @param $code + * @return \App\Libraries\IotaCodes\Airport + */ public static function create($name, $code) { return new self($name, $code); diff --git a/app/Libraries/IotaCodes/AirportTransformer.php b/app/Libraries/IotaCodes/AirportTransformer.php index dae7b9e..d86a571 100644 --- a/app/Libraries/IotaCodes/AirportTransformer.php +++ b/app/Libraries/IotaCodes/AirportTransformer.php @@ -5,6 +5,12 @@ use League\Fractal\TransformerAbstract; class AirportTransformer extends TransformerAbstract { + /** + * Fractal Transformer for Airport Resource + * + * @param \App\Libraries\IotaCodes\Airport $airport + * @return array + */ public function transform(Airport $airport) { return [ diff --git a/app/Libraries/IotaCodes/Client.php b/app/Libraries/IotaCodes/Client.php index 29bda6a..84c6ab9 100644 --- a/app/Libraries/IotaCodes/Client.php +++ b/app/Libraries/IotaCodes/Client.php @@ -22,6 +22,11 @@ class Client $this->guzzle = $guzzle; } + /** + * Factory Method + * + * @return \App\Libraries\IotaCodes\Client + */ public static function create() { return new self(new Guzzle([ @@ -31,6 +36,12 @@ class Client ])); } + /** + * Get list of airports + * + * @param string $autoComplete + * @return \Illuminate\Support\Collection|false + */ public function listAirports($autoComplete = '') { $uri = '/api/v6/airports'; @@ -48,14 +59,20 @@ class Client ]); } catch (\Exception $e) { - //todo handle error + return false; } $result = $response->getBody()->getContents(); + if (empty($result)) { + return false; + } app('cache')->put($key, $result, $minutes); return $result; }); + if(! $response) { + return false; + } $result = $this->toAirportCollection($response); if ($autoComplete != '') { $result = $this->autoCompleteMap($result, $autoComplete); @@ -71,7 +88,7 @@ class Client { $uri = '/api/v6/airports'; $cacheKey = 'airport.'.$code; - $cacheMinutes = 1; + $cacheMinutes = 60; $response = app('cache')->get($cacheKey, function () use ($cacheKey, $cacheMinutes, $uri, $code) { try { @@ -103,6 +120,9 @@ class Client private function toAirportCollection($json) { $data = json_decode($json); + if (! $data) { + return new Collection(); + } $result = (new Collection($data->response)) ->sortBy('name') ->map(function ($item, $key) { @@ -112,6 +132,11 @@ class Client return $result; } + /** + * @param \Illuminate\Support\Collection $collection + * @param $autoComplete + * @return Collection + */ private function autoCompleteMap(Collection $collection, $autoComplete) { return $collection->filter(function ($value, $key) use ($autoComplete) { diff --git a/app/Libraries/IotaCodes/IotaCodesServiceProvider.php b/app/Libraries/IotaCodes/IotaCodesServiceProvider.php new file mode 100644 index 0000000..c4c6d91 --- /dev/null +++ b/app/Libraries/IotaCodes/IotaCodesServiceProvider.php @@ -0,0 +1,20 @@ +app->singleton('IotaCodesClient', function ($app) { + return Client::create(); + }); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index ddec046..c331888 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -3,6 +3,7 @@ namespace App\Providers; use Illuminate\Support\ServiceProvider; +use League\Fractal\Manager; class AppServiceProvider extends ServiceProvider { @@ -13,6 +14,8 @@ class AppServiceProvider extends ServiceProvider */ public function register() { - // + $this->app->singleton('FractalManager', function ($app) { + return new Manager(); + }); } } diff --git a/bootstrap/app.php b/bootstrap/app.php index d45d2d9..e2ec623 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -78,10 +78,11 @@ $app->singleton( | */ -// $app->register(App\Providers\AppServiceProvider::class); +$app->register(App\Providers\AppServiceProvider::class); // $app->register(App\Providers\AuthServiceProvider::class); // $app->register(App\Providers\EventServiceProvider::class); $app->register(Illuminate\Redis\RedisServiceProvider::class); +$app->register(App\Libraries\IotaCodes\IotaCodesServiceProvider::class); /* |-------------------------------------------------------------------------- diff --git a/tests/acceptance/AirportsCest.php b/tests/acceptance/AirportsCest.php index dfca4ff..6663bb5 100755 --- a/tests/acceptance/AirportsCest.php +++ b/tests/acceptance/AirportsCest.php @@ -3,6 +3,8 @@ class AirportsCest { + protected $uri = '/airports'; + public function _before(AcceptanceTester $I) { } @@ -12,7 +14,81 @@ class AirportsCest } // tests - public function tryToTest(AcceptanceTester $I) + public function listAirportsReturnsJson(AcceptanceTester $I) { + $params = []; + + $I->sendGET($this->uri, $params); + $r = $I->grabResponse(); + + $I->seeResponseCodeIs(200, $r); + $I->seeResponseIsJson(); + } + + public function listAirportsReturns1ItemWhenPerPageIs1(AcceptanceTester $I) + { + $params = [ + 'page' => 1, + 'per_page' => 1 + ]; + + $I->sendGET($this->uri, $params); + $r = $I->grabResponse(); + + $I->seeResponseCodeIs(200, $r); + $I->seeResponseIsJson(); + + $data = json_decode($r); + $I->assertCount(1, $data->data, json_encode($data)); + } + + public function listAirportsReturns2ItemWhenPerPageIs2(AcceptanceTester $I) + { + $params = [ + 'page' => 1, + 'per_page' => 2 + ]; + + $I->sendGET($this->uri, $params); + $r = $I->grabResponse(); + + $I->seeResponseCodeIs(200, $r); + $I->seeResponseIsJson(); + + $data = json_decode($r); + $I->assertCount(2, $data->data, json_encode($data)); + } + + public function getAirportReturnsTripJson(AcceptanceTester $I) + { + $I->sendGET($this->uri, []); + $r = $I->grabResponse(); + $data = json_decode($r, true); + $airportId = $data['data'][0]['id']; + + $params = []; + $uri = $this->uri . '/' . $airportId; + + $I->sendGET($uri, $params); + $r = $I->grabResponse(); + + $I->seeResponseCodeIs(200, $r); + $I->seeResponseIsJson(); + + $data = json_decode($r, true); + $I->assertEquals('airports', $data['data']['type']); + $I->assertEquals($airportId, $data['data']['id']); + } + + public function getAirportReturns400IfTripIdNotFound(AcceptanceTester $I) + { + $params = []; + $uri = $this->uri . '/999999'; + + $I->sendGET($uri, $params); + $r = $I->grabResponse(); + + $I->seeResponseCodeIs(400, $r); + $I->seeResponseIsJson(); } } diff --git a/tests/acceptance/FlightsCest.php b/tests/acceptance/FlightsCest.php index de40bf8..53b4ca4 100755 --- a/tests/acceptance/FlightsCest.php +++ b/tests/acceptance/FlightsCest.php @@ -3,6 +3,8 @@ class FlightsCest { + protected $uri = '/flights'; + public function _before(AcceptanceTester $I) { } @@ -12,7 +14,133 @@ class FlightsCest } // tests - public function tryToTest(AcceptanceTester $I) + public function listFlightsReturnsJson(AcceptanceTester $I) { + $params = []; + + $I->sendGET($this->uri, $params); + $r = $I->grabResponse(); + + $I->seeResponseCodeIs(200, $r); + $I->seeResponseIsJson(); + } + + public function listFlightsReturns1ItemWhenPerPageIs1(AcceptanceTester $I) + { + $params = [ + 'page' => 1, + 'per_page' => 1 + ]; + + $I->sendGET($this->uri, $params); + $r = $I->grabResponse(); + + $I->seeResponseCodeIs(200, $r); + $I->seeResponseIsJson(); + + $data = json_decode($r); + $I->assertCount(1, $data->data, json_encode($data)); + } + + public function listFlightsReturns2ItemWhenPerPageIs2(AcceptanceTester $I) + { + $params = [ + 'page' => 1, + 'per_page' => 2 + ]; + + $I->sendGET($this->uri, $params); + $r = $I->grabResponse(); + + $I->seeResponseCodeIs(200, $r); + $I->seeResponseIsJson(); + + $data = json_decode($r); + $I->assertCount(2, $data->data, json_encode($data)); + } + + public function listFlightsReturns0ItemWhenPageIsBeyondEndOfCollection(AcceptanceTester $I) + { + $params = [ + 'page' => 99999, + 'per_page' => 2 + ]; + + $I->sendGET($this->uri, $params); + $r = $I->grabResponse(); + + $I->seeResponseCodeIs(200, $r); + $I->seeResponseIsJson(); + + $data = json_decode($r); + $I->assertCount(0, $data->data, json_encode($data)); + } + + + public function getFlightReturnsTripJson(AcceptanceTester $I) + { + $I->sendGET($this->uri, []); + $r = $I->grabResponse(); + $data = json_decode($r, true); + $tripId = $data['data'][0]['id']; + + $params = []; + $uri = $this->uri . '/' . $tripId; + + $I->sendGET($uri, $params); + $r = $I->grabResponse(); + + $I->seeResponseCodeIs(200, $r); + $I->seeResponseIsJson(); + + $data = json_decode($r, true); + $I->assertEquals('flights', $data['data']['type']); + $I->assertEquals($tripId, $data['data']['id']); + } + + public function getFlightReturns400IfTripIdNotFound(AcceptanceTester $I) + { + $params = []; + $uri = $this->uri . '/999999'; + + $I->sendGET($uri, $params); + $r = $I->grabResponse(); + + $I->seeResponseCodeIs(400, $r); + $I->seeResponseIsJson(); + } + + public function removeFlightRemovesFlightFromTrips(AcceptanceTester $I) + { + $tripId = $this->getFirstTripId($I); + $r = $this->addFlightToTrip($I, 'YUL', $tripId); + $data = json_decode($r, true); + + $flightId = $data['data']['attributes']['flights'][0]['id']; + $uri = $this->uri .'/'.$flightId; + + $I->sendDELETE($uri); + + $I->seeResponseCodeIs(204); + } + + private function getFirstTripId(AcceptanceTester $I) + { + $uri = '/trips/'; + $I->sendGET($this->uri, []); + $r = $I->grabResponse(); + $data = json_decode($r, true); + $tripId = $data['data'][0]['id']; + return $tripId; + } + + private function addFlightToTrip(AcceptanceTester $I, $destination, $tripId) + { + $uri = '/trips/' . $tripId .'/flights'; + + $I->sendPost($uri, [ + 'destination' => $destination + ]); + return $I->grabResponse(); } } diff --git a/tests/acceptance/TripsCest.php b/tests/acceptance/TripsCest.php index 3363f11..e5db2e1 100755 --- a/tests/acceptance/TripsCest.php +++ b/tests/acceptance/TripsCest.php @@ -39,7 +39,7 @@ class TripsCest $I->seeResponseIsJson(); $data = json_decode($r); - $I->assertCount(1, $data->data, $data); + $I->assertCount(1, $data->data, json_encode($data)); } public function listTripReturns2ItemWhenPerPageIs2(AcceptanceTester $I) @@ -56,7 +56,7 @@ class TripsCest $I->seeResponseIsJson(); $data = json_decode($r); - $I->assertCount(2, $data->data, $data); + $I->assertCount(2, $data->data, json_encode($data)); } public function getTripReturnsTripJson(AcceptanceTester $I) @@ -114,4 +114,42 @@ class TripsCest $data = json_decode($r, true); $I->assertCount($nbFlights+1, $data['data']['attributes']['flights']); } + + public function addFlightReturns400WhenDestinationInvalid(AcceptanceTester $I) + { + $I->sendGET($this->uri, []); + $r = $I->grabResponse(); + $data = json_decode($r, true); + $tripId = $data['data'][0]['id']; + $nbFlights = count($data['data'][0]['attributes']['flights']); + + $params = [ + 'destination' => 'BlueFin' + ]; + $uri = $this->uri . '/' . $tripId .'/flights'; + + $I->sendPost($uri, $params); + $r = $I->grabResponse(); + + $I->seeResponseCodeIs(400, $r); + $I->seeResponseIsJson(); + } + + public function addFlightReturns400WhenDestinationNotSet(AcceptanceTester $I) + { + $I->sendGET($this->uri, []); + $r = $I->grabResponse(); + $data = json_decode($r, true); + $tripId = $data['data'][0]['id']; + $nbFlights = count($data['data'][0]['attributes']['flights']); + + $params = []; + $uri = $this->uri . '/' . $tripId .'/flights'; + + $I->sendPost($uri, $params); + $r = $I->grabResponse(); + + $I->seeResponseCodeIs(400, $r); + $I->seeResponseIsJson(); + } }