Skip to content

Commit

Permalink
Merge pull request #110 from RonasIT/dpankratov/disable-main-doc-vali…
Browse files Browse the repository at this point in the history
…dation-in-constructor

Dpankratov/disable main doc validation in constructor
  • Loading branch information
astorozhevsky authored Oct 30, 2023
2 parents 1ce8737 + 80b9853 commit 4cf29fb
Show file tree
Hide file tree
Showing 7 changed files with 351 additions and 51 deletions.
96 changes: 52 additions & 44 deletions src/Services/SwaggerService.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class SwaggerService
public const SWAGGER_VERSION = '2.0';

protected $driver;
protected $openAPIValidator;

protected $data;
protected $config;
Expand All @@ -57,6 +58,8 @@ class SwaggerService

public function __construct(Container $container)
{
$this->openAPIValidator = app(SwaggerSpecValidator::class);

$this->initConfig();

$this->setDriver();
Expand All @@ -68,9 +71,7 @@ public function __construct(Container $container)

$this->data = $this->driver->getTmpData();

if (!empty($this->data)) {
$this->validateSpec($this->data);
} else {
if (empty($this->data)) {
$this->data = $this->generateEmptyData();

$this->driver->saveTmpData($this->data);
Expand Down Expand Up @@ -692,55 +693,20 @@ public function getDocFileContent()
{
$documentation = $this->driver->getDocumentation();

$this->openAPIValidator->validate($documentation);

$additionalDocs = config('auto-doc.additional_paths', []);

foreach ($additionalDocs as $filePath) {
$fullFilePath = base_path($filePath);

try {
if (!file_exists($fullFilePath)) {
throw new DocFileNotExistsException($fullFilePath);
}

$fileContent = json_decode(file_get_contents($fullFilePath), true);

if (empty($fileContent)) {
throw new EmptyDocFileException($fullFilePath);
}

$this->validateSpec($fileContent);
$additionalDocContent = $this->getOpenAPIFileContent(base_path($filePath));
} catch (DocFileNotExistsException|EmptyDocFileException|InvalidSwaggerSpecException $exception) {
report($exception);

continue;
}

$paths = array_keys($fileContent['paths']);

foreach ($paths as $path) {
$additionalDocPath = $fileContent['paths'][$path];

if (empty($documentation['paths'][$path])) {
$documentation['paths'][$path] = $additionalDocPath;
} else {
$methods = array_keys($documentation['paths'][$path]);
$additionalDocMethods = array_keys($additionalDocPath);

foreach ($additionalDocMethods as $method) {
if (!in_array($method, $methods)) {
$documentation['paths'][$path][$method] = $additionalDocPath[$method];
}
}
}
}

$definitions = array_keys($fileContent['definitions']);

foreach ($definitions as $definition) {
if (empty($documentation['definitions'][$definition])) {
$documentation['definitions'][$definition] = $fileContent['definitions'][$definition];
}
}
$this->mergeOpenAPIDocs($documentation, $additionalDocContent);
}

return $documentation;
Expand Down Expand Up @@ -871,8 +837,50 @@ protected function prepareInfo(array $info): array
return $info;
}

protected function validateSpec(array $doc): void
protected function getOpenAPIFileContent(string $filePath): array
{
if (!file_exists($filePath)) {
throw new DocFileNotExistsException($filePath);
}

$fileContent = json_decode(file_get_contents($filePath), true);

if (empty($fileContent)) {
throw new EmptyDocFileException($filePath);
}

$this->openAPIValidator->validate($fileContent);

return $fileContent;
}

protected function mergeOpenAPIDocs(array &$documentation, array $additionalDocumentation): void
{
app(SwaggerSpecValidator::class)->validate($doc);
$paths = array_keys($additionalDocumentation['paths']);

foreach ($paths as $path) {
$additionalDocPath = $additionalDocumentation['paths'][$path];

if (empty($documentation['paths'][$path])) {
$documentation['paths'][$path] = $additionalDocPath;
} else {
$methods = array_keys($documentation['paths'][$path]);
$additionalDocMethods = array_keys($additionalDocPath);

foreach ($additionalDocMethods as $method) {
if (!in_array($method, $methods)) {
$documentation['paths'][$path][$method] = $additionalDocPath[$method];
}
}
}
}

$definitions = array_keys($additionalDocumentation['definitions']);

foreach ($definitions as $definition) {
if (empty($documentation['definitions'][$definition])) {
$documentation['definitions'][$definition] = $additionalDocumentation['definitions'][$definition];
}
}
}
}
6 changes: 3 additions & 3 deletions src/Validators/SwaggerSpecValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,9 @@ protected function validateSecurityDefinitions(): void

$this->validateFieldsPresent(self::REQUIRED_FIELDS['security_definition'], $parentId);

$this->validateFieldValue("{$parentId}.'type", self::ALLOWED_VALUES['security_definition_type']);
$this->validateFieldValue("{$parentId}.'in", self::ALLOWED_VALUES['security_definition_in']);
$this->validateFieldValue("{$parentId}.'flow", self::ALLOWED_VALUES['security_definition_flow']);
$this->validateFieldValue("{$parentId}.type", self::ALLOWED_VALUES['security_definition_type']);
$this->validateFieldValue("{$parentId}.in", self::ALLOWED_VALUES['security_definition_in']);
$this->validateFieldValue("{$parentId}.flow", self::ALLOWED_VALUES['security_definition_flow']);
}
}

Expand Down
24 changes: 20 additions & 4 deletions tests/SwaggerServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -271,23 +271,39 @@ public function getConstructorInvalidTmpData(): array
'exceptionMessage' => "Validation failed. Path parameters cannot be optional. "
. "Set required=true for the 'username' parameters at operation 'paths./users.get'."
],
[
'tmpDoc' => 'documentation/invalid_format__security_definition__type',
'exception' => InvalidSwaggerSpecException::class,
'exceptionMessage' => "Validation failed. Field 'securityDefinitions.0.type' has an invalid value: invalid. Allowed values: basic, apiKey, oauth2."
],
[
'tmpDoc' => 'documentation/invalid_format__security_definition__flow',
'exception' => InvalidSwaggerSpecException::class,
'exceptionMessage' => "Validation failed. Field 'securityDefinitions.0.flow' has an invalid value: invalid. Allowed values: implicit, password, application, accessCode."
],
[
'tmpDoc' => 'documentation/invalid_format__security_definition__in',
'exception' => InvalidSwaggerSpecException::class,
'exceptionMessage' => "Validation failed. Field 'securityDefinitions.0.in' has an invalid value: invalid. Allowed values: query, header."
],
];
}

/**
* @dataProvider getConstructorInvalidTmpData
*
* @param string $tmpDoc
* @param string $docFilePath
* @param string $exception
* @param string $exceptionMessage
*/
public function testConstructorInvalidTmpData(string $tmpDoc, string $exception, string $exceptionMessage)
public function testGetDocFileContentInvalidTmpData(string $docFilePath, string $exception, string $exceptionMessage)
{
$this->mockDriverGetTpmData($this->getJsonFixture($tmpDoc));
$this->mockDriverGetDocumentation($this->getJsonFixture($docFilePath));

$this->expectException($exception);
$this->expectExceptionMessage($exceptionMessage);

app(SwaggerService::class);
app(SwaggerService::class)->getDocFileContent();
}

public function testEmptyContactEmail()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
{
"swagger": "2.0",
"host": "localhost",
"basePath": "\/",
"schemes": [],
"paths": {
"\/api\/users":
{
"post":
{
"tags": ["api"],
"consumes": ["application\/x-www-form-urlencoded"],
"produces": ["application\/json"],
"parameters": [
{
"in": "body",
"name": "body",
"description": "",
"required": true,
"schema": {
"$ref": "#/definitions/apiusersObject"
}
}
],
"responses":
{
"403":
{
"description": "Forbidden",
"schema":
{
"example":
{
"message": "This action is unauthorized."
}
}
}
},
"security": [],
"description": "",
"summary": "test"
}
}
},
"definitions": {
"apiusersObject": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": ""
},
"user_id": {
"type": "integer",
"description": "with_to_array_rule_string_name"
},
"is_email_enabled": {
"type": "string",
"description": "test_rule_without_to_string"
}
},
"required": {
"0": "query"
},
"example": {
"first_name": "andrey",
"last_name": "voronin"
}
}
},
"info": {
"description": "This is automatically collected documentation",
"version": "0.0.0",
"title": "Name of Your Application",
"termsOfService": "",
"contact":
{
"email": "[email protected]"
}
},
"securityDefinitions": [
{
"type": "basic",
"in": "query",
"flow": "invalid"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
{
"swagger": "2.0",
"host": "localhost",
"basePath": "\/",
"schemes": [],
"paths": {
"\/api\/users":
{
"post":
{
"tags": ["api"],
"consumes": ["application\/x-www-form-urlencoded"],
"produces": ["application\/json"],
"parameters": [
{
"in": "body",
"name": "body",
"description": "",
"required": true,
"schema": {
"$ref": "#/definitions/apiusersObject"
}
}
],
"responses":
{
"403":
{
"description": "Forbidden",
"schema":
{
"example":
{
"message": "This action is unauthorized."
}
}
}
},
"security": [],
"description": "",
"summary": "test"
}
}
},
"definitions": {
"apiusersObject": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": ""
},
"user_id": {
"type": "integer",
"description": "with_to_array_rule_string_name"
},
"is_email_enabled": {
"type": "string",
"description": "test_rule_without_to_string"
}
},
"required": {
"0": "query"
},
"example": {
"first_name": "andrey",
"last_name": "voronin"
}
}
},
"info": {
"description": "This is automatically collected documentation",
"version": "0.0.0",
"title": "Name of Your Application",
"termsOfService": "",
"contact":
{
"email": "[email protected]"
}
},
"securityDefinitions": [
{
"type": "basic",
"in": "invalid",
"flow": "password"
}
]
}
Loading

0 comments on commit 4cf29fb

Please sign in to comment.