diff --git a/src/Exceptions/SpecValidation/InvalidFieldValueException.php b/src/Exceptions/SpecValidation/InvalidFieldValueException.php index d2efac32..d5600647 100644 --- a/src/Exceptions/SpecValidation/InvalidFieldValueException.php +++ b/src/Exceptions/SpecValidation/InvalidFieldValueException.php @@ -9,6 +9,8 @@ public function __construct(string $fieldName, array $allowedValues, array $inva $allowedValuesString = implode(', ', $allowedValues); $invalidValuesString = implode(', ', $invalidValues); - parent::__construct("Field '{$fieldName}' has an invalid value: {$invalidValuesString}. Allowed values: {$allowedValuesString}."); + parent::__construct( + "Field '{$fieldName}' has an invalid value: {$invalidValuesString}. Allowed values: {$allowedValuesString}." + ); } } diff --git a/src/Exceptions/SpecValidation/InvalidStatusCodeException.php b/src/Exceptions/SpecValidation/InvalidStatusCodeException.php index fde3e03b..02763d0f 100644 --- a/src/Exceptions/SpecValidation/InvalidStatusCodeException.php +++ b/src/Exceptions/SpecValidation/InvalidStatusCodeException.php @@ -6,6 +6,9 @@ class InvalidStatusCodeException extends InvalidSwaggerSpecException { public function __construct(string $responseId) { - parent::__construct("Operation at '{$responseId}' should only have three-digit status codes, `default`, and vendor extensions (`x-*`) as properties."); + parent::__construct( + "Operation at '{$responseId}' should only have three-digit status codes, `default`, " + . "and vendor extensions (`x-*`) as properties." + ); } } diff --git a/src/Validators/SwaggerSpecValidator.php b/src/Validators/SwaggerSpecValidator.php index dd3e8d92..2a77c3b7 100644 --- a/src/Validators/SwaggerSpecValidator.php +++ b/src/Validators/SwaggerSpecValidator.php @@ -177,7 +177,9 @@ protected function validateResponse(array $response, string $statusCode, string } if (!empty($response['schema'])) { - $this->validateType($response['schema'], array_merge(self::SCHEMA_TYPES, ['file']), "{$responseId}.schema"); + $this->validateType( + $response['schema'], array_merge(self::SCHEMA_TYPES, ['file']), "{$responseId}.schema" + ); } if (!empty($response['items'])) { @@ -195,7 +197,9 @@ protected function validateParameters(array $operation, string $path, string $op $this->validateFieldsPresent(self::REQUIRED_FIELDS['parameter'], $paramId); $this->validateFieldValue("{$paramId}.in", self::ALLOWED_VALUES['parameter_in']); - $this->validateFieldValue("{$paramId}.collectionFormat", self::ALLOWED_VALUES['parameter_collection_format']); + $this->validateFieldValue( + "{$paramId}.collectionFormat", self::ALLOWED_VALUES['parameter_collection_format'] + ); $this->validateParameterType($param, $operation, $paramId, $operationId); @@ -226,7 +230,7 @@ protected function validateType(array $schema, array $validTypes, string $schema protected function validatePathParameters(array $params, string $path, string $operationId): void { $pathParams = Arr::where($params, function ($param) { - return ($param['in'] === 'path'); + return $param['in'] === 'path'; }); preg_match_all(self::PATH_PARAM_REGEXP, $path, $matches); @@ -243,7 +247,10 @@ protected function validatePathParameters(array $params, string $path, string $o })); if (!empty($requiredParams)) { - throw new InvalidSwaggerSpecException("Path parameters cannot be optional. Set required=true for the '{$requiredParams}' parameters at operation '{$operationId}'."); + throw new InvalidSwaggerSpecException( + "Path parameters cannot be optional. Set required=true for the " + . "'{$requiredParams}' parameters at operation '{$operationId}'." + ); } $missingPlaceholders = array_diff(Arr::pluck($pathParams, 'name'), $placeholders); @@ -265,11 +272,15 @@ protected function validateBodyParameters(array $parameters, string $operationId $formParamsCount = collect($parameters)->where('in', 'formData')->count(); if ($bodyParamsCount > 1) { - throw new InvalidSwaggerSpecException("Operation '{$operationId}' has {$bodyParamsCount} body parameters. Only one is allowed."); + throw new InvalidSwaggerSpecException( + "Operation '{$operationId}' has {$bodyParamsCount} body parameters. Only one is allowed." + ); } if (!empty($bodyParams) && $formParamsCount) { - throw new InvalidSwaggerSpecException("Operation '{$operationId}' has body and formData parameters. Only one or the other is allowed."); + throw new InvalidSwaggerSpecException( + "Operation '{$operationId}' has body and formData parameters. Only one or the other is allowed." + ); } } @@ -358,7 +369,9 @@ protected function validateRefs(): void if (!empty($refFilename) && !file_exists($refFilename)) { throw new MissingRefFileException($refFilename); - } elseif (!empty($refFilename) && file_exists($refFilename)) { + } + + if (!empty($refFilename)) { $externalDoc = json_decode(file_get_contents($refFilename), true); $missingRefs = $this->getMissingFields([$refKey], $refParentKey, $externalDoc); @@ -426,7 +439,9 @@ function ($consume) { ); if (empty($requiredConsume)) { - throw new InvalidSwaggerSpecException("Operation '{$operationId}' has body and formData parameters. Only one or the other is allowed."); + throw new InvalidSwaggerSpecException( + "Operation '{$operationId}' has body and formData parameters. Only one or the other is allowed." + ); } } diff --git a/tests/AutoDocControllerTest.php b/tests/AutoDocControllerTest.php index 355a12b0..b74eae67 100644 --- a/tests/AutoDocControllerTest.php +++ b/tests/AutoDocControllerTest.php @@ -52,15 +52,17 @@ public function testGetJSONDocumentationWithAdditionalPaths() public function getJSONDocumentationInvalidAdditionalDoc(): array { + $basePath = 'tests/fixtures/AutoDocControllerTest'; + return [ [ 'additionalDocPath' => 'invalid_path/non_existent_file.json' ], [ - 'additionalDocPath' => 'tests/fixtures/AutoDocControllerTest/documentation__non_json.txt' + 'additionalDocPath' => $basePath . '/documentation__non_json.txt' ], [ - 'additionalDocPath' => 'tests/fixtures/AutoDocControllerTest/documentation__invalid_format__missing_field__paths.json' + 'additionalDocPath' => $basePath. '/documentation__invalid_format__missing_field__paths.json' ] ]; } @@ -174,7 +176,9 @@ public function testGetElementsAssetFile() $response->assertStatus(Response::HTTP_OK); - $this->assertEquals($response->getContent(), file_get_contents(resource_path('/assets/elements/web-components.min.js'))); + $this->assertEquals( + $response->getContent(), file_get_contents(resource_path('/assets/elements/web-components.min.js')) + ); $response->assertHeader('Content-Type', 'text/html; charset=UTF-8'); } diff --git a/tests/SwaggerServiceTest.php b/tests/SwaggerServiceTest.php index 58264860..dada6186 100755 --- a/tests/SwaggerServiceTest.php +++ b/tests/SwaggerServiceTest.php @@ -77,42 +77,50 @@ public function getConstructorInvalidTmpData(): array [ 'tmpDoc' => 'documentation__invalid_format__array_parameter__no_items', 'exception' => InvalidSwaggerSpecException::class, - 'exceptionMessage' => "Validation failed. paths./users.post.parameters.0 is an array, so it must include an 'items' field." + 'exceptionMessage' => "Validation failed. paths./users.post.parameters.0 is an " + . "array, so it must include an 'items' field." ], [ 'tmpDoc' => 'documentation__invalid_format__array_response_body__no_items', 'exception' => InvalidSwaggerSpecException::class, - 'exceptionMessage' => "Validation failed. paths./users.get.responses.200.schema is an array, so it must include an 'items' field." + 'exceptionMessage' => "Validation failed. paths./users.get.responses.200.schema is an array, " + . "so it must include an 'items' field." ], [ 'tmpDoc' => 'documentation__invalid_format__array_response_header__no_items', 'exception' => InvalidSwaggerSpecException::class, - 'exceptionMessage' => "Validation failed. paths./users.get.responses.default.headers.Last-Modified is an array, so it must include an 'items' field." + 'exceptionMessage' => "Validation failed. paths./users.get.responses.default.headers." + . "Last-Modified is an array, so it must include an 'items' field." ], [ 'tmpDoc' => 'documentation__invalid_format__body_and_form_params', 'exception' => InvalidSwaggerSpecException::class, - 'exceptionMessage' => "Validation failed. Operation 'paths./users/{username}.post' has body and formData parameters. Only one or the other is allowed." + 'exceptionMessage' => "Validation failed. Operation 'paths./users/{username}.post' " + . "has body and formData parameters. Only one or the other is allowed." ], [ 'tmpDoc' => 'documentation__invalid_format__duplicate_header_params', 'exception' => DuplicateParamException::class, - 'exceptionMessage' => "Validation failed. Operation 'paths./users/{username}.get' has multiple in:header parameters with name:foo." + 'exceptionMessage' => "Validation failed. Operation 'paths./users/{username}.get' " + . "has multiple in:header parameters with name:foo." ], [ 'tmpDoc' => 'documentation__invalid_format__duplicate_path_params', 'exception' => DuplicateParamException::class, - 'exceptionMessage' => "Validation failed. Operation 'paths./users/{username}.get' has multiple in:path parameters with name:username" + 'exceptionMessage' => "Validation failed. Operation 'paths./users/{username}.get' has " + . "multiple in:path parameters with name:username" ], [ 'tmpDoc' => 'documentation__invalid_format__duplicate_path_placeholders', 'exception' => DuplicatePathPlaceholderException::class, - 'exceptionMessage' => "Validation failed. Path '/users/{username}/profile/{username}/image/{img_id}' has multiple path placeholders with name: username." + 'exceptionMessage' => "Validation failed. Path '/users/{username}/profile/{username}/image/{img_id}' " + . "has multiple path placeholders with name: username." ], [ 'tmpDoc' => 'documentation__invalid_format__duplicate_operation_id', 'exception' => DuplicateFieldException::class, - 'exceptionMessage' => "Validation failed. Found multiple fields 'paths.*.*.operationId' with values: addPet." + 'exceptionMessage' => "Validation failed. Found multiple fields 'paths.*.*.operationId' " + . "with values: addPet." ], [ 'tmpDoc' => 'documentation__invalid_format__duplicate_tag', @@ -122,42 +130,50 @@ public function getConstructorInvalidTmpData(): array [ 'tmpDoc' => 'documentation__invalid_format__file_invalid_consumes', 'exception' => InvalidSwaggerSpecException::class, - 'exceptionMessage' => "Validation failed. Operation 'paths./users/{username}/profile/image.post' has body and formData parameters. Only one or the other is allowed." + 'exceptionMessage' => "Validation failed. Operation 'paths./users/{username}/profile/image.post' " + . "has body and formData parameters. Only one or the other is allowed." ], [ 'tmpDoc' => 'documentation__invalid_format__file_no_consumes', 'exception' => InvalidSwaggerSpecException::class, - 'exceptionMessage' => "Validation failed. Operation 'paths./users/{username}/profile/image.post' has body and formData parameters. Only one or the other is allowed." + 'exceptionMessage' => "Validation failed. Operation 'paths./users/{username}/profile/image.post' " + . "has body and formData parameters. Only one or the other is allowed." ], [ 'tmpDoc' => 'documentation__invalid_format__multiple_body_params', 'exception' => InvalidSwaggerSpecException::class, - 'exceptionMessage' => "Validation failed. Operation 'paths./users/{username}.get' has 2 body parameters. Only one is allowed." + 'exceptionMessage' => "Validation failed. Operation 'paths./users/{username}.get' has 2 body " + . "parameters. Only one is allowed." ], [ 'tmpDoc' => 'documentation__invalid_format__no_path_params', 'exception' => MissingPathParamException::class, - 'exceptionMessage' => "Validation failed. Operation 'paths./users/{username}/{foo}.get' has no params for placeholders: username, foo." + 'exceptionMessage' => "Validation failed. Operation 'paths./users/{username}/{foo}.get' has " + . "no params for placeholders: username, foo." ], [ 'tmpDoc' => 'documentation__invalid_format__path_param_no_placeholder', 'exception' => MissingPathPlaceholderException::class, - 'exceptionMessage' => "Validation failed. Operation 'paths./users/{username}.post' has no placeholders for params: foo." + 'exceptionMessage' => "Validation failed. Operation 'paths./users/{username}.post' has no " + . "placeholders for params: foo." ], [ 'tmpDoc' => 'documentation__invalid_format__invalid_value__path', 'exception' => InvalidPathException::class, - 'exceptionMessage' => "Validation failed. Incorrect 'paths.users'. Paths should only have path names that starts with `/`." + 'exceptionMessage' => "Validation failed. Incorrect 'paths.users'. Paths should only have path " + . "names that starts with `/`." ], [ 'tmpDoc' => 'documentation__invalid_format__invalid_value__status_code', 'exception' => InvalidStatusCodeException::class, - 'exceptionMessage' => "Validation failed. Operation at 'paths./users.get.responses.8888' should only have three-digit status codes, `default`, and vendor extensions (`x-*`) as properties." + 'exceptionMessage' => "Validation failed. Operation at 'paths./users.get.responses.8888' should " + . "only have three-digit status codes, `default`, and vendor extensions (`x-*`) as properties." ], [ 'tmpDoc' => 'documentation__invalid_format__invalid_value__parameter_in', 'exception' => InvalidFieldValueException::class, - 'exceptionMessage' => "Validation failed. Field 'paths./auth/login.post.parameters.0.in' has an invalid value: invalid_in. Allowed values: body, formData, query, path, header." + 'exceptionMessage' => "Validation failed. Field 'paths./auth/login.post.parameters.0.in' " + . "has an invalid value: invalid_in. Allowed values: body, formData, query, path, header." ], [ 'tmpDoc' => 'documentation__invalid_format__missing_field__paths', @@ -167,22 +183,26 @@ public function getConstructorInvalidTmpData(): array [ 'tmpDoc' => 'documentation__invalid_format__missing_field__operation_responses', 'exception' => MissingFieldException::class, - 'exceptionMessage' => "Validation failed. 'paths./auth/login.post' should have required fields: responses." + 'exceptionMessage' => "Validation failed. 'paths./auth/login.post' should have required " + . "fields: responses." ], [ 'tmpDoc' => 'documentation__invalid_format__missing_field__parameter_in', 'exception' => MissingFieldException::class, - 'exceptionMessage' => "Validation failed. 'paths./auth/login.post.parameters.0' should have required fields: in." + 'exceptionMessage' => "Validation failed. 'paths./auth/login.post.parameters.0' should " + . "have required fields: in." ], [ 'tmpDoc' => 'documentation__invalid_format__missing_field__response_description', 'exception' => MissingFieldException::class, - 'exceptionMessage' => "Validation failed. 'paths./auth/login.post.responses.200' should have required fields: description." + 'exceptionMessage' => "Validation failed. 'paths./auth/login.post.responses.200' should " + . "have required fields: description." ], [ 'tmpDoc' => 'documentation__invalid_format__missing_field__definition_type', 'exception' => MissingFieldException::class, - 'exceptionMessage' => "Validation failed. 'definitions.authloginObject' should have required fields: type." + 'exceptionMessage' => "Validation failed. 'definitions.authloginObject' should have " + . "required fields: type." ], [ 'tmpDoc' => 'documentation__invalid_format__missing_field__info_version', @@ -192,12 +212,14 @@ public function getConstructorInvalidTmpData(): array [ 'tmpDoc' => 'documentation__invalid_format__missing_field__items_type', 'exception' => MissingFieldException::class, - 'exceptionMessage' => "Validation failed. 'paths./pet/findByStatus.get.parameters.0.items' should have required fields: type." + 'exceptionMessage' => "Validation failed. 'paths./pet/findByStatus.get.parameters.0.items' " + . "should have required fields: type." ], [ 'tmpDoc' => 'documentation__invalid_format__missing_field__header_type', 'exception' => MissingFieldException::class, - 'exceptionMessage' => "Validation failed. 'paths./user/login.get.responses.200.headers.X-Rate-Limit' should have required fields: type." + 'exceptionMessage' => "Validation failed. 'paths./user/login.get.responses.200.headers.X-Rate-Limit' " + . "should have required fields: type." ], [ 'tmpDoc' => 'documentation__invalid_format__missing_field__tag_name', @@ -207,17 +229,20 @@ public function getConstructorInvalidTmpData(): array [ 'tmpDoc' => 'documentation__invalid_format__missing_local_ref', 'exception' => MissingLocalRefException::class, - 'exceptionMessage' => "Validation failed. Ref 'loginObject' is used in \$ref but not defined in 'definitions' field." + 'exceptionMessage' => "Validation failed. Ref 'loginObject' is used in \$ref but not defined " + . "in 'definitions' field." ], [ 'tmpDoc' => 'documentation__invalid_format__missing_external_ref', 'exception' => MissingExternalRefException::class, - 'exceptionMessage' => "Validation failed. Ref 'authloginObject' is used in \$ref but not defined in 'tests/fixtures/SwaggerServiceTest/documentation__with_definitions.json' file." + 'exceptionMessage' => "Validation failed. Ref 'authloginObject' is used in \$ref but not defined " + . "in 'tests/fixtures/SwaggerServiceTest/documentation__with_definitions.json' file." ], [ 'tmpDoc' => 'documentation__invalid_format__missing_ref_file', 'exception' => MissingRefFileException::class, - 'exceptionMessage' => "Validation failed. Filename 'invalid-filename.json' is used in \$ref but file doesn't exist." + 'exceptionMessage' => "Validation failed. Filename 'invalid-filename.json' is used in \$ref but " + . "file doesn't exist." ], ]; } @@ -282,7 +307,9 @@ public function testAddDataRequestWithEmptyDataAndInfo() { config(['auto-doc.info' => []]); - $this->mockDriverGetEmptyAndSaveTpmData([], $this->getJsonFixture('tmp_data_request_with_empty_data_and_info')); + $this->mockDriverGetEmptyAndSaveTpmData( + [], $this->getJsonFixture('tmp_data_request_with_empty_data_and_info') + ); app(SwaggerService::class); } @@ -343,7 +370,9 @@ public function testAddData(?string $contentType, string $requestFixture, string public function testAddDataRequestWithoutRuleType() { - $this->mockDriverGetEmptyAndSaveTpmData($this->getJsonFixture('tmp_data_search_roles_request_without_rule_type')); + $this->mockDriverGetEmptyAndSaveTpmData( + $this->getJsonFixture('tmp_data_search_roles_request_without_rule_type') + ); $service = app(SwaggerService::class); @@ -356,7 +385,9 @@ public function testAddDataRequestWithoutRuleType() public function testAddDataRequestWithAnnotations() { - $this->mockDriverGetEmptyAndSaveTpmData($this->getJsonFixture('tmp_data_search_roles_request_with_annotations')); + $this->mockDriverGetEmptyAndSaveTpmData( + $this->getJsonFixture('tmp_data_search_roles_request_with_annotations') + ); $service = app(SwaggerService::class); @@ -498,7 +529,9 @@ public function testAddDataPostRequestWithObjectParams() { config(['auto-doc.security' => 'jwt']); - $this->mockDriverGetEmptyAndSaveTpmData($this->getJsonFixture('tmp_data_post_user_request_with_object_params')); + $this->mockDriverGetEmptyAndSaveTpmData( + $this->getJsonFixture('tmp_data_post_user_request_with_object_params') + ); $service = app(SwaggerService::class);