Skip to content
This repository has been archived by the owner on Jan 15, 2024. It is now read-only.

Comprehensive validation errors #49

Closed
wants to merge 36 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
8f57970
Added maintenance status.
kleijnweb Jan 26, 2016
4e161bd
Linking to example repo
kleijnweb Jan 31, 2016
d8214d2
Trying out codeclimate
kleijnweb Feb 4, 2016
20dbab8
Merge branch 'master' of github.com:kleijnweb/swagger-bundle
kleijnweb Feb 4, 2016
d32d969
Build fixes
kleijnweb Feb 4, 2016
5ac5d7b
Codeclimate config fix
kleijnweb Feb 4, 2016
db87f0b
Attempt at busting GitHub image cache
kleijnweb Feb 4, 2016
65ca6fe
Update README.md
kleijnweb Feb 4, 2016
fd9f5e8
Removing code climate, adding coveralls
kleijnweb Feb 4, 2016
4a23e03
Replace code climate badge with coveralls badge
kleijnweb Feb 4, 2016
9c3c3d8
Fix path to coverall script
kleijnweb Feb 4, 2016
103b2c1
Trying downgrade of php-coveralls (and removed obsolete composer flag)
kleijnweb Feb 4, 2016
dd1ecf8
Disable xdebug in Travis when no needed
kleijnweb Feb 4, 2016
7de1a39
Coveralls not working, see https://github.com/satooshi/php-coveralls/…
kleijnweb Feb 4, 2016
78f39a3
Add scrutinizer badge
kleijnweb Feb 4, 2016
658d5b8
Re-added coveralls bade
kleijnweb Feb 5, 2016
549658b
Extracted dev-utils (bar testing), refs #12
kleijnweb Feb 6, 2016
66498f0
Updated documentation
kleijnweb Feb 6, 2016
f22fae8
Merge pull request #48 from kleijnweb/feature/issue-12
kleijnweb Feb 6, 2016
b52fddd
Merge branch 'feature/issue-12'
kleijnweb Feb 6, 2016
386146f
Master is 3.0-dev
kleijnweb Feb 6, 2016
74d3de0
Doc update
kleijnweb Feb 6, 2016
a2d3019
Normalized document array data to objects, fetching external referenc…
kleijnweb Feb 6, 2016
031f19e
Caching, refs #7
kleijnweb Feb 6, 2016
3fe6773
2xx code handling, fixes #45
kleijnweb Feb 6, 2016
b305b32
Parser fix
kleijnweb Feb 6, 2016
f4a7a9a
Test fixes
kleijnweb Feb 6, 2016
4e20666
Build fixes
kleijnweb Feb 6, 2016
fd592af
Refactored, refs #27
kleijnweb Feb 7, 2016
c0a782b
Starting to add JSON-Schema errors as HAL links (upped min version of…
kleijnweb Feb 7, 2016
b880442
Forgot to commit test
kleijnweb Feb 7, 2016
6ebd1c0
Doc update
kleijnweb Feb 7, 2016
dd69352
Merge branch 'master' into feature/issue-27
kleijnweb Feb 7, 2016
022fa09
Some cleanup
kleijnweb Feb 7, 2016
44f80d4
Updated docs
kleijnweb Feb 7, 2016
00113c8
String URI building for pointers in validation responses (WIP)
kleijnweb Feb 8, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/bin/
vendor
src/Tests/Functional/PetStore/app/cache
src/Tests/Functional/PetStore/app/logs
composer.lock
/vendor
/build
/src/Tests/Functional/PetStore/app/cache
/src/Tests/Functional/PetStore/app/logs
/composer.lock
phpunit.xml
30 changes: 16 additions & 14 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
language: php

sudo: false

before_install:
- composer self-update

- if [[ "$TRAVIS_PHP_VERSION" != "5.6" ]]; then phpenv config-rm xdebug.ini; fi
- composer self-update
install:
- composer update $COMPOSER_FLAGS --dev

- composer update $COMPOSER_FLAGS
matrix:
include:
- php: 5.4
env: COMPOSER_FLAGS="--prefer-lowest"
- php: 5.4
- php: 5.5
- php: 5.6
- php: 7.0
fast_finish: true
include:
- php: 5.4
env: COMPOSER_FLAGS="--prefer-lowest"
- php: 5.4
- php: 5.5
- php: 5.6
script: phpunit --coverage-clover build/logs/clover.xml
before_script:
- mkdir -p build/logs
after_script:
- travis_retry php bin/coveralls -v
- php: 7.0
fast_finish: true
176 changes: 80 additions & 96 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,45 @@
# KleijnWeb\SwaggerBundle [![Build Status](https://travis-ci.org/kleijnweb/swagger-bundle.svg?branch=master)](https://travis-ci.org/kleijnweb/swagger-bundle)
# KleijnWeb\SwaggerBundle
[![Build Status](https://travis-ci.org/kleijnweb/swagger-bundle.svg?branch=master)](https://travis-ci.org/kleijnweb/swagger-bundle)
[![Coverage Status](https://coveralls.io/repos/github/kleijnweb/swagger-bundle/badge.svg?branch=master)](https://coveralls.io/github/kleijnweb/swagger-bundle?branch=master)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/kleijnweb/swagger-bundle/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/kleijnweb/swagger-bundle/?branch=master)
[![Latest Stable Version](https://poser.pugx.org/kleijnweb/swagger-bundle/v/stable)](https://packagist.org/packages/kleijnweb/swagger-bundle)

Invert your workflow (contract first) using Swagger specs and set up a Symfony REST app with minimal config.
Invert your workflow (contract first) using Swagger ([Open API](https://openapis.org/)) specs and set up a Symfony REST app with minimal config.

Aimed to be lightweight, this bundle does not depend on FOSRestBundle or Twig (except for dev purposes).
Aimed to be lightweight, this bundle does not depend on FOSRestBundle or Twig.

*SwaggerBundle only supports json in- and output, and only YAML Swagger defintions.*
## Important Notes
* You are looking at the documentation for the upcoming 3.0 release.
* SwaggerBundle only supports json in- and output, and only YAML Swagger definitions
* This bundle is currently actively maintained.
* Go to the [release page](https://github.com/kleijnweb/swagger-bundle/releases) to find details about the latest release.

Go to the [release page](https://github.com/kleijnweb/swagger-bundle/releases) to find details about the latest release.
For a pretty complete example, see [swagger-bundle-example](https://github.com/kleijnweb/swagger-bundle-example).
A minimal example is also [available](https://github.com/kleijnweb/symfony-swagger-microservice-edition).

# This Bundle..
## This bundle will..

## Will:

* Coerce parameters to their defined types when possible.
* Validate content and parameters based on your Swagger spec.
* Configure routing based on your Swagger spec.
* Handle standard status codes such as 500, 400 and 404.
* Encode response data as JSON.
* Return `application/vnd.error+json` responses when errors occur.
* Configure routing based on your Swagger documents(s), accounting for things like type, enums and pattern matches.
* Validate body and parameters based on your Swagger documents(s).
* Coerce query and path parameters to their defined types when possible.
* Resolve [JSON Pointer](http://json-spec.readthedocs.org/en/latest/pointer.html)s anywhere in your Swagger documents and partials (not just in the [JSON Schema](http://json-schema.org/) bits).
* Return [vnd.error](https://github.com/blongden/vnd.error) (`application/vnd.error+json`) responses when errors occur.
* Utilize vnd.error's `logref` to make errors traceable.
* Resolve JSON-Schema `$ref`s in your Swagger spec to allow reusable partial specs.

## Can:

* Amend your Swagger spec to include the error responses added by SwaggerBundle.
* (De-) Serialize objects using either the Symfony Component Serializer or JMS\Serializer
* Generate DTO-like classes representing resources in your Swagger spec.
* Optionally (De-) Serialize objects using either the Symfony Component Serializer or JMS\Serializer

## Won't:
## It won't, and probably never will:

* Handle Form posts.
* Generate your API documentation. Use your Swagger document, plenty of options.
* Generate your API documentation. Use your Swagger documents, plenty of options.
* Mix well with GUI bundles. The bundle is biased towards lightweight API-only apps.
* Update the resource schemas in your Swagger spec when these classes change (not yet, but __soon__, see [#3](https://github.com/kleijnweb/swagger-bundle/issues/3)).
* Work with JSON Swagger documents (yet, see [#10](https://github.com/kleijnweb/swagger-bundle/issues/10)).
* Do content negotiation. May support XML in the future (low priority, see [#1](https://github.com/kleijnweb/swagger-bundle/issues/1)).

__TIP:__ Want to build an API-only app using this bundle? Try [kleijnweb/symfony-swagger-microservice-edition](https://github.com/kleijnweb/symfony-swagger-microservice-edition).
* Work with JSON Swagger documents.
* Do content negotiation or support XML.

# Usage

1. Create a Swagger file, for example using http://editor.swagger.io/.
2. Install and configure this bundle
3. Create one or more controllers (as services!), doing the actual work, whatever that may be.
4. You are DONE.

Pretty much. ;)
3. Create one or more controllers (as services), doing the actual work, whatever that may be.

## Install And Configure

Expand All @@ -55,16 +49,16 @@ Add Swagger-based routing to your app, for example:

```yml
test:
resource: "config/yourapp.yml"
type: swagger
resource: "config/yourapp.yml"
type: swagger
```

The path here is relative to the `swagger.document.base_path` configuration option. The above example would require something like this in your config:

```yml
swagger:
document:
base_path: "%kernel.root_dir%"
document:
base_path: "%kernel.root_dir%"
```

# Functional Details / Features
Expand Down Expand Up @@ -103,7 +97,7 @@ public function placeOrder(array $body)
}
```

__NOTE:__ SwaggerBundle applies some type conversion to input and adds the converted types to the Request `attributes`.
__NOTE:__ SwaggerBundle applies some type conversion to query and path parameters and adds the converted values to the Request `attributes`.
Using `Request::get()` will give precedence to parameters in `query`. These values will be 'raw', using `attributes` is preferred.

Your controllers do not need to implement any interfaces or extend any classes. A controller might look like this (using object deserialization, see section below):
Expand All @@ -127,7 +121,17 @@ class StoreController

It would make more sense to name the parameter `order` instead of `body`, but this is how it is in the pet store example provided by Swagger.

Other parameters can be added to the signature as well, this is standard Symfony behaviour.
All (type-casted) parameters can be added to the signature, since they are attributes when the controller is invoked. This is standard Symfony behaviour.

## Caching

Parsing YAML and resolving JSON Pointers can be slow, especially with larger specs with external references. SwaggerBundle can use a Doctrine cache to mitigate this. Use a DI key to reference the service you want to use:

```yml
swagger:
document:
cache: "some.doctrine.cache.service"
```

## Route Matching

Expand All @@ -137,24 +141,48 @@ and `enum` when dealing with string path parameters.

## Exception Handling

Any exceptions are caught, logged by the `@logger` service, and result in `application/vnd.error+json`. Routing failure results in a 404 response without `logref`.
Any exceptions are caught, logged by the `@logger` service, and result in `application/vnd.error+json`. The log-level/severity depends on the exception type and/or code. "Not Found" errors are logged as 'INFO'.

## Input Validation

### Parameter Validation

SwaggerBundle will attempt to convert string values to any scalar value specified in your swagger file, within reason.
SwaggerBundle will attempt to convert path and query string values to the scalar value specified in your swagger file, within reason.
For example, it will accept all of "0", "1", "TRUE", "FALSE", "true", and "false" as boolean values, but wont blindly
evaluate any string value as `TRUE`.

Parameter validation errors will result in a `vnd.error` response with a status code of 400.

__NOTE__: SwaggerBundle currently does not support `multi` for `collectionFormat` when handling array parameters.
__NOTE__: SwaggerBundle currently does not support `multi` for `collectionFormat` when handling array parameters (see [#50](https://github.com/kleijnweb/swagger-bundle/issues/50)).

### Content Validation

If the content cannot be decoded using the format specified by the request's Content-Type header, or if validation
of the content using the resource schema failed, SwaggerBundle will return a `vnd.error` response with a 400 status code.
If the content cannot be decoded as JSON, or if validation of the content using the resource schema failed,
SwaggerBundle will return a `vnd.error` response with a 400 status code.

### Validation Feedback

Parameter validation errors will result in a `vnd.error` response with a status code of 400.

The validation errors (produced by [justinrainbow/json-schema](https://github.com/justinrainbow/json-schema)), are included in the response, with [HAL](http://stateless.co/hal_specification.html) links that are essentially JSON Pointers
to the parameter definition in the relevant Swagger Document.

In order for this to work properly, you may need some additional config.

When SwaggerBundle generates the JSON Pointer URI, it uses the following conventions:

1. For the protocol, it uses to the protocol used to make the request, unless globally configured otherwise, *or* if not in the specs `schemes` (in which case it will use, in order of preference: https, wss, http, ws).
2. For the host name, it will prefer the global config. If not defined it will use the value of `host` in the spec, ultimately falling back to the host name used to make the request.
3. For the relative path, it will use the path relative to `swagger.document.base_path`. If configured, it will prefix the `swagger.document.public.base_url`

Example:

```yaml
swagger:
document:
public:
proto: 'http' # Even if the spec claims it support https, this will cause the links to use http, unless the request was made using https (likewise you can use this to force https even if the request was made using http)
base_url: specs # This will prefix '/specs' to all paths
host: some.host.tld # Fetch specs from said host, instead of what's defined in the spec or the current one
```

### Object (De-) Serialization

Expand Down Expand Up @@ -210,58 +238,15 @@ public function placeOrder(Request $request)
//...
}
```
When a controller action returns `NULL`, SwaggerBundle will return an empty `204` response.

#### Using Annotations

In order to use annotations, you should make sure you use an autoload bootstrap
that will initialize doctrine/annotations:

```php
use Doctrine\Common\Annotations\AnnotationRegistry;
use Composer\Autoload\ClassLoader;

/**
* @var ClassLoader $loader
*/
$loader = require __DIR__.'/../vendor/autoload.php';

AnnotationRegistry::registerLoader(array($loader, 'loadClass'));

return $loader;
```

Good chance you are already using a bootstrap file like this, but if the annotations won't load, this is where to look.

## Authentication

SwaggerBundle 2.0+ does not include authentication functionality. The JWT support from 1.0 was moved into [kleijnweb/jwt-bundle](https://github.com/kleijnweb/jwt-bundle)).

When using `SecurityDefinition` type `oauth2`, it would be possible to translate *scopes* to Symfony roles,
add them to the user, and automatically configure `access_control`.
This is not currently implemented (yet, see [#15](https://github.com/kleijnweb/swagger-bundle/issues/15)).

When a controller action returns `NULL`, SwaggerBundle will return an empty `204` response, provided that one is defined in the specification.

Otherwise, it will default to the first 2xx type response defined in your spec, or if all else fails, simply 200.

# Developing

__NOTE:__ In order to use development tools, the `require-dev` dependencies are needed, as well as setting the `dev` configuration option:

```yml
swagger:
dev: true # Or perhaps "%kernel.debug%"
```

## Amending Your Swagger Document

SwaggerBundle adds some standardized behavior, this should be reflected in your Swagger document. Instead of doing this manually, you can use the `swagger:document:amend` command.

## Generating Resource Classes

SwaggerBundle can generate classes for you based on your Swagger resource definitions.
You can use the resulting classes as DTO-like objects for your services, or create Doctrine mapping config for them. Obviously this requires you to enable object serialization.
The resulting classes will have JMS\Serializer annotations by default, the use of which is optional, remove them if you're using the standard Symfony serializer.
# Utilities

See `app/console swagger:generate:resources --help` for more details.
See [swagger-bundle-tools](https://github.com/kleijnweb/swagger-bundle-tools).

## Functional Testing Your API

Expand Down Expand Up @@ -312,10 +297,9 @@ class PetStoreApiTest extends WebTestCase
}
}
```

When using ApiTestCase, initSchemaManager() will also validate your Swagger spec against the official schema to ensure it is valid.


When using ApiTestCase, initSchemaManager() will also validate your Swagger spec against the official schema to ensure it is valid.

## License

KleijnWeb\SwaggerBundle is made available under the terms of the [LGPL, version 3.0](https://spdx.org/licenses/LGPL-3.0.html#licenseText).
17 changes: 13 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,16 @@
"symfony/finder": ">=2.6.0",
"symfony/property-access": ">=2.6.0",
"doctrine/collections": "^1.3",
"justinrainbow/json-schema": ">=1.4.2 <1.5"
"justinrainbow/json-schema": ">=1.4.2 <1.5",
"ramsey/vnderror": "^3.0.0",
"nocarrier/hal": "^0.9.12"
},
"suggest": {
"symfony/serializer": "Object de- serialization using Symfony Serializer Component",
"jms/serializer": "Object de- serialization using JMS\\Serializer",
"doctrine/annotations": "Object de- serialization annotation support",
"kleijnweb/jwt-bundle": "JWT authentication support"
"kleijnweb/jwt-bundle": "JWT authentication support",
"doctrine/cache": "Caching parsed and resolved Swagger documents"
},
"require-dev": {
"phpunit/phpunit": ">=4.1.0",
Expand All @@ -51,10 +54,16 @@
"twig/twig": "^1.21",
"jms/serializer": "1.0",
"phpoption/phpoption": ">=1.1.0",
"fr3d/swagger-assertions": "^0.2.0"

"fr3d/swagger-assertions": "^0.2.0",
"satooshi/php-coveralls": "<1.0",
"doctrine/cache": "^1.5.0"
},
"config": {
"bin-dir": "bin"
},
"extra": {
"branch-alias": {
"dev-master": "3.0.x-dev"
}
}
}
5 changes: 1 addition & 4 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ public function getConfigTreeBuilder()

$rootNode
->children()
->scalarNode('dev')
->defaultFalse()
->end()

->arrayNode('serializer')
->addDefaultsIfNotSet()
->children()
Expand All @@ -45,6 +41,7 @@ public function getConfigTreeBuilder()
->arrayNode('document')
->addDefaultsIfNotSet()
->children()
->scalarNode('cache')->isRequired()->defaultFalse()->end()
->scalarNode('base_path')->defaultValue('')->end()
->end()
->end()
Expand Down
9 changes: 5 additions & 4 deletions src/DependencyInjection/KleijnWebSwaggerExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ public function load(array $configs, ContainerBuilder $container)
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.yml');

if ($config['dev']) {
$loader->load('services_dev.yml');
}

$container->setParameter('swagger.document.base_path', $config['document']['base_path']);
$container->setParameter('swagger.serializer.namespace', $config['serializer']['namespace']);

Expand All @@ -44,6 +40,11 @@ public function load(array $configs, ContainerBuilder $container)
$resolverDefinition = $container->getDefinition('swagger.request.processor.content_decoder');
$resolverDefinition->addArgument(new Reference('swagger.serializer.type_resolver'));
}

if (isset($config['document']['cache'])) {
$resolverDefinition = $container->getDefinition('swagger.document.repository');
$resolverDefinition->addArgument(new Reference($config['document']['cache']));
}
}

/**
Expand Down
Loading