diff --git a/src/OpenApiSpecFactory.php b/src/OpenApiSpecFactory.php new file mode 100644 index 0000000..a229a80 --- /dev/null +++ b/src/OpenApiSpecFactory.php @@ -0,0 +1,23 @@ + */ private string $adapter = HttpFoundationAdapter::class; @@ -33,9 +37,7 @@ public function __construct(private BaseValidatorBuilder $validatorBuilder) */ public static function fromYaml(string $definition): ValidatorBuilderInterface { - $method = is_file($definition) ? 'fromYamlFile' : 'fromYaml'; - - return self::fromMethod($method, $definition); + return self::fromYamlFile($definition); } /** @@ -45,9 +47,7 @@ public static function fromYaml(string $definition): ValidatorBuilderInterface */ public static function fromJson(string $definition): ValidatorBuilderInterface { - $method = is_file($definition) ? 'fromJsonFile' : 'fromJson'; - - return self::fromMethod($method, $definition); + return self::fromJsonFile($definition); } /** @@ -57,7 +57,7 @@ public static function fromJson(string $definition): ValidatorBuilderInterface */ public static function fromYamlFile(string $definition): ValidatorBuilderInterface { - return self::fromMethod('fromYamlFile', $definition); + return self::fromMethod(self::determineMethod($definition, 'yaml'), $definition); } /** @@ -67,7 +67,7 @@ public static function fromYamlFile(string $definition): ValidatorBuilderInterfa */ public static function fromJsonFile(string $definition): ValidatorBuilderInterface { - return self::fromMethod('fromJsonFile', $definition); + return self::fromMethod(self::determineMethod($definition, 'json'), $definition); } /** @@ -98,11 +98,51 @@ public static function fromJsonString(string $definition): ValidatorBuilderInter */ private static function fromMethod(string $method, string $definition): ValidatorBuilderInterface { + $openApiFactory = self::getOpenApiSpecFactory(); + + $definition = match ($method) { + 'fromJsonSchema' => $openApiFactory->readFromJsonFile($definition), + 'fromYamlSchema' => $openApiFactory->readFromYamlFile($definition), + default => $definition, + }; + + if (in_array($method, ['fromJsonSchema', 'fromYamlSchema'], true)) { + $method = 'fromSchema'; + } + $builder = (new BaseValidatorBuilder())->{$method}($definition); return new ValidatorBuilder($builder); } + private static function determineMethod(string $definition, string $format): string + { + if (filter_var($definition, FILTER_VALIDATE_URL) && in_array(parse_url($definition, PHP_URL_SCHEME), ['http', 'https'], true)) { + return sprintf('from%sSchema', ucfirst($format)); + } + + if (is_file($definition)) { + return sprintf('from%sFile', ucfirst($format)); + } + + return sprintf('from%s', ucfirst($format)); + } + + /** @param ?Closure():OpenApiSpecFactoryInterface $resolver */ + public static function setOpenApiSpecFactoryResolver(?Closure $resolver = null): void + { + self::$openApiSpecFactoryResolver = $resolver; + } + + public static function getOpenApiSpecFactory(): OpenApiSpecFactoryInterface + { + if (self::$openApiSpecFactoryResolver === null) { + return new OpenApiSpecFactory(); + } + + return (self::$openApiSpecFactoryResolver)(); + } + /** @inheritDoc */ public function setCache(object $cache): ValidatorBuilderInterface { diff --git a/tests/OpenApiSpecFactoryTest.php b/tests/OpenApiSpecFactoryTest.php new file mode 100644 index 0000000..3864595 --- /dev/null +++ b/tests/OpenApiSpecFactoryTest.php @@ -0,0 +1,28 @@ +readFromYamlFile(self::$yamlDefinition); + + $this->assertInstanceOf(SpecObjectInterface::class, $result); + $this->assertInstanceOf(OpenApi::class, $result); + } + + public function test_it_creates_an_open_api_spec_from_json_file(): void + { + $result = (new OpenApiSpecFactory())->readFromJsonFile(self::$jsonDefinition); + + $this->assertInstanceOf(SpecObjectInterface::class, $result); + $this->assertInstanceOf(OpenApi::class, $result); + } +} diff --git a/tests/ValidatorBuilderTest.php b/tests/ValidatorBuilderTest.php index f10f023..607ff47 100644 --- a/tests/ValidatorBuilderTest.php +++ b/tests/ValidatorBuilderTest.php @@ -4,9 +4,11 @@ namespace Osteel\OpenApi\Testing\Tests; +use cebe\openapi\Reader; use InvalidArgumentException; use Osteel\OpenApi\Testing\Adapters\MessageAdapterInterface; use Osteel\OpenApi\Testing\Cache\CacheAdapterInterface; +use Osteel\OpenApi\Testing\OpenApiSpecFactoryInterface; use Osteel\OpenApi\Testing\Validator; use Osteel\OpenApi\Testing\ValidatorBuilder; use stdClass; @@ -42,6 +44,30 @@ public function test_it_builds_a_validator(string $method, string $definition) $this->assertTrue($result->get($response, static::PATH)); } + public function test_it_builds_a_validator_given_a_absolute_url(): void + { + $url = 'https://foobar.localhost/openapi.yaml'; + + $openApiFactory = $this->createMock(OpenApiSpecFactoryInterface::class); + $openApiFactory->expects($this->once()) + ->method('readFromYamlFile') + ->with($url) + ->willReturn(Reader::readFromYamlFile(self::$yamlDefinition)); + + ValidatorBuilder::setOpenApiSpecFactoryResolver(fn () => $openApiFactory); + + $result = ValidatorBuilder::fromYaml($url)->getValidator(); + + $this->assertInstanceOf(Validator::class, $result); + + $request = $this->httpFoundationRequest(static::PATH, 'get', ['foo' => 'bar']); + $response = $this->httpFoundationResponse(['foo' => 'bar']); + + // Validate a request and a response to make sure the definition was correctly parsed. + $this->assertTrue($result->get($request, static::PATH)); + $this->assertTrue($result->get($response, static::PATH)); + } + public function test_it_does_not_set_the_adapter_because_its_type_is_invalid() { $this->expectException(InvalidArgumentException::class);