Skip to content

Commit

Permalink
Merge f3b3ff7 into 9b5861b
Browse files Browse the repository at this point in the history
  • Loading branch information
byjg committed Mar 18, 2019
2 parents 9b5861b + f3b3ff7 commit 284b3a4
Show file tree
Hide file tree
Showing 15 changed files with 2,720 additions and 26 deletions.
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@

[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/byjg/php-swagger-test/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/byjg/php-swagger-test/?branch=master)



# MIGRATION to OAS 3.0

## https://blog.readme.io/an-example-filled-guide-to-swagger-3-2/
## https://blog.runscope.com/posts/tutorial-upgrading-swagger-2-api-definition-to-openapi-3



A set of tools for test your REST calls based on the swagger documentation using PHPUnit.

PHP Swagger Test can help you to test your REST Api. You can use this tool both for Unit Tests or Functional Tests.
Expand All @@ -24,14 +33,19 @@ The SwaggerTest's assertion process is based on throwing exceptions if some vali

You can use the Swagger Test as:

- Functional test sases
- Functional test cases
- Unit test cases
- Runtime parameters validator

# OpenAPI 3 Support

Basic OpenAPI 3 Support has been added. This means that a Swagger specification that has been migrated to OpenAPI 3 should work here. The
new OpenAPI 3 features, like callbacks and links, were not implemented. Previous specification versions are still supported.

# Using it as Functional Test cases

Swagger Test provide the class `SwaggerTestCase` for you extend and create a PHPUnit test case. The code will try to
make a request to your API Method and check if the request parameters, status and object returned are OK.
make a request to your API Method and check if the request parameters, status and object returned are OK.

```php
<?php
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
],
"require": {
"guzzlehttp/guzzle": "6.*",
"byjg/uri": "1.0.*",
"php": ">=5.6"
},
"require-dev": {
Expand Down
6 changes: 5 additions & 1 deletion src/SwaggerBody.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ abstract class SwaggerBody
{
const SWAGGER_PROPERTIES="properties";
const SWAGGER_REQUIRED="required";

/**
* @var \ByJG\Swagger\SwaggerSchema
*/
Expand Down Expand Up @@ -266,6 +266,10 @@ protected function matchSchema($name, $schema, $body)
return true;
}

if(!isset($schema['$ref']) && isset($schema['content'])) {
$schema['$ref'] = $schema['content'][key($schema['content'])]['schema']['$ref'];
}

// Get References and try to match it again
if (isset($schema['$ref'])) {
$defintion = $this->swaggerSchema->getDefintion($schema['$ref']);
Expand Down
20 changes: 20 additions & 0 deletions src/SwaggerRequestBody.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,24 @@ class SwaggerRequestBody extends SwaggerBody
*/
public function match($body)
{
if ($this->swaggerSchema->getSpecificationVersion() === '3') {
if (isset($this->structure['content']) || isset($this->structure['$ref'])) {
if (isset($this->structure['required']) && $this->structure['required'] === true && empty($body)) {
throw new RequiredArgumentNotFound('The body is required but it is empty');
}

if (isset($this->structure['$ref'])) {
return $this->matchSchema($this->name, $this->structure, $body);
}

return $this->matchSchema($this->name, $this->structure['content'][key($this->structure['content'])]['schema'], $body);
}

if (!empty($body)) {
throw new InvalidDefinitionException('Body is passed but there is no request body definition');
}
}

foreach ($this->structure as $parameter) {
if ($parameter['in'] == "body") {
if (isset($parameter['required']) && $parameter['required'] === true && empty($body)) {
Expand All @@ -37,6 +55,8 @@ public function match($body)
throw new InvalidDefinitionException('Body is passed but there is no request body definition');
}



return false;
}
}
16 changes: 11 additions & 5 deletions src/SwaggerRequester.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,16 @@ public function send()
);

// Defining Variables
$httpSchema = $this->swaggerSchema->getHttpSchema();
$host = $this->swaggerSchema->getHost();
$basePath = $this->swaggerSchema->getBasePath();
$pathName = $this->path;
$serverUrl = null;
if ($this->swaggerSchema->getSpecificationVersion() === '3') {
$serverUrl = $this->swaggerSchema->getServerUrl();
} else {
$httpSchema = $this->swaggerSchema->getHttpSchema();
$host = $this->swaggerSchema->getHost();
$basePath = $this->swaggerSchema->getBasePath();
$pathName = $this->path;
$serverUrl = "$httpSchema://$host$basePath$pathName$paramInQuery";
}

// Check if the body is the expected before request
$bodyRequestDef = $this->swaggerSchema->getRequestParameters("$basePath$pathName", $this->method);
Expand All @@ -164,7 +170,7 @@ public function send()
// Make the request
$request = new Request(
$this->method,
"$httpSchema://$host$basePath$pathName$paramInQuery",
$serverUrl,
$header,
json_encode($this->requestBody)
);
Expand Down
11 changes: 10 additions & 1 deletion src/SwaggerResponseBody.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,22 @@ class SwaggerResponseBody extends SwaggerBody
*/
public function match($body)
{
if ($this->swaggerSchema->getSpecificationVersion() === '3') {
if (!isset($this->structure['content'])) {
if (!empty($body)) {
throw new NotMatchedException("Expected empty body for " . $this->name);
}
return true;
}
return $this->matchSchema($this->name, $this->structure['content'][key($this->structure['content'])]['schema'], $body);
}

if (!isset($this->structure['schema'])) {
if (!empty($body)) {
throw new NotMatchedException("Expected empty body for " . $this->name);
}
return true;
}

return $this->matchSchema($this->name, $this->structure['schema'], $body);
}
}
116 changes: 101 additions & 15 deletions src/SwaggerSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,37 @@
use ByJG\Swagger\Exception\InvalidDefinitionException;
use ByJG\Swagger\Exception\NotMatchedException;
use ByJG\Swagger\Exception\PathNotFoundException;
use ByJG\Util\Uri;

class SwaggerSchema
{
protected $jsonFile;
protected $allowNullValues;

protected $specificationVersion;

const SWAGGER_PATHS="paths";
const SWAGGER_PARAMETERS="parameters";
const SWAGGER_COMPONENTS="components";

public function __construct($jsonFile, $allowNullValues = false)
{
$this->jsonFile = json_decode($jsonFile, true);
$this->allowNullValues = (bool) $allowNullValues;
$this->specificationVersion = isset($this->jsonFile['swagger']) ? '2' : '3';
}

/**
* Returns the major specification version
* @return string
*/
public function getSpecificationVersion()
{
return $this->specificationVersion;
}

public function getServerUrl()
{
return isset($this->jsonFile['servers']) ? $this->jsonFile['servers'][0]['url'] : '';
}

public function getHttpSchema()
Expand All @@ -39,27 +57,36 @@ public function getHost()

public function getBasePath()
{
if ($this->getSpecificationVersion() === '3') {
$basePath =isset($this->jsonFile['servers']) ? explode('/', $this->jsonFile['servers'][0]['url']) : '';
return is_array($basePath) ? '/' . end($basePath) : $basePath;
}

return isset($this->jsonFile['basePath']) ? $this->jsonFile['basePath'] : '';
}

/**
* @param $path
* @param $method
* @return mixed
* @throws \ByJG\Swagger\Exception\HttpMethodNotFoundException
* @throws \ByJG\Swagger\Exception\NotMatchedException
* @throws \ByJG\Swagger\Exception\PathNotFoundException
* @throws DefinitionNotFoundException
* @throws HttpMethodNotFoundException
* @throws InvalidDefinitionException
* @throws NotMatchedException
* @throws PathNotFoundException
*/
public function getPathDefinition($path, $method)
{
$method = strtolower($method);

$path = preg_replace('~^' . $this->getBasePath() . '~', '', $path);

$uri = new Uri($path);

// Try direct match
if (isset($this->jsonFile[self::SWAGGER_PATHS][$path])) {
if (isset($this->jsonFile[self::SWAGGER_PATHS][$path][$method])) {
return $this->jsonFile[self::SWAGGER_PATHS][$path][$method];
if (isset($this->jsonFile[self::SWAGGER_PATHS][$uri->getPath()])) {
if (isset($this->jsonFile[self::SWAGGER_PATHS][$uri->getPath()][$method])) {
return $this->jsonFile[self::SWAGGER_PATHS][$uri->getPath()][$method];
}
throw new HttpMethodNotFoundException("The http method '$method' not found in '$path'");
}
Expand All @@ -73,13 +100,24 @@ public function getPathDefinition($path, $method)
$pathItemPattern = '~^' . preg_replace('~\{(.*?)\}~', '(?<\1>[^/]+)', $pathItem) . '$~';

$matches = [];
if (preg_match($pathItemPattern, $path, $matches)) {
if (preg_match($pathItemPattern, $uri->getPath(), $matches)) {
$pathDef = $this->jsonFile[self::SWAGGER_PATHS][$pathItem];
if (!isset($pathDef[$method])) {
throw new HttpMethodNotFoundException("The http method '$method' not found in '$path'");
}

$this->validateArguments('path', $pathDef[$method][self::SWAGGER_PARAMETERS], $matches);
$parametersPathMethod = [];
$parametersPath = [];

if (isset($pathDef[$method][self::SWAGGER_PARAMETERS])) {
$parametersPathMethod = $pathDef[$method][self::SWAGGER_PARAMETERS];
}

if (isset($pathDef[self::SWAGGER_PARAMETERS])) {
$parametersPath = $pathDef[self::SWAGGER_PARAMETERS];
}

$this->validateArguments('path', array_merge($parametersPathMethod, $parametersPath), $matches);

return $pathDef[$method];
}
Expand All @@ -92,10 +130,37 @@ public function getPathDefinition($path, $method)
* @param $parameterIn
* @param $parameters
* @param $arguments
* @throws \ByJG\Swagger\Exception\NotMatchedException
* @throws DefinitionNotFoundException
* @throws InvalidDefinitionException
* @throws NotMatchedException
*/
private function validateArguments($parameterIn, $parameters, $arguments)
{
if ($this->getSpecificationVersion() === '3') {
foreach ($parameters as $parameter) {
if (isset($parameter['$ref'])) {
$paramParts = explode("/", $parameter['$ref']);
if (count($paramParts) != 4 || $paramParts[0] != "#" || $paramParts[1] != self::SWAGGER_COMPONENTS || $paramParts[2] != self::SWAGGER_PARAMETERS) {
throw new InvalidDefinitionException(
"Not get the reference in the expected format #/components/parameters/<NAME>"
);
}
if (!isset($this->jsonFile[self::SWAGGER_COMPONENTS][self::SWAGGER_PARAMETERS][$paramParts[3]])) {
throw new DefinitionNotFoundException(
"Not find reference #/components/parameters/${paramParts[3]}"
);
}
$parameter = $this->jsonFile[self::SWAGGER_COMPONENTS][self::SWAGGER_PARAMETERS][$paramParts[3]];
}
if ($parameter['in'] === $parameterIn &&
$parameter['schema']['type'] === "integer"
&& filter_var($arguments[$parameter['name']], FILTER_VALIDATE_INT) === false) {
throw new NotMatchedException('Path expected an integer value');
}
}
return;
}

foreach ($parameters as $parameter) {
if ($parameter['in'] === $parameterIn
&& $parameter['type'] === "integer"
Expand All @@ -115,7 +180,19 @@ public function getDefintion($name)
{
$nameParts = explode('/', $name);

if (count($nameParts) < 3 || $nameParts[0] != '#') {
if ($this->getSpecificationVersion() === '3') {
if (count($nameParts) < 4 || $nameParts[0] !== '#') {
throw new InvalidDefinitionException('Invalid Component');
}

if (!isset($this->jsonFile[$nameParts[1]][$nameParts[2]][$nameParts[3]])) {
throw new DefinitionNotFoundException("Component'$name' not found");
}

return $this->jsonFile[$nameParts[1]][$nameParts[2]][$nameParts[3]];
}

if (count($nameParts) < 3 || $nameParts[0] !== '#') {
throw new InvalidDefinitionException('Invalid Definition');
}

Expand All @@ -130,18 +207,26 @@ public function getDefintion($name)
* @param $path
* @param $method
* @return \ByJG\Swagger\SwaggerRequestBody
* @throws \ByJG\Swagger\Exception\HttpMethodNotFoundException
* @throws \ByJG\Swagger\Exception\NotMatchedException
* @throws \ByJG\Swagger\Exception\PathNotFoundException
* @throws DefinitionNotFoundException
* @throws HttpMethodNotFoundException
* @throws InvalidDefinitionException
* @throws NotMatchedException
* @throws PathNotFoundException
*/
public function getRequestParameters($path, $method)
{
$structure = $this->getPathDefinition($path, $method);

if($this->getSpecificationVersion() === '3') {
if (!isset($structure['requestBody'])) {
return new SwaggerRequestBody($this, "$method $path", []);
}
return new SwaggerRequestBody($this, "$method $path", $structure['requestBody']);
}

if (!isset($structure[self::SWAGGER_PARAMETERS])) {
return new SwaggerRequestBody($this, "$method $path", []);
}

return new SwaggerRequestBody($this, "$method $path", $structure[self::SWAGGER_PARAMETERS]);
}

Expand All @@ -154,6 +239,7 @@ public function getRequestParameters($path, $method)
* @throws \ByJG\Swagger\Exception\InvalidDefinitionException
* @throws \ByJG\Swagger\Exception\NotMatchedException
* @throws \ByJG\Swagger\Exception\PathNotFoundException
* @throws DefinitionNotFoundException
*/
public function getResponseParameters($path, $method, $status)
{
Expand Down
Loading

0 comments on commit 284b3a4

Please sign in to comment.