Skip to content

Commit

Permalink
Merge pull request #7 from Makedo/issue-6
Browse files Browse the repository at this point in the history
Issue 6
  • Loading branch information
byjg committed Nov 6, 2017
2 parents 9de661c + dfcd76c commit dcbb116
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 76 deletions.
8 changes: 8 additions & 0 deletions composer.json
Expand Up @@ -13,9 +13,17 @@
"require": {
"guzzlehttp/guzzle": "6.*"
},
"require-dev": {
"phpunit/phpunit": "5.*"
},
"autoload": {
"psr-4": {
"ByJG\\Swagger\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Test\\": "tests/"
}
}
}
49 changes: 42 additions & 7 deletions src/SwaggerBody.php
Expand Up @@ -15,21 +15,31 @@ abstract class SwaggerBody

protected $name;

/**
* OpenApi 2.0 does not describe null values, so this flag defines,
* if match is ok when one of property, which has type, is null
*
* @var bool
*/
protected $allowNullValues;

/**
* SwaggerRequestBody constructor.
*
* @param \ByJG\Swagger\SwaggerSchema $swaggerSchema
* @param string $name
* @param array $structure
* @param bool $allowNullValues
*/
public function __construct(SwaggerSchema $swaggerSchema, $name, $structure)
public function __construct(SwaggerSchema $swaggerSchema, $name, $structure, $allowNullValues = false)
{
$this->swaggerSchema = $swaggerSchema;
$this->name = $name;
if (!is_array($structure)) {
throw new \InvalidArgumentException('I expected the structure to be an array');
}
$this->structure = $structure;
$this->allowNullValues = $allowNullValues;
}

abstract public function match($body);
Expand Down Expand Up @@ -85,19 +95,25 @@ protected function matchArray($name, $schema, $body)
protected function matchSchema($name, $schema, $body)
{
if (isset($schema['type'])) {
if ($schema['type'] == 'string') {

$type = $schema['type'];
if (is_null($body)) {
return $this->matchNull($name, $type);
}

if ($type == 'string') {
return $this->matchString($name, $schema, $body);
}

if ($schema['type'] == 'integer' || $schema['type'] == 'float' || $schema['type'] == 'number') {
if ($type == 'integer' || $type == 'float' || $schema['type'] == 'number') {
return $this->matchNumber($name, $body);
}

if ($schema['type'] == 'bool' || $schema['type'] == 'boolean') {
if ($type == 'bool' || $schema['type'] == 'boolean') {
return $this->matchBool($name, $body);
}

if ($schema['type'] == 'array') {
if ($type == 'array') {
return $this->matchArray($name, $schema, $body);
}
}
Expand All @@ -113,14 +129,15 @@ protected function matchSchema($name, $schema, $body)
}
foreach ($schema['properties'] as $prop => $def) {
$required = array_search($prop, $schema['required']);
// if (!array_key_exists($prop, $body)) {
if (!isset($body[$prop])) {

if (!array_key_exists($prop, $body)) {
if ($required !== false) {
throw new NotMatchedException("Required property '$prop' in '$name' not found in object");
}
unset($body[$prop]);
continue;
}

$this->matchSchema($prop, $def, $body[$prop]);
unset($schema['properties'][$prop]);
if ($required !== false) {
Expand Down Expand Up @@ -151,4 +168,22 @@ protected function matchSchema($name, $schema, $body)

throw new \Exception("Not all cases are defined. Please open an issue about this. Schema: $name");
}

/**
* @param $name
* @param $type
* @return bool
* @throws NotMatchedException
*/
protected function matchNull($name, $type)
{
if (false === $this->swaggerSchema->isAllowNullValues()) {
throw new NotMatchedException(
"Value of property '$name' is null, but should be of type '$type'",
$this->structure
);
}

return true;
}
}
15 changes: 14 additions & 1 deletion src/SwaggerSchema.php
Expand Up @@ -16,10 +16,12 @@
class SwaggerSchema
{
protected $jsonFile;
protected $allowNullValues;

public function __construct($jsonFile)
public function __construct($jsonFile, $allowNullValues = false)
{
$this->jsonFile = json_decode($jsonFile, true);
$this->allowNullValues = (bool) $allowNullValues;
}

public function getHttpSchema()
Expand Down Expand Up @@ -123,4 +125,15 @@ public function getResponseParameters($path, $method, $status)

return new SwaggerResponseBody($this, "$method $status $path", $structure['responses'][$status]);
}

/**
* OpenApi 2.0 doesn't describe null values, so this flag defines,
* if match is ok when one of property
*
* @return bool
*/
public function isAllowNullValues()
{
return $this->allowNullValues;
}
}
30 changes: 30 additions & 0 deletions tests/SwaggerBodyTestCase.php
@@ -0,0 +1,30 @@
<?php

namespace Test;

use ByJG\Swagger\SwaggerSchema;
use PHPUnit\Framework\TestCase;

class SwaggerBodyTestCase extends TestCase
{

/**
* @param bool $allowNullValues
* @return SwaggerSchema
*/
protected static function swaggerSchema($allowNullValues = false)
{
return new SwaggerSchema(
self::getSwaggerJsonContent(),
$allowNullValues
);
}

/**
* @return string
*/
protected static function getSwaggerJsonContent()
{
return file_get_contents(__DIR__ . '/example/swagger.json');
}
}
59 changes: 24 additions & 35 deletions tests/SwaggerRequestBodyTest.php
Expand Up @@ -7,31 +7,8 @@

namespace Test;

use ByJG\Swagger\SwaggerSchema;
use PHPUnit\Framework\TestCase;

// backward compatibility
if (!class_exists('\PHPUnit\Framework\TestCase')) {
class_alias('\PHPUnit_Framework_TestCase', '\PHPUnit\Framework\TestCase');
}

class SwaggerRequestBodyTest extends TestCase
class SwaggerRequestBodyTest extends SwaggerBodyTestCase
{
/**
* @var SwaggerSchema
*/
protected $object;

public function setUp()
{
$this->object = new SwaggerSchema(file_get_contents(__DIR__ . '/example/swagger.json'));
}

public function tearDown()
{
$this->object = null;
}

public function testMatchRequestBody()
{
$body = [
Expand All @@ -42,7 +19,7 @@ public function testMatchRequestBody()
"status" => 'placed',
"complete" => true
];
$requestParameter = $this->object->getRequestParameters('/v2/store/order', 'post');
$requestParameter = self::swaggerSchema()->getRequestParameters('/v2/store/order', 'post');
$this->assertTrue($requestParameter->match($body));
}

Expand All @@ -52,7 +29,7 @@ public function testMatchRequestBody()
*/
public function testMatchRequiredRequestBodyEmpty()
{
$requestParameter = $this->object->getRequestParameters('/v2/store/order', 'post');
$requestParameter = self::swaggerSchema()->getRequestParameters('/v2/store/order', 'post');
$this->assertTrue($requestParameter->match(null));
}

Expand All @@ -62,7 +39,7 @@ public function testMatchRequiredRequestBodyEmpty()
*/
public function testMatchInexistantBodyDefinition()
{
$requestParameter = $this->object->getRequestParameters('/v2/pet/1', 'get');
$requestParameter = self::swaggerSchema()->getRequestParameters('/v2/pet/1', 'get');
$body = [
"id" => "10",
"petId" => 50,
Expand All @@ -80,7 +57,7 @@ public function testMatchInexistantBodyDefinition()
*/
public function testMatchDataType()
{
$this->object->getRequestParameters('/v2/pet/STRING', 'get');
self::swaggerSchema()->getRequestParameters('/v2/pet/STRING', 'get');
$this->assertTrue(true);
}

Expand All @@ -94,27 +71,39 @@ public function testMatchRequestBodyRequired1()
"id" => "10",
"status" => "pending",
];
$requestParameter = $this->object->getRequestParameters('/v2/pet', 'post');
$requestParameter = self::swaggerSchema()->getRequestParameters('/v2/pet', 'post');
$this->assertTrue($requestParameter->match($body));
}

/**
* It is not OK { name: null }
* It is not OK when allowNullValues is false (as by default) { name: null }
* https://stackoverflow.com/questions/45575493/what-does-required-in-openapi-really-mean
* @todo Add the "nullable" validation. (have to add the test case also)
*
* @expectedException \ByJG\Swagger\Exception\NotMatchedException
* @expectedExceptionMessage Required property
* @expectedExceptionMessage Value of property 'name' is null, but should be of type 'string'
*/
public function testMatchRequestBodyRequired2()
public function testMatchRequestBodyRequiredNullsNotAllowed()
{
$body = [
"id" => "10",
"status" => "pending",
"name" => null,
"photoUrls" => ["http://example.com/1", "http://example.com/2"]
];
$requestParameter = self::swaggerSchema()->getRequestParameters('/v2/pet', 'post');
$this->assertTrue($requestParameter->match($body));
}

public function testMatchRequestBodyRequiredNullsAllowed()
{
$allowNullValues = true;
$body = [
"id" => "10",
"status" => "pending",
"name" => null,
"photoUrls" => ["http://example.com/1", "http://example.com/2"]
];
$requestParameter = $this->object->getRequestParameters('/v2/pet', 'post');
$requestParameter = self::swaggerSchema($allowNullValues)->getRequestParameters('/v2/pet', 'post');
$this->assertTrue($requestParameter->match($body));
}

Expand All @@ -130,7 +119,7 @@ public function testMatchRequestBodyRequired3()
"name" => "",
"photoUrls" => ["http://example.com/1", "http://example.com/2"]
];
$requestParameter = $this->object->getRequestParameters('/v2/pet', 'post');
$requestParameter = self::swaggerSchema()->getRequestParameters('/v2/pet', 'post');
$this->assertTrue($requestParameter->match($body));
}
}

0 comments on commit dcbb116

Please sign in to comment.