diff --git a/src/Http/Controllers/Api/ApiFormAutocompleteController.php b/src/Http/Controllers/Api/ApiFormAutocompleteController.php index 17ad9b99c..e124414f2 100644 --- a/src/Http/Controllers/Api/ApiFormAutocompleteController.php +++ b/src/Http/Controllers/Api/ApiFormAutocompleteController.php @@ -9,6 +9,8 @@ use Illuminate\Http\Request; use Illuminate\Support\Arr; use Illuminate\Support\Facades\Route; +use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class ApiFormAutocompleteController extends Controller { @@ -58,7 +60,7 @@ public function index(string $globalFilter, EntityKey $entityKey, string $autoco $fieldEndpoint = $this->normalizeEndpoint($field->remoteEndpoint()); // Check that requestEndpoint is valid - $this->checkEndpoint($requestEndpoint, $fieldEndpoint); + $this->checkEndpoint($requestEndpoint, $fieldEndpoint, $field->remoteMethod()); $apiResponse = app()->handle( tap(Request::create( @@ -89,25 +91,22 @@ private function normalizeEndpoint(string $input): string return str($input)->startsWith('http') ? $input : url($input); } - private function checkEndpoint(string $requestEndpoint, string $fieldEndpoint): void + private function checkEndpoint(string $requestEndpoint, string $fieldEndpoint, string $fieldMethod): void { // Validates that the endpoint defined in the field is the same as the one called preg_match( - '#' + '#^' .str() ->of(preg_quote($fieldEndpoint)) ->replaceMatches('#\\\\{\\\\{(.*)\\\\}\\\\}#', '(.*)') - .'#im', + .'$#im', $requestEndpoint ) ?: throw new SharpInvalidConfigException('The endpoint is not the one defined in the autocomplete field.'); - // Check that the remote route exists in the app - collect(Route::getRoutes()->getRoutes()) - ->map(fn ($route) => url($route->uri)) - ->filter(fn (string $routeUrl) => preg_match( - '#'.str()->of($routeUrl)->replaceMatches('#\\{(.*)\\}#', '(.*)').'#im', - str($requestEndpoint)->before('?') - )) - ->count() > 0 ?: throw new SharpInvalidConfigException('The endpoint is not a valid internal route.'); + try { + Route::getRoutes()->match(Request::create($requestEndpoint, method: $fieldMethod)); + } catch (NotFoundHttpException|MethodNotAllowedHttpException $e) { + throw new SharpInvalidConfigException('The endpoint is not a valid internal route.', previous: $e); + } } } diff --git a/tests/Http/Api/ApiFormAutocompleteControllerTest.php b/tests/Http/Api/ApiFormAutocompleteControllerTest.php index 9cfd32254..015df4438 100644 --- a/tests/Http/Api/ApiFormAutocompleteControllerTest.php +++ b/tests/Http/Api/ApiFormAutocompleteControllerTest.php @@ -3,6 +3,7 @@ use Code16\Sharp\EntityList\Commands\EntityCommand; use Code16\Sharp\EntityList\Commands\InstanceCommand; use Code16\Sharp\EntityList\Commands\SingleInstanceCommand; +use Code16\Sharp\Exceptions\SharpInvalidConfigException; use Code16\Sharp\Form\Fields\SharpFormAutocompleteRemoteField; use Code16\Sharp\Form\Fields\SharpFormEditorField; use Code16\Sharp\Form\Fields\SharpFormListField; @@ -379,7 +380,7 @@ public function buildFormFields(FieldsContainer $formFields): void 'entityKey' => 'person', 'autocompleteFieldKey' => 'autocomplete_field', ])); -})->throws(\Code16\Sharp\Exceptions\SharpInvalidConfigException::class); +})->throws(SharpInvalidConfigException::class); it('fails if field is not a remote autocomplete field', function () { $this->withoutExceptionHandling(); @@ -399,7 +400,7 @@ public function buildFormFields(FieldsContainer $formFields): void 'entityKey' => 'person', 'autocompleteFieldKey' => 'name', ])); -})->throws(\Code16\Sharp\Exceptions\SharpInvalidConfigException::class); +})->throws(SharpInvalidConfigException::class); it('validates that the sent remote endpoint is the same that was defined in the autocomplete field', function () { $this->withoutExceptionHandling(); @@ -427,7 +428,7 @@ public function buildFormFields(FieldsContainer $formFields): void 'endpoint' => '/another/endpoint', 'search' => 'my search', ]); -})->throws(\Code16\Sharp\Exceptions\SharpInvalidConfigException::class); +})->throws(SharpInvalidConfigException::class); it('allows the defined endpoint to have a querystring', function () { $this->withoutExceptionHandling(); @@ -602,7 +603,7 @@ public function buildFormFields(FieldsContainer $formFields): void 'endpoint' => 'https://google.fr', 'search' => 'my search', ]); -})->throws(\Code16\Sharp\Exceptions\SharpInvalidConfigException::class); +})->throws(SharpInvalidConfigException::class); it('allows internal remote endpoint with a querystring', function () { $this->withoutExceptionHandling(); @@ -614,7 +615,7 @@ public function buildFormFields(FieldsContainer $formFields): void $formFields->addField( SharpFormAutocompleteRemoteField::make('autocomplete_field') ->setRemoteMethodPOST() - ->setRemoteEndpoint('/my/endpoint') + ->setRemoteEndpoint('/my/endpoint?param1=one') ); } });