Skip to content

Commit

Permalink
Add automatic detection and validation from response path in openapi
Browse files Browse the repository at this point in the history
  • Loading branch information
AaronBernabeu committed Feb 16, 2020
1 parent c5a3011 commit b32a7f2
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 4 deletions.
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
"justinrainbow/json-schema": "^5.2",
"behat/behat": "^3.5",
"behat/mink": "@dev",
"behat/mink-browserkit-driver": "^1.3",
"behat/symfony2-extension": "^2.1",
"behat/mink-extension": "^2.3"
},
"require-dev": {
Expand Down
47 changes: 47 additions & 0 deletions src/Behat/ResponseValidatorOpenApiContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Behat\MinkExtension\Context\MinkContext;
use Pccomponentes\OpenApiMessagingContext\OpenApi\JsonSchema;
use Pccomponentes\OpenApiMessagingContext\OpenApi\OpenApiSchemaParser;
use Symfony\Bundle\FrameworkBundle\Client;
use Symfony\Component\Yaml\Yaml;

final class ResponseValidatorOpenApiContext implements Context
Expand Down Expand Up @@ -43,6 +44,27 @@ public function theJsonResponseShouldBeValidAccordingToOpenApiSchema($dumpPath,
$this->validate($responseJson, new JsonSchema(\json_decode(\json_encode($schemaSpec), false)));
}

/**
* @Then the response should be valid according to OpenApi :dumpPath
*/
public function theResponseShouldBeValidAccordingToOpenApi($dumpPath): void
{
$path = realpath($this->rootPath . '/' . $dumpPath);
$this->checkSchemaFile($path);

$currentPath = $this->extractCurrentPath();
$statusCode = $this->extractStatusCode();
$method = $this->extractMethod();
$contentType = $this->extractContentType();

$responseJson = $this->minkContext->getSession()->getPage()->getContent();

$allSpec = Yaml::parse(file_get_contents($path));
$schemaSpec = (new OpenApiSchemaParser($allSpec))->fromResponse($currentPath, $method, $statusCode, $contentType);

$this->validate($responseJson, new JsonSchema(\json_decode(\json_encode($schemaSpec), false)));
}

private function checkSchemaFile($filename): void
{
if (false === is_file($filename)) {
Expand All @@ -61,4 +83,29 @@ private function validate(string $json, JsonSchema $schema): bool

return $schema->validate(\json_decode($json, false), $validator);
}

private function extractMethod(): string
{
/** @var Client $requestClient */
$requestClient = $this->minkContext->getSession()->getDriver()->getClient();
$method = $requestClient->getHistory()->current()->getMethod();

return strtolower($method);
}

private function extractCurrentPath(): string
{
$currentUrl = $this->minkContext->getSession()->getCurrentUrl();
return parse_url($currentUrl)['path'];
}

private function extractStatusCode(): int
{
return $this->minkContext->getSession()->getStatusCode();
}

private function extractContentType(): string
{
return $this->minkContext->getSession()->getResponseHeader('content-type');
}
}
8 changes: 4 additions & 4 deletions src/OpenApi/JsonSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ final class JsonSchema
private $schema;
private $uri;

public function __construct(\stdClass $schema, string $uri = null)
public function __construct($schema, string $uri = null)
{
$this->schema = $schema;
$this->uri = $uri;
}

public function resolve(SchemaStorage $resolver)
public function resolve(SchemaStorage $resolver): JsonSchema
{
if (!$this->hasUri()) {
return $this;
Expand All @@ -27,7 +27,7 @@ public function resolve(SchemaStorage $resolver)
return $this;
}

public function validate(\stdClass $json, Validator $validator)
public function validate($json, Validator $validator): bool
{
$validator->check($json, $this->schema);

Expand All @@ -47,7 +47,7 @@ public function schema(): string
return $this->schema;
}

private function hasUri()
private function hasUri(): bool
{
return null !== $this->uri;
}
Expand Down
66 changes: 66 additions & 0 deletions src/OpenApi/OpenApiSchemaParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,26 @@ public function parse($name): array
return $this->extractData($schemaSpec);
}

public function fromResponse(string $path, string $method, int $statusCode, string $contentType): array
{
$rootPaths = $this->originalContent['paths'];
$this->assertPathRoot($path, $rootPaths);
$pathRoot = $rootPaths[$path];

$this->assertMethodRoot($path, $method, $pathRoot);
$methodRoot = $pathRoot[$method];

$this->assertStatusCodeRoot($path, $method, $statusCode, $methodRoot);
$statusCodeRoot = $methodRoot['responses'][$statusCode];

if (false === \array_key_exists('content', $statusCodeRoot)) {
return [];
}

$this->assertContentTypeRoot($path, $method, $statusCode, $contentType, $statusCodeRoot);
return $this->extractData($statusCodeRoot['content'][$contentType]['schema']);
}

private function extractData(array $data): array
{
$aux = [];
Expand Down Expand Up @@ -49,4 +69,50 @@ private function findDefinition(string $def): array

return $this->extractData(\array_key_exists('payload', $foundDef) ? $foundDef['payload'] : $foundDef);
}

private function assertPathRoot(string $path, $rootPaths): void
{
if (false === \array_key_exists($path, $rootPaths)) {
throw new \InvalidArgumentException(\sprintf('%s path not found', $path));
}
}

private function assertMethodRoot(string $path, string $method, $pathRoot): void
{
if (false === \array_key_exists($method, $pathRoot)) {
throw new \InvalidArgumentException(\sprintf('%s method not found on %s', $method, $path));
}
}

private function assertStatusCodeRoot(string $path, string $method, int $statusCode, $methodRoot): void
{
if (false === \array_key_exists('responses', $methodRoot) || false === \array_key_exists(
$statusCode,
$methodRoot['responses']
)) {
throw new \InvalidArgumentException(
\sprintf('%s response not found on %s path with %s method', $statusCode, $path, $method)
);
}
}

private function assertContentTypeRoot(
string $path,
string $method,
int $statusCode,
string $contentType,
$statusCodeRoot
): void {
if (false === \array_key_exists($contentType, $statusCodeRoot['content'])) {
throw new \InvalidArgumentException(
\sprintf(
'%s content-type not found on %s path with %s method with %s statusCode',
$contentType,
$path,
$method,
$statusCode
)
);
}
}
}

0 comments on commit b32a7f2

Please sign in to comment.