diff --git a/docs/api.apib b/docs/api.apib index 0b4c030e8..daf813fe0 100644 --- a/docs/api.apib +++ b/docs/api.apib @@ -36,6 +36,7 @@ format. + `filter-cabin-class` (array, optional) + Y, F, C (enum) - request with this cabin class(es) + `filter-airline`: AB (array, optional) - list of airline IATA codes + + `filter-stops`: 0 (number, optional) - if set to zero, will only return non-stop flights + `legs` (array, fixed-type, required) + (object) + `departure`: BER (string, required) - name of departure IATA airport code diff --git a/src/Search/Model/AmadeusRequestTransformer.php b/src/Search/Model/AmadeusRequestTransformer.php index aa9f5f1ce..57b7c151c 100644 --- a/src/Search/Model/AmadeusRequestTransformer.php +++ b/src/Search/Model/AmadeusRequestTransformer.php @@ -4,6 +4,7 @@ namespace Flight\Service\Amadeus\Search\Model; use Amadeus\Client; +use Amadeus\Client\RequestOptions\FareMasterPricerTbSearch; use Flight\SearchRequestMapping\Entity\BusinessCase; use Flight\SearchRequestMapping\Entity\Leg; use Flight\SearchRequestMapping\Entity\Request; @@ -75,9 +76,9 @@ public function buildClientParams(BusinessCase $businessCase, LoggerInterface $l * * @param Request $request * - * @return Client\RequestOptions\FareMasterPricerTbSearch + * @return FareMasterPricerTbSearch */ - public function buildFareMasterRequestOptions(Request $request) : Client\RequestOptions\FareMasterPricerTbSearch + public function buildFareMasterRequestOptions(Request $request) : FareMasterPricerTbSearch { /** @var BusinessCase $businessCase */ $businessCase = $request->getBusinessCases()->first()->first(); @@ -102,26 +103,31 @@ public function buildFareMasterRequestOptions(Request $request) : Client\Request 'flightOptions' => $this->buildFlightOptions($businessCase, $coopCodes) ]; - if (!empty($excludedAirlines)) { - $options['airlineOptions'][Client\RequestOptions\FareMasterPricerTbSearch::AIRLINEOPT_EXCLUDED] = $excludedAirlines; - } - if (!empty($request->getFilterAirline())) { - $options['airlineOptions'][Client\RequestOptions\FareMasterPricerTbSearch::AIRLINEOPT_MANDATORY ] = $request->getFilterAirline(); + $options['airlineOptions'][FareMasterPricerTbSearch::AIRLINEOPT_MANDATORY] = + array_diff($request->getFilterAirline(), $excludedAirlines); + } elseif (!empty($excludedAirlines)) { + $options['airlineOptions'][FareMasterPricerTbSearch::AIRLINEOPT_EXCLUDED] = $excludedAirlines; } if (!empty(($request->getFilterCabinClass()))) { - $options['cabinOption'] = Client\RequestOptions\FareMasterPricerTbSearch::CABINOPT_MANDATORY; + $options['cabinOption'] = FareMasterPricerTbSearch::CABINOPT_MANDATORY; $options['cabinClass'] = $request->getFilterCabinClass(); } + if ($request->getFilterStops() === 0) { + $options['requestedFlightTypes'] = [ + FareMasterPricerTbSearch::FLIGHTTYPE_NONSTOP, + ]; + } + if (!empty($coopCodes)) { - $options['corporateQualifier'] = Client\RequestOptions\FareMasterPricerTbSearch::CORPORATE_QUALIFIER_UNIFARE; + $options['corporateQualifier'] = FareMasterPricerTbSearch::CORPORATE_QUALIFIER_UNIFARE; $options['corporateCodesUnifares'] = array_values($coopCodes); } - return new Client\RequestOptions\FareMasterPricerTbSearch($options); + return new FareMasterPricerTbSearch($options); } /** @@ -252,7 +258,7 @@ protected function buildFlightOptions(BusinessCase $businessCase, array $coopCod //removes CorpUnifare option if no CoopCode is set in config if (empty($coopCodes)) { - $pricingOptions = array_diff($pricingOptions, [Client\RequestOptions\FareMasterPricerTbSearch::FLIGHTOPT_CORPORATE_UNIFARES]); + $pricingOptions = array_diff($pricingOptions, [FareMasterPricerTbSearch::FLIGHTOPT_CORPORATE_UNIFARES]); } if ($businessCase->getOptions()->isOvernight()) { diff --git a/src/Search/Request/Validator/AmadeusRequestValidator.php b/src/Search/Request/Validator/AmadeusRequestValidator.php index 17b91b0a2..c83795cf1 100644 --- a/src/Search/Request/Validator/AmadeusRequestValidator.php +++ b/src/Search/Request/Validator/AmadeusRequestValidator.php @@ -102,6 +102,8 @@ function ($element) { } ); + $validator->optional('filter-stops')->integer(true); + $validator->required('legs')->each( function (Validator $validator) { $validator->required('departure'); diff --git a/tests/_data/requests/valid-request-with-filters.json b/tests/_data/requests/valid-request-with-filters.json new file mode 100644 index 000000000..b3fb2479d --- /dev/null +++ b/tests/_data/requests/valid-request-with-filters.json @@ -0,0 +1,82 @@ +{ + "search-hash": "", + "flight-hash": "", + "agent": "fluege.de", + "departure": [ + "BER" + ], + "depart-at": [ + 1514725420 + ], + "is-flexible-date": [ + ], + "arrival": [ + "LON" + ], + "adults": 2, + "children": 0, + "infants": 0, + "is-synthetic": false, + "filter-airline": [ + "LH", "AB", "4U", "EJ" + ], + "filter-cabin-class": [ + "Y", "F" + ], + "filter-arrive-before": [ + + ], + "filter-depart-after": [ + + ], + "filter-duration": [ + + ], + "filter-free-beggage": false, + "filter-identical-origin": false, + "filter-airports": [ + + ], + "filter-payment-method": [ + + ], + "filter-price-min": "", + "filter-price-max": "", + "filter-stops": 0, + "page": 1, + "legs": [ + { + "departure": "BER", + "arrival": "LON", + "depart-at": 1514725420, + "is-flexible-date": false, + "filter": [ + + ] + } + ], + "business-cases": [ + [ + { + "content-provider": "amadeus", + "type": "one-way", + "fare-type": "net", + "options": { + "is-one-way-combination": true, + "is-overnight": false, + "is-area-search": false, + "is-benchmark": false, + "result-limit": 1 + }, + "authentication": { + "office-id": "LEJL128AC", + "user-id": "WSTBDUFI", + "password-data": "dkhBM1J5VTU=", + "password-length": 8, + "duty-code": "SU", + "organization-id": "NMC-GERMAN" + } + } + ] + ] +} diff --git a/tests/_data/requests/wrong-filter-types.json b/tests/_data/requests/wrong-filter-types.json index fa1cfc35a..7ce06253f 100644 --- a/tests/_data/requests/wrong-filter-types.json +++ b/tests/_data/requests/wrong-filter-types.json @@ -39,7 +39,7 @@ ], "filter-price-min": "", "filter-price-max": "", - "filter-stops": null, + "filter-stops": true, "page": 1, "legs": [ { diff --git a/tests/api/Search/RequestErrorHandling/CheckFilterCept.php b/tests/api/Search/RequestErrorHandling/CheckFilterCept.php index 9ecdad52f..67678a2ed 100644 --- a/tests/api/Search/RequestErrorHandling/CheckFilterCept.php +++ b/tests/api/Search/RequestErrorHandling/CheckFilterCept.php @@ -32,6 +32,15 @@ ] ]] ); +$I->canSeeResponseContainsJson( + ['filter-stops' => [ + [ + 'code' => ValidationException::INTERNAL_ERROR_CODE, + 'message' => 'INVALID OR MISSING REQUEST PARAM - filter-stops must be an integer', + 'status' => 400 + ] + ]] +); $I->canSeeResponseContainsJson( [ diff --git a/tests/api/Search/ValidWithFiltersCept.php b/tests/api/Search/ValidWithFiltersCept.php new file mode 100644 index 000000000..1c97902ac --- /dev/null +++ b/tests/api/Search/ValidWithFiltersCept.php @@ -0,0 +1,24 @@ +wantTo('see an response that matches the response schema even if I set all supported filters'); +$I->sendPOST( + '/flight-search/', + file_get_contents(codecept_data_dir('requests/valid-request-with-filters.json')) +); +$I->seeResponseCodeIs(200); +$I->seeHttpHeader('content-type', 'application/hal+json'); + +$response = $I->grabResponse(); + +$validator = new \JsonSchema\Validator(); +$validator->validate( + $response, + (object)[ + '$ref' => 'file://' . codecept_data_dir('schema/response-schema.json') + ] +); + +$I->expect($validator->isValid()); + +$I->seeResponseHasLinkToSelf('/flight-search/'); diff --git a/tests/unit/Helper/RequestFaker.php b/tests/unit/Helper/RequestFaker.php index 47dd99b59..59236abd4 100644 --- a/tests/unit/Helper/RequestFaker.php +++ b/tests/unit/Helper/RequestFaker.php @@ -24,6 +24,7 @@ class RequestFaker const OPT_FLEXIBLE_LEG_DATES = 'flexibleDate'; const OPT_AIRLINE_FILTER = 'airlineFilter'; const OPT_CABIN_CLASS_FILTER = 'cabinClassFilter'; + const OPT_NONSTOP_FILTER = 'nonstopFilter'; const OPT_AREA_SEARCH = 'areaSearch'; const OPT_PAX = 'pax'; const OPT_RESULT_LIMIT = 'resultLimit'; @@ -49,6 +50,9 @@ public static function getFakeRequest(array $options) : Request if (isset($options[self::OPT_CABIN_CLASS_FILTER])) { $request->setFilterCabinClass($options[self::OPT_CABIN_CLASS_FILTER]); } + if (isset($options[self::OPT_NONSTOP_FILTER])) { + $request->setFilterStops(0); + } $request->setBusinessCases(new ArrayCollection()); $request->getBusinessCases()->add(new ArrayCollection()); $request->getBusinessCases()->first()->add(self::buildBusinessCase($options)); diff --git a/tests/unit/Search/Model/AmadeusRequestTransformerTest.php b/tests/unit/Search/Model/AmadeusRequestTransformerTest.php index fbb4453da..9848fb1af 100644 --- a/tests/unit/Search/Model/AmadeusRequestTransformerTest.php +++ b/tests/unit/Search/Model/AmadeusRequestTransformerTest.php @@ -116,63 +116,20 @@ public function testItTransformsAirlineFilter() { $config = new \stdClass(); $config->search = new \stdClass(); + $config->search->excluded_airlines = ['DY']; $transformer = new AmadeusRequestTransformer($config); $requestOptions = [ - RequestFaker::OPT_TYPE =>'one-way', - RequestFaker::OPT_FLEXIBLE_LEG_DATES => [false, false], - RequestFaker::OPT_PAX => [1, 1, 0], - RequestFaker::OPT_AREA_SEARCH => false, - RequestFaker::OPT_RESULT_LIMIT => 3, - RequestFaker::OPT_AIRLINE_FILTER => ['AB', 'LH'] - ]; - $request = RequestFaker::getFakeRequest($requestOptions); - - $options = $transformer->buildFareMasterRequestOptions($request); - - $expectedAdult = new Client\RequestOptions\Fare\MPPassenger(); - $expectedAdult->type = Client\RequestOptions\Fare\MPPassenger::TYPE_ADULT; - $expectedAdult->count = 1; - - $expectedChild = new Client\RequestOptions\Fare\MPPassenger(); - $expectedChild->type = Client\RequestOptions\Fare\MPPassenger::TYPE_CHILD; - $expectedChild->count = 1; - - $expectedPax = [ - 0 => $expectedAdult, - 1 => $expectedChild - ]; - - $expectedLegs = [ - 0 => new Client\RequestOptions\Fare\MPItinerary( - [ - 'departureLocation' => new Client\RequestOptions\Fare\MPLocation( - [ - 'city' => 'BER' - ] - ), - 'arrivalLocation' => new Client\RequestOptions\Fare\MPLocation( - [ - 'city' => 'LON' - ] - ), - 'date' => new Client\RequestOptions\Fare\MPDate( - [ - 'dateTime' => RequestFaker::getDepartureDateTime(), - ] - ) - ] - ) + RequestFaker::OPT_AIRLINE_FILTER => ['AB', 'LH', 'DY'] ]; + $request = RequestFaker::buildDefaultRequest($requestOptions); $expectedAF = ['M' => ['AB', 'LH']]; + $options = $transformer->buildFareMasterRequestOptions($request); + $this->assertInstanceOf(Client\RequestOptions\FareMasterPricerTbSearch::class, $options); - $this->assertEquals(3, $options->nrOfRequestedResults); - $this->assertEquals(2, $options->nrOfRequestedPassengers); - $this->assertArraySubset($expectedPax, $options->passengers); - $this->assertArraySubset($expectedLegs, $options->itinerary); $this->assertArraySubset($expectedAF, $options->airlineOptions); } @@ -188,60 +145,13 @@ public function testItProcessesAirlineBlacklist() $transformer = new AmadeusRequestTransformer($config); - $requestOptions = [ - RequestFaker::OPT_TYPE =>'one-way', - RequestFaker::OPT_FLEXIBLE_LEG_DATES => [false, false], - RequestFaker::OPT_PAX => [1, 1, 0], - RequestFaker::OPT_AREA_SEARCH => false, - RequestFaker::OPT_RESULT_LIMIT => 3, - RequestFaker::OPT_AIRLINE_FILTER => ['AB', 'LH'] - ]; - $request = RequestFaker::getFakeRequest($requestOptions); - - $options = $transformer->buildFareMasterRequestOptions($request); - - $expectedAdult = new Client\RequestOptions\Fare\MPPassenger(); - $expectedAdult->type = Client\RequestOptions\Fare\MPPassenger::TYPE_ADULT; - $expectedAdult->count = 1; - - $expectedChild = new Client\RequestOptions\Fare\MPPassenger(); - $expectedChild->type = Client\RequestOptions\Fare\MPPassenger::TYPE_CHILD; - $expectedChild->count = 1; - - $expectedPax = [ - 0 => $expectedAdult, - 1 => $expectedChild - ]; - - $expectedLegs = [ - 0 => new Client\RequestOptions\Fare\MPItinerary( - [ - 'departureLocation' => new Client\RequestOptions\Fare\MPLocation( - [ - 'city' => 'BER' - ] - ), - 'arrivalLocation' => new Client\RequestOptions\Fare\MPLocation( - [ - 'city' => 'LON' - ] - ), - 'date' => new Client\RequestOptions\Fare\MPDate( - [ - 'dateTime' => RequestFaker::getDepartureDateTime(), - ] - ) - ] - ) - ]; + $request = RequestFaker::buildDefaultRequest(); $expectedExcludedAL = ['X' => ['DY', '3K', 'MX', 'OB']]; + $options = $transformer->buildFareMasterRequestOptions($request); + $this->assertInstanceOf(Client\RequestOptions\FareMasterPricerTbSearch::class, $options); - $this->assertEquals(3, $options->nrOfRequestedResults); - $this->assertEquals(2, $options->nrOfRequestedPassengers); - $this->assertArraySubset($expectedPax, $options->passengers); - $this->assertArraySubset($expectedLegs, $options->itinerary); $this->assertArraySubset($expectedExcludedAL, $options->airlineOptions); } @@ -256,61 +166,17 @@ public function testItTransformsCabinClassFilter() $transformer = new AmadeusRequestTransformer($config); $requestOptions = [ - RequestFaker::OPT_TYPE =>'one-way', - RequestFaker::OPT_FLEXIBLE_LEG_DATES => [false, false], - RequestFaker::OPT_PAX => [1, 1, 0], - RequestFaker::OPT_AREA_SEARCH => false, - RequestFaker::OPT_RESULT_LIMIT => 3, RequestFaker::OPT_CABIN_CLASS_FILTER => ['Y', 'C'], ]; - $request = RequestFaker::getFakeRequest($requestOptions); - - $options = $transformer->buildFareMasterRequestOptions($request); - - $expectedAdult = new Client\RequestOptions\Fare\MPPassenger(); - $expectedAdult->type = Client\RequestOptions\Fare\MPPassenger::TYPE_ADULT; - $expectedAdult->count = 1; - - $expectedChild = new Client\RequestOptions\Fare\MPPassenger(); - $expectedChild->type = Client\RequestOptions\Fare\MPPassenger::TYPE_CHILD; - $expectedChild->count = 1; - - $expectedPax = [ - 0 => $expectedAdult, - 1 => $expectedChild - ]; - - $expectedLegs = [ - 0 => new Client\RequestOptions\Fare\MPItinerary( - [ - 'departureLocation' => new Client\RequestOptions\Fare\MPLocation( - [ - 'city' => 'BER' - ] - ), - 'arrivalLocation' => new Client\RequestOptions\Fare\MPLocation( - [ - 'city' => 'LON' - ] - ), - 'date' => new Client\RequestOptions\Fare\MPDate( - [ - 'dateTime' => RequestFaker::getDepartureDateTime(), - ] - ) - ] - ) - ]; + $request = RequestFaker::buildDefaultRequest($requestOptions); $expectedCabinClass = $requestOptions[RequestFaker::OPT_CABIN_CLASS_FILTER]; + $options = $transformer->buildFareMasterRequestOptions($request); + $this->assertInstanceOf(Client\RequestOptions\FareMasterPricerTbSearch::class, $options); - $this->assertEquals(3, $options->nrOfRequestedResults); - $this->assertEquals(2, $options->nrOfRequestedPassengers); - $this->assertArraySubset($expectedPax, $options->passengers); - $this->assertArraySubset($expectedLegs, $options->itinerary); $this->assertEquals('MD', $options->cabinOption); - $this->assertArraySubset($expectedCabinClass, $options->cabinClass); + $this->assertEquals($expectedCabinClass, $options->cabinClass); } /** @@ -326,29 +192,9 @@ public function testItSetsAreaSearchFilter() $transformer = new AmadeusRequestTransformer($config); $requestOptions = [ - RequestFaker::OPT_TYPE =>'one-way', - RequestFaker::OPT_FLEXIBLE_LEG_DATES => [false, false], - RequestFaker::OPT_PAX => [1, 1, 0], RequestFaker::OPT_AREA_SEARCH => true, - RequestFaker::OPT_RESULT_LIMIT => 3, - RequestFaker::OPT_CABIN_CLASS_FILTER => ['Y', 'C'], - ]; - $request = RequestFaker::getFakeRequest($requestOptions); - - $options = $transformer->buildFareMasterRequestOptions($request); - - $expectedAdult = new Client\RequestOptions\Fare\MPPassenger(); - $expectedAdult->type = Client\RequestOptions\Fare\MPPassenger::TYPE_ADULT; - $expectedAdult->count = 1; - - $expectedChild = new Client\RequestOptions\Fare\MPPassenger(); - $expectedChild->type = Client\RequestOptions\Fare\MPPassenger::TYPE_CHILD; - $expectedChild->count = 1; - - $expectedPax = [ - 0 => $expectedAdult, - 1 => $expectedChild ]; + $request = RequestFaker::buildDefaultRequest($requestOptions); $expectedLegs = [ 0 => new Client\RequestOptions\Fare\MPItinerary( @@ -376,10 +222,9 @@ public function testItSetsAreaSearchFilter() ) ]; + $options = $transformer->buildFareMasterRequestOptions($request); + $this->assertInstanceOf(Client\RequestOptions\FareMasterPricerTbSearch::class, $options); - $this->assertEquals(3, $options->nrOfRequestedResults); - $this->assertEquals(2, $options->nrOfRequestedPassengers); - $this->assertArraySubset($expectedPax, $options->passengers); $this->assertArraySubset($expectedLegs, $options->itinerary); } @@ -397,28 +242,11 @@ public function testItSetsFlexibleDate() $requestOptions = [ RequestFaker::OPT_TYPE =>'round-trip', RequestFaker::OPT_FLEXIBLE_LEG_DATES => [false, true], - RequestFaker::OPT_PAX => [1, 1, 0], - RequestFaker::OPT_AREA_SEARCH => false, - RequestFaker::OPT_RESULT_LIMIT => 3, - RequestFaker::OPT_CABIN_CLASS_FILTER => ['Y', 'C'], ]; - $request = RequestFaker::getFakeRequest($requestOptions); + $request = RequestFaker::buildDefaultRequest($requestOptions); $options = $transformer->buildFareMasterRequestOptions($request); - $expectedAdult = new Client\RequestOptions\Fare\MPPassenger(); - $expectedAdult->type = Client\RequestOptions\Fare\MPPassenger::TYPE_ADULT; - $expectedAdult->count = 1; - - $expectedChild = new Client\RequestOptions\Fare\MPPassenger(); - $expectedChild->type = Client\RequestOptions\Fare\MPPassenger::TYPE_CHILD; - $expectedChild->count = 1; - - $expectedPax = [ - 0 => $expectedAdult, - 1 => $expectedChild - ]; - $expectedLegs = [ 0 => new Client\RequestOptions\Fare\MPItinerary( [ @@ -463,9 +291,6 @@ public function testItSetsFlexibleDate() ]; $this->assertInstanceOf(Client\RequestOptions\FareMasterPricerTbSearch::class, $options); - $this->assertEquals(3, $options->nrOfRequestedResults); - $this->assertEquals(2, $options->nrOfRequestedPassengers); - $this->assertArraySubset($expectedPax, $options->passengers); $this->assertArraySubset($expectedLegs, $options->itinerary); } @@ -667,4 +492,25 @@ public function testItBuildsClientParams() $this->assertEquals('password-length', $params->authParams->passwordLength); $this->assertEquals('user-id', $params->authParams->userId); } + + /** + * Verify that it adds the required flight types if the stops filter is set + */ + public function testItSetsNonStopFilter() + { + $config = new \stdClass(); + $config->search = new \stdClass(); + + $transformer = new AmadeusRequestTransformer($config); + + $requestOptions = [ + RequestFaker::OPT_NONSTOP_FILTER => true, + ]; + $request = RequestFaker::buildDefaultRequest($requestOptions); + + $options = $transformer->buildFareMasterRequestOptions($request); + + $this->assertInstanceOf(Client\RequestOptions\FareMasterPricerTbSearch::class, $options); + $this->assertEquals(['N'], $options->requestedFlightTypes); + } } diff --git a/web/index.php b/web/index.php index acaec3be5..79184c0c7 100644 --- a/web/index.php +++ b/web/index.php @@ -5,11 +5,12 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +set_time_limit(0); +ini_set('display_errors', 0); + chdir(__DIR__ . '/..'); require_once __DIR__ . '/../vendor/autoload.php'; -set_time_limit(0); - $app = new Silex\Application(); // general service provider