diff --git a/.gitignore b/.gitignore
index d938b52..d072ff3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
/vendor/
composer.lock
/nbproject/private/
-nbproject/
\ No newline at end of file
+nbproject/
+
+.idea/
\ No newline at end of file
diff --git a/README.Symfony3.md b/README.Symfony3.md
new file mode 100644
index 0000000..717d1a8
--- /dev/null
+++ b/README.Symfony3.md
@@ -0,0 +1,182 @@
+# Voryx REST Generator Bundle
+[](https://insight.sensiolabs.com/projects/ac1842d9-4e36-45cc-8db1-b97e2e62540e)
+
+## About
+
+A CRUD like REST Generator
+
+## Features
+
+* Generators RESTful action from entity
+* Simplifies setting up a RESTful Controller
+
+
+## Installation
+Require the "voryx/restgeneratorbundle" package in your composer.json and update your dependencies.
+
+```bash
+$ php composer.phar require voryx/restgeneratorbundle dev-master
+```
+
+Add the VoryxRestGeneratorBundle to your application's kernel along with other dependencies:
+
+```php
+public function registerBundles()
+{
+ $bundles = array(
+ //...
+ new Voryx\RESTGeneratorBundle\VoryxRESTGeneratorBundle(),
+ new FOS\RestBundle\FOSRestBundle(),
+ new JMS\SerializerBundle\JMSSerializerBundle($this),
+ new Nelmio\CorsBundle\NelmioCorsBundle(),
+ new Nelmio\ApiDocBundle\NelmioApiDocBundle(),
+ //...
+ );
+ //...
+}
+```
+
+## Configuration
+
+This bundle depends on a number of other Symfony bundles, so they need to be configured in order for the generator to work properly
+
+```yaml
+framework:
+ csrf_protection: false #only use for public API
+
+fos_rest:
+ routing_loader:
+ default_format: json
+ param_fetcher_listener: true
+ body_listener: true
+ #disable_csrf_role: ROLE_USER
+ body_converter:
+ enabled: true
+ view:
+ view_response_listener: force
+
+nelmio_cors:
+ defaults:
+ allow_credentials: false
+ allow_origin: []
+ allow_headers: []
+ allow_methods: []
+ expose_headers: []
+ max_age: 0
+ paths:
+ '^/api/':
+ allow_origin: ['*']
+ allow_headers: ['*']
+ allow_methods: ['POST', 'PUT', 'GET', 'DELETE']
+ max_age: 3600
+
+sensio_framework_extra:
+ request: { converters: true }
+ view: { annotations: false }
+ router: { annotations: true }
+
+nelmio_api_doc: ~
+```
+
+## Generating the Controller
+
+
+Generate the REST controller
+
+```bash
+$ php bin/console voryx:generate:rest
+```
+
+This will guide you through the generator which will generate a RESTful controller for an entity.
+
+
+## Example
+
+Create a new entity called 'Post':
+
+```bash
+$ php bin/console doctrine:generate:entity --entity=AppBundle:Post --format=annotation --fields="name:string(255) description:string(255)" --no-interaction
+```
+
+Update the database schema:
+
+```bash
+$ php bin/console doctrine:schema:update --force
+```
+
+Generate the API controller:
+
+```bash
+$ php bin/console voryx:generate:rest --entity="AppBundle:Post"
+```
+
+Full example with all parameters
+
+```bash
+$ php app/console voryx:generate:rest --entity="AppBundle:Post" --document --overwrite --route-prefix="api" --route-format="yml" --service-format="yml" --test="none"
+```
+
+possible values for all parameters
+
+| Parameter | Explanation | Values |
+| --------- | ----------- | ------- |
+| entity | The entity for which the REST api should be generated | **AppBundle:Post**, **AppBundle:Blog\Post**
+| document | Whether or not you want API's documented by Nelmio | Yes if present, else No
+| overwrite | Whether or not you want to overwrite existing generated files | Yes if present, else No
+| resource | - | Yes if present, else No
+| route-prefix | The route to prefix the generated Controller with (default api) | Any string
+| route-format | The format that routing is generated in (default yml) | **yml** or **annotation**
+| service-format | The format that the service is generated in (default yml) | **yml** or **xml**
+| test | The type of test that should be generated | **none**, **oauth** or **no-authentication**
+
+
+### Using the API
+If you selected the default options you'll be able to start using the API like this:
+
+Creating a new post (`POST`)
+
+```bash
+$ curl -i -H "Content-Type: application/json" -X POST -d '{"name" : "Test Post", "description" : "This is a test post"}' http://localhost/app_dev.php/api/posts
+```
+
+Updating (`PUT`)
+
+```bash
+$ curl -i -H "Content-Type: application/json" -X PUT -d '{"name" : "Test Post 1", "description" : "This is an updated test post"}' http://localhost/app_dev.php/api/posts/1
+```
+
+Get all posts (`GET`)
+
+```bash
+$ curl http://localhost/app_dev.php/api/posts
+```
+
+Get one post (`GET`)
+
+```bash
+$ curl http://localhost/app_dev.php/api/posts/1
+```
+
+
+Delete (`DELETE`)
+
+```bash
+$ curl -X DELETE http://localhost/app_dev.php/api/posts/1
+```
+
+
+## Related Entities
+
+If you want the form to be able to convert related entities into the correct entity id on POST, PUT or PATCH, use the voryx_entity form type
+
+```php
+use Voryx\RESTGeneratorBundle\Form\Type\VoryxEntityType;
+
+#Form/PostType()
+
+ ->add(
+ 'user', VoryxEntityType:class, array(
+ 'class' => 'Acme\Bundle\Entity\User'
+ )
+ )
+```
diff --git a/README.md b/README.md
index a0f1227..d467b15 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,7 @@ public function registerBundles()
new FOS\RestBundle\FOSRestBundle(),
new JMS\SerializerBundle\JMSSerializerBundle($this),
new Nelmio\CorsBundle\NelmioCorsBundle(),
+ new Nelmio\ApiDocBundle\NelmioApiDocBundle(),
//...
);
//...
@@ -73,6 +74,8 @@ sensio_framework_extra:
request: { converters: true }
view: { annotations: false }
router: { annotations: true }
+
+nelmio_api_doc: ~
```
## Generating the Controller
@@ -107,6 +110,27 @@ Generate the API controller:
$ php app/console voryx:generate:rest --entity="AppBundle:Post"
```
+Full example with all parameters
+
+```bash
+$ php app/console voryx:generate:rest --entity="AppBundle:Post" --document --resource --overwrite --route-prefix="api" --route-format="yml" --service-format="yml" --test="none"
+$ php app/console voryx:generate:rest --entity="AppBundle:User/Domain" --document --overwrite --route-prefix="api" --route-format="annotation" --service-format="yml" --test="oauth2"
+```
+
+possible values for all parameters
+
+| Parameter | Explanation | Values |
+| --------- | ----------- | ------- |
+| entity | The entity for which the REST api should be generated | **AppBundle:Post**, **AppBundle:Blog\Post**
+| document | Whether or not you want API's documented by Nelmio | Yes if present, else No
+| overwrite | Whether or not you want to overwrite existing generated files | Yes if present, else No
+| resource | Whether or not you want the resource name encapsulating the response | Yes if present, else No
+| route-prefix | The route to prefix the generated Controller with (default api) | Any string
+| route-format | The format that routing is generated in (default yml) | **yml** or **annotation**
+| service-format | The format that the service is generated in (default yml) | **yml** or **xml**
+| test | The type of test that should be generated | **none**, **oauth** or **no-authentication**
+
+
### Using the API
If you selected the default options you'll be able to start using the API like this:
@@ -147,10 +171,12 @@ $ curl -X DELETE http://localhost/app_dev.php/api/posts/1
If you want the form to be able to convert related entities into the correct entity id on POST, PUT or PATCH, use the voryx_entity form type
```php
-#Form/PostType()
+use Voryx\RESTGeneratorBundle\Form\Type\VoryxEntityType;
+
+// ...
->add(
- 'user', 'voryx_entity', array(
+ 'user', VoryxEntityType:class, array(
'class' => 'Acme\Bundle\Entity\User'
)
)
diff --git a/composer.json b/composer.json
index 347beb1..3d6c22e 100644
--- a/composer.json
+++ b/composer.json
@@ -1,6 +1,6 @@
{
"name": "voryx/restgeneratorbundle",
- "description": "REST API Generator for Symfony 2",
+ "description": "REST API Generator for Symfony",
"type": "symfony-bundle",
"license": "MIT",
"authors": [
@@ -9,14 +9,20 @@
},
{
"name": "David Dan"
+ },
+ {
+ "name": "Maarten Sprakel"
}
],
"require": {
- "php": ">=5.3.0",
+ "php": ">=5.5.9",
+ "symfony/symfony": ">=2.8",
"sensio/generator-bundle": "~3.0",
- "friendsofsymfony/rest-bundle": "~1.7",
- "jms/serializer-bundle": "~1.1",
- "nelmio/cors-bundle": "~1.4 "
+ "friendsofsymfony/rest-bundle": "^2.2",
+ "jms/serializer-bundle": "^2.0",
+ "nelmio/cors-bundle": "~1.5",
+ "nelmio/api-doc-bundle": "~2.7",
+ "doctrine/orm": ">=2.0"
},
"autoload": {
"psr-0": {
diff --git a/src/Voryx/RESTGeneratorBundle/Command/GenerateDoctrineRESTCommand.php b/src/Voryx/RESTGeneratorBundle/Command/GenerateDoctrineRESTCommand.php
index d90f461..6517eac 100644
--- a/src/Voryx/RESTGeneratorBundle/Command/GenerateDoctrineRESTCommand.php
+++ b/src/Voryx/RESTGeneratorBundle/Command/GenerateDoctrineRESTCommand.php
@@ -13,8 +13,13 @@
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\DependencyInjection\Container;
+use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
+use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
use Sensio\Bundle\GeneratorBundle\Command\Helper\QuestionHelper;
+use Symfony\Component\Validator\Mapping\ClassMetadata;
+use Symfony\Component\Yaml\Exception\ParseException;
+use Symfony\Component\Yaml\Yaml;
use Voryx\RESTGeneratorBundle\Generator\DoctrineRESTGenerator;
use Sensio\Bundle\GeneratorBundle\Command\Validators;
use Voryx\RESTGeneratorBundle\Manipulator\RoutingManipulator;
@@ -39,9 +44,12 @@ protected function configure()
array(
new InputOption('entity', '', InputOption::VALUE_REQUIRED, 'The entity class name to initialize (shortcut notation)'),
new InputOption('route-prefix', '', InputOption::VALUE_REQUIRED, 'The route prefix'),
+ new InputOption('route-format', '', InputOption::VALUE_REQUIRED, 'The format used for generation of routing (yml or annotation)', 'yml'),
+ new InputOption('service-format', '', InputOption::VALUE_REQUIRED, 'The format used for generation of services (yml or xml)', 'yml'),
+ new InputOption('test', '', InputOption::VALUE_REQUIRED, 'Generate a test for the given authentication mode (oauth2, no-authentication, none)', 'none'),
new InputOption('overwrite', '', InputOption::VALUE_NONE, 'Do not stop the generation if rest api controller already exist, thus overwriting all generated files'),
new InputOption('resource', '', InputOption::VALUE_NONE, 'The object will return with the resource name'),
- new InputOption('document', '', InputOption::VALUE_NONE, 'Use NelmioApiDocBundle to document the controller'),
+ new InputOption('document', '', InputOption::VALUE_NONE, 'Use NelmioApiDocBundle to document the controller')
)
)
->setDescription('Generates a REST api based on a Doctrine entity')
@@ -71,6 +79,9 @@ protected function configure()
/**
* @see Command
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ * @return int|null
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
@@ -88,9 +99,12 @@ protected function execute(InputInterface $input, OutputInterface $output)
$entity = Validators::validateEntityName($input->getOption('entity'));
list($bundle, $entity) = $this->parseShortcutNotation($entity);
- $format = "rest";
+ $format = $input->getOption('route-format');
+ $service_format = $input->getOption('service-format');
$prefix = $this->getRoutePrefix($input, $entity);
+ /** @var bool $forceOverwrite */
$forceOverwrite = $input->getOption('overwrite');
+ $test = $input->getOption('test');
$questionHelper->writeSection($output, 'REST api generation');
@@ -99,22 +113,61 @@ protected function execute(InputInterface $input, OutputInterface $output)
$bundle = $this->getContainer()->get('kernel')->getBundle($bundle);
$resource = $input->getOption('resource');
$document = $input->getOption('document');
+ $constraints = array();
+
+ $constraintMetadata = null;
+ try
+ {
+ /** @var \Symfony\Component\Validator\Validator\RecursiveValidator $validator */
+ $validator = $this->getContainer()->get('validator');
+
+ /** @var ClassMetadata $constraintMetadata */
+ $constraintMetadata = $validator->getMetadataFor(new $entityClass);
+ foreach($constraintMetadata->getConstrainedProperties() as $property)
+ {
+ //var_dump($constraint_metadata->getPropertyMetadata($property));
+ $constraints[$property] = $constraintMetadata->getPropertyMetadata($property)[0]->constraints;
+ }
+ }
+ catch(ServiceNotFoundException $snfex)
+ {
+ //no constraints are checked
+ $output->writeln($snfex->getMessage());
+ }
+ catch(\Exception $ex)
+ {
+ $output->writeln($ex->getMessage());
+ }
+ if ($constraintMetadata === null)
+ {
+ if ($test !== 'none')
+ {
+ $output->writeln('No class constraint metadata found for entity ' . $entityClass . '');
+ }
+ }
+
+ /** @var DoctrineRESTGenerator $generator */
$generator = $this->getGenerator($bundle);
- $generator->generate($bundle, $entity, $metadata[0], $prefix, $forceOverwrite, $resource, $document);
+ $generator->generate($bundle, $entity, $metadata[0], $constraints, $prefix, $forceOverwrite, $resource, $document, $format, $service_format, $test);
$output->writeln('Generating the REST api code: OK');
+ if ($test === 'oauth2')
+ {
+ $output->writeln('Please make sure you check the Tests/oauthBase.php and fill in a correct username/password on line 17/18 before running the test.');
+ }
$errors = array();
$runner = $questionHelper->getRunner($output, $errors);
// form
- $this->generateForm($bundle, $entity, $metadata);
+ $this->generateForm($bundle, $entity, $metadata, $forceOverwrite);
$output->writeln('Generating the Form code: OK');
// create route
$runner($this->updateRouting($questionHelper, $input, $output, $bundle, $format, $entity, $prefix));
+
$questionHelper->writeGeneratorSummary($output, $errors);
}
@@ -149,6 +202,21 @@ protected function interact(InputInterface $input, OutputInterface $output)
$input->setOption('entity', $entity);
list($bundle, $entity) = $this->parseShortcutNotation($entity);
+ //routing format
+ $format = $input->getOption('route-format');
+ $output->writeln(
+ array(
+ '',
+ 'Determine the routing format (yml or annotation).',
+ ''
+ )
+ );
+ $question = new Question($questionHelper->getQuestion('Routing format', $format), $format);
+ $question->setValidator(array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateFormat'));
+ $format = $questionHelper->ask($input, $output, $question);
+
+ $input->setOption('route-format',$format);
+
// route prefix
$prefix = 'api';
$output->writeln(
@@ -163,6 +231,36 @@ protected function interact(InputInterface $input, OutputInterface $output)
$prefix = $questionHelper->ask($input, $output, new Question($questionHelper->getQuestion('Routes prefix', '/' . $prefix), '/' . $prefix));
$input->setOption('route-prefix', $prefix);
+ //service format
+ $serviceFormat = $input->getOption('service-format');
+ $output->writeln(
+ array(
+ '',
+ 'Determine the service format (yml or xml).',
+ ''
+ )
+ );
+ $question = new Question($questionHelper->getQuestion('Service format', $serviceFormat), $serviceFormat);
+ $question->setValidator(array('Voryx\RESTGeneratorBundle\Command\Validators', 'validateServiceFormat'));
+ $serviceFormat = $questionHelper->ask($input, $output, $question);
+
+ $input->setOption('service-format',$serviceFormat);
+
+ //testing mode
+ $output->writeln(
+ array(
+ '',
+ 'Determine what kind of test you want to have generated (if any)',
+ 'Possible values are none (no tests), no-authentication and oauth2',
+ ''
+ )
+ );
+ $question = new Question($questionHelper->getQuestion('What type of tests do you want to generate?', $input->getOption('test')),$input->getOption('test'));
+ $question->setValidator(array('Voryx\RESTGeneratorBundle\Command\Validators', 'validateTestFormat'));
+ $test = $questionHelper->ask($input, $output, $question);
+
+ $input->setOption('test',$test);
+
// summary
$output->writeln(
array(
@@ -175,12 +273,12 @@ protected function interact(InputInterface $input, OutputInterface $output)
);
}
-
/**
* @param QuestionHelper $questionHelper
* @param InputInterface $input
* @param OutputInterface $output
* @param BundleInterface $bundle
+ * @param $format
* @param $entity
* @param $prefix
* @return array
@@ -195,7 +293,59 @@ protected function updateRouting(QuestionHelper $questionHelper, InputInterface
$output->write('Importing the REST api routes: ');
$this->getContainer()->get('filesystem')->mkdir($bundle->getPath() . '/Resources/config/');
+
+ if ($format === 'annotation')
+ {
+ $bundle_name = str_replace("Bundle", "", $bundle->getName());
+ $route_name = strtolower($bundle_name);
+ $yml_file_location = $this->getContainer()->getParameter('kernel.root_dir') . '/config/routing.yml';
+ try
+ {
+ $yml_file = Yaml::parse(file_get_contents($yml_file_location));
+ }
+ catch(ParseException $pex)
+ {
+ return array(
+ 'Could not read yaml file '.$yml_file_location.'',
+ 'On line',
+ 'parsed line: '.$pex->getParsedLine() . ' and current line '. $pex->getLine(),
+ 'With snippet '.$pex->getSnippet(),
+ 'Exception message:',
+ ''.$pex->getMessage().''
+ );
+ }
+ $resource_location = sprintf('@%s/Controller/', $bundle->getName());
+
+ $bundle_routing = null;
+ if (array_key_exists($route_name, $yml_file))
+ {
+ $bundle_routing = $yml_file[$route_name];
+ }
+
+ if (is_array($bundle_routing))
+ {
+ if (array_key_exists('type',$bundle_routing) && array_key_exists('resource',$bundle_routing) && $bundle_routing['type'] === $format && $bundle_routing['resource'] === $resource_location)
+ {
+ //all is good
+ return array();
+ }
+ }
+
+ $bundle_routing = array(
+ 'resource' => $resource_location,
+ 'type' => $format
+ );
+
+ $yml_file[$route_name] = $bundle_routing;
+
+ $yml_content = Yaml::dump($yml_file, 2);
+ file_put_contents($yml_file_location, $yml_content);
+
+ return array();
+ }
+
$routing = new RoutingManipulator($this->getContainer()->getParameter('kernel.root_dir') . '/config/routing.yml');
+
try {
$ret = $auto ? $routing->addResource($bundle->getName(), '/' . $prefix, $entity) : false;
} catch (\RuntimeException $exc) {
@@ -210,7 +360,6 @@ protected function updateRouting(QuestionHelper $questionHelper, InputInterface
);
$help .= sprintf(" type: %s\n", 'rest');
$help .= sprintf(" prefix: /%s\n", $prefix);
-
return array(
'- Import this resource into the Apps routing file',
sprintf(' (%s).', $this->getContainer()->getParameter('kernel.root_dir') . '/config/routing.yml'),
@@ -232,7 +381,10 @@ protected function updateRouting(QuestionHelper $questionHelper, InputInterface
*/
protected function createGenerator($bundle = null)
{
- return new DoctrineRESTGenerator($this->getContainer()->get('filesystem'));
+ /** @var Filesystem $fileSystem */
+ $fileSystem = $this->getContainer()->get('filesystem');
+
+ return new DoctrineRESTGenerator($fileSystem);
}
/**
diff --git a/src/Voryx/RESTGeneratorBundle/Command/Validators.php b/src/Voryx/RESTGeneratorBundle/Command/Validators.php
new file mode 100644
index 0000000..9451005
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Command/Validators.php
@@ -0,0 +1,62 @@
+
+ */
+class Validators
+{
+ /**
+ * @param string $format
+ * @return string
+ * @throws \RuntimeException
+ */
+ public static function validateTestFormat($format)
+ {
+ if (!$format) {
+ return 'none';
+ }
+
+ if ($format === 'oauth') {
+ $format = 'oauth2';
+ }
+
+ $format = strtolower($format);
+
+ $supported = array('none', 'oauth2', 'no-authentication', 'csrf');
+
+ if (!in_array($format, $supported)) {
+ throw new \RuntimeException(sprintf('Test format "%s" is not supported, only '.implode(',',$supported).' are supported.', $format));
+ }
+
+ return $format;
+ }
+
+ /**
+ * @param string $service_format
+ * @return string
+ * @throws \RuntimeException
+ */
+ public static function validateServiceFormat($service_format)
+ {
+ if (!$service_format)
+ {
+ return 'xml';
+ }
+
+ $service_format = strtolower($service_format);
+
+ $supported_service_formats = array('xml', 'yml');
+
+ if (!in_array($service_format, $supported_service_formats))
+ {
+ throw new \RuntimeException(sprintf('Service format "%s" is not supported, only '.implode(',',$supported_service_formats).' are supported.', $service_format));
+ }
+
+ return $service_format;
+ }
+}
diff --git a/src/Voryx/RESTGeneratorBundle/Controller/VoryxController.php b/src/Voryx/RESTGeneratorBundle/Controller/VoryxController.php
index ce6587d..cf53e47 100644
--- a/src/Voryx/RESTGeneratorBundle/Controller/VoryxController.php
+++ b/src/Voryx/RESTGeneratorBundle/Controller/VoryxController.php
@@ -1,15 +1,11 @@
setDefaults(
array(
@@ -58,14 +58,6 @@ public function setDefaultOptions(OptionsResolverInterface $resolver)
*/
public function getParent()
{
- return 'entity';
- }
-
- /**
- * @return string
- */
- public function getName()
- {
- return 'voryx_entity';
+ return EntityType::class;
}
}
diff --git a/src/Voryx/RESTGeneratorBundle/Generator/DoctrineRESTGenerator.php b/src/Voryx/RESTGeneratorBundle/Generator/DoctrineRESTGenerator.php
index f90caaf..f7f1a39 100644
--- a/src/Voryx/RESTGeneratorBundle/Generator/DoctrineRESTGenerator.php
+++ b/src/Voryx/RESTGeneratorBundle/Generator/DoctrineRESTGenerator.php
@@ -11,10 +11,15 @@
namespace Voryx\RESTGeneratorBundle\Generator;
+use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Sensio\Bundle\GeneratorBundle\Generator\Generator;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
-use Doctrine\ORM\Mapping\ClassMetadataInfo;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\Constraints\NotBlank;
+use Symfony\Component\Yaml\Parser;
+use Symfony\Component\Yaml\Yaml;
/**
* Generates a REST controller.
@@ -23,12 +28,18 @@
*/
class DoctrineRESTGenerator extends Generator
{
+ /** @var Filesystem */
protected $filesystem;
protected $routePrefix;
protected $routeNamePrefix;
+
+ /** @var BundleInterface */
protected $bundle;
protected $entity;
+
+ /** @var ClassMetadataInfo */
protected $metadata;
+ protected $entityConstraints;
protected $format;
protected $actions;
@@ -48,32 +59,44 @@ public function __construct(Filesystem $filesystem)
* @param BundleInterface $bundle A bundle object
* @param string $entity The entity relative class name
* @param ClassMetadataInfo $metadata The entity class metadata
+ * @param array $entityConstraints array of fields with constraints array('field' => array(Constraint, Constraint2),'field2' => array(Constraint, Constraint2))
* @param string $routePrefix The route name prefix
- * @param array $forceOverwrite Whether or not to overwrite an existing controller
- *
- * @throws \RuntimeException
+ * @param bool $forceOverwrite Whether or not to overwrite an existing controller
+ * @param bool $resource
+ * @param bool $document Whether or not to use Nelmio api documentation
+ * @param string $format Format of routing
+ * @param string $service_format Format of service generation
+ * @param string $test Test-mode (none, oauth or no-authentication)
*/
- public function generate(BundleInterface $bundle, $entity, ClassMetadataInfo $metadata, $routePrefix, $forceOverwrite)
+ public function generate(BundleInterface $bundle,$entity,ClassMetadataInfo $metadata, $entityConstraints,$routePrefix,$forceOverwrite,$resource,$document,$format, $service_format, $test)
{
- $this->routePrefix = $routePrefix;
+ $this->routePrefix = $routePrefix;
$this->routeNamePrefix = str_replace('/', '_', $routePrefix);
$this->actions = array('getById', 'getAll', 'post', 'put', 'delete');
if (count($metadata->identifier) > 1) {
- throw new \RuntimeException('The REST api generator does not support entity classes with multiple primary keys.');
+ throw new \RuntimeException(
+ 'The REST api generator does not support entity classes with multiple primary keys.'
+ );
}
if (!in_array('id', $metadata->identifier)) {
- throw new \RuntimeException('The REST api generator expects the entity object has a primary key field named "id" with a getId() method.');
+ throw new \RuntimeException(
+ 'The REST api generator expects the entity object has a primary key field named "id" with a getId() method.'
+ );
}
- $this->entity = $entity;
- $this->bundle = $bundle;
+ $this->entity = $entity;
+ $this->bundle = $bundle;
$this->metadata = $metadata;
- $this->setFormat('yml');
-
- $this->generateControllerClass($forceOverwrite);
+ $this->entityConstraints = $entityConstraints;
+ $this->setFormat($format);
+ $this->generateControllerClass($forceOverwrite, $document, $resource);
+ $this->generateHandler($forceOverwrite, $document);
+ $this->generateExceptionClass();
+ $this->declareService($service_format);
+ $this->generateTestClass($forceOverwrite, $test);
}
/**
@@ -128,9 +151,11 @@ protected function generateConfiguration()
/**
* Generates the controller class only.
- *
+ * @param bool $forceOverwrite
+ * @param bool $document
+ * @param bool $resource
*/
- protected function generateControllerClass($forceOverwrite)
+ protected function generateControllerClass($forceOverwrite, $document, $resource)
{
$dir = $this->bundle->getPath();
@@ -153,36 +178,377 @@ protected function generateControllerClass($forceOverwrite)
'rest/controller.php.twig',
$target,
array(
- 'actions' => $this->actions,
- 'route_prefix' => $this->routePrefix,
+ 'actions' => $this->actions,
+ 'route_prefix' => $this->routePrefix,
'route_name_prefix' => $this->routeNamePrefix,
- 'bundle' => $this->bundle->getName(),
- 'entity' => $this->entity,
- 'entity_class' => $entityClass,
+ 'bundle' => $this->bundle->getName(),
+ 'entity' => $this->entity,
+ 'entity_class' => $entityClass,
+ 'namespace' => $this->bundle->getNamespace(),
+ 'entity_namespace' => $entityNamespace,
+ 'format' => $this->format,
+ 'resource' => $resource,
+ 'document' => $document,
+ )
+ );
+ }
+
+ /**
+ * Generates the Handle only.
+ * @param bool $forceOverwrite
+ * @param bool $document
+ */
+ protected function generateHandler($forceOverwrite, $document)
+ {
+ $dir = $this->bundle->getPath();
+
+ $parts = explode('\\', $this->entity);
+ $entityClass = array_pop($parts);
+ $entityNamespace = implode('\\', $parts);
+
+ $target = sprintf(
+ '%s/Handler/%s/%sRESTHandler.php',
+ $dir,
+ str_replace('\\', '/', $entityNamespace),
+ $entityClass
+ );
+
+ if (!is_dir(dirname($target))) {
+ mkdir(dirname($target), 0777, true);
+ }
+
+ if (!$forceOverwrite && file_exists($target)) {
+ throw new \RuntimeException('Unable to generate the controller as it already exists.');
+ }
+
+ $this->renderFile(
+ 'rest/handler.php.twig',
+ $target,
+ array(
+ 'route_prefix' => $this->routePrefix,
+ 'route_name_prefix' => $this->routeNamePrefix,
+ 'bundle' => $this->bundle->getName(),
+ 'entity' => $this->entity,
+ 'entity_class' => $entityClass,
+ 'namespace' => $this->bundle->getNamespace(),
+ 'entity_namespace' => $entityNamespace,
+ 'format' => $this->format,
+ 'document' => $document,
+ )
+ );
+ }
+
+ /**
+ *
+ */
+ public function generateExceptionClass()
+ {
+ $dir = $this->bundle->getPath();
+
+ $target = sprintf('%s/Exception/InvalidFormException.php', $dir);
+
+ if (!is_dir(dirname($target))) {
+ mkdir(dirname($target), 0777, true);
+ }
+
+ $this->renderFile(
+ 'rest/form_exception.php.twig',
+ $target,
+ array('namespace' => $this->bundle->getNamespace())
+ );
+ }
+
+ /**
+ * Declares the handler as a service
+ * @param $service_format
+ */
+ public function declareService($service_format)
+ {
+ $dir = $this->bundle->getPath();
+
+ $parts = explode('\\', $this->entity);
+ $entityClass = array_pop($parts);
+ $entityNamespace = implode('\\', $parts);
+ if (strlen($entityNamespace) > 0)
+ {
+ $entityNamespace .= '\\';
+ }
+ $namespace = $this->bundle->getNamespace();
+
+ $bundleName = strtolower($this->bundle->getName());
+ $entityName = strtolower($this->entity);
+ $entityName = str_replace('\\','.',$entityName);
+
+ $services = sprintf(
+ "%s/Resources/config/servicesREST.".$service_format,
+ $dir
+ );
+
+ $handlerClass = sprintf(
+ "%s\\Handler\\%s%sRESTHandler",
+ $namespace,
+ $entityNamespace,
+ $entityClass
+ );
+
+ $newId = sprintf(
+ "%s.%s.handler",
+ str_replace("bundle", "", $bundleName),
+ $entityName
+ );
+
+ $fileName = sprintf(
+ "%s/DependencyInjection/%s.php",
+ $dir,
+ str_replace("Bundle", "Extension", $this->bundle->getName())
+ );
+
+ if (!is_file($services)) {
+ $this->renderFile("rest/service/services.".$service_format.".twig", $services, array());
+ }
+
+ switch($service_format)
+ {
+ case 'xml':
+ $this->handleServiceDeclarationAsXML($services,$newId, $handlerClass,$namespace,$entityNamespace,$entityClass,$fileName);
+ break;
+ case 'yml':
+ default:
+ $this->handleServiceDeclarationAsYML($services,$newId, $handlerClass,$namespace,$entityNamespace,$entityClass,$fileName);
+ break;
+
+ }
+ $this->updateDIFile($fileName,$service_format);
+ }
+
+ private function handleServiceDeclarationAsXML($services, $newId, $handlerClass,$namespace,$entityNamespace,$entityClass,$fileName)
+ {
+ //this could be saved more readable by using dom_import_simplexml (http://stackoverflow.com/questions/1191167/format-output-of-simplexml-asxml)
+ $newXML = simplexml_load_file($services);
+
+ if (!($servicesTag = $newXML->services)) {
+ $servicesTag = $newXML->addChild("services");
+ }
+
+ $search = $newXML->xpath("//*[@id='$newId']");
+ if (!$search) {
+ $newServiceTag = $servicesTag->addChild("service");
+ $newServiceTag->addAttribute("id", $newId);
+ $newServiceTag->addAttribute("class", $handlerClass);
+
+ $entityManagerTag = $newServiceTag->addChild("argument");
+ $entityManagerTag->addAttribute("type", "service");
+ $entityManagerTag->addAttribute("id", "doctrine.orm.entity_manager");
+
+ $newServiceTag->addChild(
+ "argument",
+ sprintf(
+ "%s\\Entity\\%s%s",
+ $namespace,
+ $entityNamespace,
+ $entityClass
+ )
+ );
+
+ $formFactoryTag = $newServiceTag->addChild("argument");
+ $formFactoryTag->addAttribute("type", "service");
+ $formFactoryTag->addAttribute("id", "form.factory");
+ }
+
+ $newXML->saveXML($services);
+ }
+
+ private function handleServiceDeclarationAsYML($services, $newId, $handlerClass,$namespace,$entityNamespace,$entityClass,$fileName)
+ {
+ $yml_file = Yaml::parse(file_get_contents($services));
+ $params = $yml_file['parameters'];
+ if (!is_array($params))
+ {
+ $params = array();
+ }
+ $yml_file['parameters'] =
+ array_merge(
+ $params,
+ array(
+ $newId.'.handler_class' => $handlerClass,
+ $newId.'.entity_class' => sprintf(
+ "%s\\Entity\\%s%s",
+ $namespace,
+ $entityNamespace,
+ $entityClass
+ ),
+ )
+ );
+ $yml_services = $yml_file['services'];
+ if (!is_array($yml_services))
+ {
+ $yml_services = array();
+ }
+ $yml_file['services'] =
+ array_merge(
+ $yml_services,
+ array(
+ $newId => array(
+ 'class' => '%'.$newId.'.handler_class%', 'arguments' => array(
+ '@doctrine.orm.entity_manager',
+ '%'.$newId.'.entity_class%',
+ '@form.factory',
+ ),
+ ),
+ )
+ );
+ $yml_content = Yaml::dump($yml_file, 3);
+ file_put_contents($services, $yml_content);
+ }
+
+ /**
+ * @param $fileName
+ * @param $serviceFormat
+ */
+ private function updateDIFile($fileName, $serviceFormat)
+ {
+ $toInput = '';
+ switch($serviceFormat)
+ {
+ case 'xml':
+ $toInput = PHP_EOL . "\t\t\$loader2 = new Loader\\XmlFileLoader(\$container, new FileLocator(__DIR__ . '/../Resources/config'));" . PHP_EOL .
+ "\t\t\$loader2->load('servicesREST.".$serviceFormat."');" . PHP_EOL . "\t";
+ break;
+ case 'yml':
+ default:
+ $toInput = PHP_EOL . "\t\t\$loader2 = new Loader\\YamlFileLoader(\$container, new FileLocator(__DIR__ . '/../Resources/config'));" . PHP_EOL .
+ "\t\t\$loader2->load('servicesREST.".$serviceFormat."');" . PHP_EOL . "\t";
+ break;
+
+ }
+
+ if (!file_exists(dirname($fileName)))
+ {
+ mkdir(dirname($fileName), 0777, true);
+ }
+ if (!file_exists($fileName))
+ {
+ $this->handleExtensionFileCreation($fileName);
+ }
+ $text = file_get_contents($fileName);
+
+ if (strpos($text, "servicesREST.".$serviceFormat) == false) {
+ $position = strpos($text, "}", strpos($text, "function load("));
+
+ $newContent = substr_replace($text, $toInput, $position, 0);
+ file_put_contents($fileName, $newContent);
+ }
+ }
+
+ /**
+ * @param $fileName
+ */
+ private function handleExtensionFileCreation($fileName)
+ {
+ $parts = explode('\\', $this->entity);
+ $entityNamespace = implode('\\', $parts);
+
+ $this->renderFile(
+ 'rest/extension.php.twig',
+ $fileName,
+ array(
+ 'class_name' => str_replace("Bundle", "Extension", $this->bundle->getName()),
'namespace' => $this->bundle->getNamespace(),
'entity_namespace' => $entityNamespace,
- 'format' => $this->format,
)
);
}
+ private function makeFormatUserFriendly($format)
+ {
+ $returnFormat = '';
+ $parts = explode('-',$format);
+ foreach($parts as $part)
+ {
+ $returnFormat .= ucfirst($part);
+ }
+
+ return $returnFormat;
+ }
+
/**
* Generates the functional test class only.
- *
+ * @param boolean $forceOverwrite whether or not to force overwriting or not
+ * @param string $format either none, no-authentication or oauth
*/
- protected function generateTestClass()
+ protected function generateTestClass($forceOverwrite, $format)
{
+ if ($format === 'none')
+ {
+ return;
+ }
+
+ $friendlyFormat = $this->makeFormatUserFriendly($format);
+
+ $base_dir = $this->bundle->getPath() . '/Tests/Base';
+ $dir = $this->bundle->getPath() . '/Tests/Controller';
+
$parts = explode('\\', $this->entity);
$entityClass = array_pop($parts);
$entityNamespace = implode('\\', $parts);
- $dir = $this->bundle->getPath() . '/Tests/Controller';
$target = $dir . '/' . str_replace('\\', '/', $entityNamespace) . '/' . $entityClass . 'RESTControllerTest.php';
+ $base_target = $base_dir . '/' . $friendlyFormat . 'BaseCase.php';
+
+ if ($forceOverwrite === false && file_exists($target))
+ {
+ throw new \RuntimeException('Unable to generate the test as it already exists.');
+ }
+
+ $this->generateBaseTestCaseIfNotExists($forceOverwrite, $format, $friendlyFormat, $base_target);
+
+ $this->renderFile(
+ 'rest/test.php.twig',
+ $target,
+ array(
+ 'format' => $format,
+ 'friendly_format' => $friendlyFormat,
+ 'fields' => $this->metadata->fieldMappings,
+ 'assoc_mapping' => $this->metadata->associationMappings,
+ 'entity_constraints' => $this->entityConstraints,
+ 'base_file' => $base_target,
+ 'route_prefix' => $this->routePrefix,
+ 'route_name_prefix' => $this->routeNamePrefix,
+ 'entity' => $this->entity,
+ 'bundle' => $this->bundle->getName(),
+ 'entity_class' => $entityClass,
+ 'namespace' => $this->bundle->getNamespace(),
+ 'entity_namespace' => $entityNamespace,
+ 'actions' => $this->actions,
+ 'form_type_name' => strtolower(str_replace('\\', '_', $this->bundle->getNamespace()) . ($parts ? '_' : '') . implode('_', $parts) . '_' . $entityClass . 'Type'),
+ )
+ );
+ }
+
+ /**
+ * @param $overwrite
+ * @param $format
+ * @param $friendlyFormat
+ * @param $target
+ */
+ protected function generateBaseTestCaseIfNotExists($overwrite, $format, $friendlyFormat, $target)
+ {
+ $parts = explode('\\', $this->entity);
+ $entityClass = array_pop($parts);
+ $entityNamespace = implode('\\', $parts);
+
+ if (file_exists($target))
+ {
+ return;
+ }
$this->renderFile(
- 'rest/tests/test.php.twig',
+ 'rest/tests/base/'.$format.'.php.twig',
$target,
array(
+ 'format' => $format,
+ 'friendly_format' => $friendlyFormat,
+ 'fields' => $this->metadata->fieldMappings,
'route_prefix' => $this->routePrefix,
'route_name_prefix' => $this->routeNamePrefix,
'entity' => $this->entity,
@@ -191,7 +557,6 @@ protected function generateTestClass()
'namespace' => $this->bundle->getNamespace(),
'entity_namespace' => $entityNamespace,
'actions' => $this->actions,
- 'form_type_name' => strtolower(str_replace('\\', '_', $this->bundle->getNamespace()) . ($parts ? '_' : '') . implode('_', $parts) . '_' . $entityClass . 'Type'),
)
);
}
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/delete.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/delete.php.twig
index 8e17c61..39bb2e0 100644
--- a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/delete.php.twig
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/delete.php.twig
@@ -2,32 +2,45 @@
{% block phpdoc_method_header %}
* Delete a {{ entity }} entity.
*
+{% block documentation %}
+{% if document %}
+ * @ApiDoc(
+ * resource = true,
+ * description = "Delete a {{ entity }} entity.",
+ * statusCodes = {
+ * 204 = "No content. Successfully excluded.",
+ * 404 = "Not found."
+ * }
+ * )
+{% endif %}
+{% endblock documentation %}
* @View(statusCode=204)
*
- * @param Request $request
- * @param $entity
+ * @param $id
*
* @return Response
{% endblock phpdoc_method_header %}
{% block phpdoc_method_annotations %}
-{% if 'annotation' == format %}{% endif %}
+{% if 'annotation' == format %}
+ *
+ * @RESTDelete("/{id}.{_format}")
+ *
+{% endif %}
{% endblock phpdoc_method_annotations %}
*/
{% block method_definition %}
- public function deleteAction(Request $request, {{ entity }} $entity)
+ public function deleteAction($id)
{% endblock method_definition %}
{
{% block method_body %}
+ ${{ entity_class|lower }} = $this->getOr404($id);
try {
- $em = $this->getDoctrine()->getManager();
- $em->remove($entity);
- $em->flush();
-
- return null;
- } catch (\Exception $e) {
- return FOSView::create($e->getMessage(), Codes::HTTP_INTERNAL_SERVER_ERROR);
+ return $this->container->get('{{ bundle|replace({'Bundle': ''})|lower}}.{{ entity|lower|replace({"\\":'.'}) }}.handler')->delete(${{ entity_class|lower }});
+ } catch (\Exception $exception) {
+ throw new \RuntimeException("Exclusion not allowed");
}
{% endblock method_body %}
{% block method_return '' %}
}
{% block form '' %}
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/getAll.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/getAll.php.twig
old mode 100644
new mode 100755
index 0acdc63..813f72d
--- a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/getAll.php.twig
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/getAll.php.twig
@@ -2,6 +2,18 @@
{% block phpdoc_method_header %}
* Get all {{ entity }} entities.
*
+{% block documentation %}
+{% if document %}
+ * @ApiDoc(
+ * resource = true,
+ * description = "Get all {{ entity }} entities.",
+ * statusCodes = {
+ * 200 = "List of {{ entity }}",
+ * 204 = "No content. Nothing to list."
+ * }
+ * )
+{% endif %}
+{% endblock documentation %}
* @View(serializerEnableMaxDepthChecks=true)
*
* @param ParamFetcherInterface $paramFetcher
@@ -9,12 +21,16 @@
* @return Response
*
* @QueryParam(name="offset", requirements="\d+", nullable=true, description="Offset from which to start listing notes.")
- * @QueryParam(name="limit", requirements="\d+", default="20", description="How many notes to return.")
+ * @QueryParam(name="limit", requirements="\d+", default="20", description="How many {{ entity_class }} to return.")
* @QueryParam(name="order_by", nullable=true, array=true, description="Order by fields. Must be an array ie. &order_by[name]=ASC&order_by[description]=DESC")
* @QueryParam(name="filters", nullable=true, array=true, description="Filter by fields. Must be an array ie. &filters[id]=3")
{% endblock phpdoc_method_header %}
{% block phpdoc_method_annotations %}
-{% if 'annotation' == format %}{% endif %}
+{% if 'annotation' == format %}
+ *
+ * @RESTGet(".{_format}")
+ *
+{% endif %}
{% endblock phpdoc_method_annotations %}
*/
{% block method_definition %}
@@ -22,23 +38,18 @@
{% endblock method_definition %}
{
{% block method_body %}
- try {
- $offset = $paramFetcher->get('offset');
- $limit = $paramFetcher->get('limit');
- $order_by = $paramFetcher->get('order_by');
- $filters = !is_null($paramFetcher->get('filters')) ? $paramFetcher->get('filters') : array();
+ $offset = $paramFetcher->get('offset');
+ $limit = $paramFetcher->get('limit');
+ $order_by = $paramFetcher->get('order_by');
+ $filters = !is_null($paramFetcher->get('filters')) ? $paramFetcher->get('filters') : array();
- $em = $this->getDoctrine()->getManager();
- $entities = $em->getRepository('{{ bundle }}:{{ entity }}')->findBy($filters, $order_by, $limit, $offset);
- if ($entities) {
- return $entities;
- }
-
- return FOSView::create('Not Found', Codes::HTTP_NO_CONTENT);
- } catch (\Exception $e) {
- return FOSView::create($e->getMessage(), Codes::HTTP_INTERNAL_SERVER_ERROR);
+ $answer{{ resource ? "['" ~ entity|lower ~ "']" }} = $this->container->get('{{ bundle|replace({'Bundle': ''})|lower}}.{{ entity|lower|replace({"\\":'.'}) }}.handler')->getAll($filters, $order_by, $limit, $offset);
+ if ($answer{{ resource ? "['" ~ entity|lower ~ "']" }}) {
+ return $answer;
}
+ return null;
{% endblock method_body %}
{% block method_return '' %}
}
{% block form '' %}
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/getById.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/getById.php.twig
old mode 100644
new mode 100755
index 848910e..2a444db
--- a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/getById.php.twig
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/getById.php.twig
@@ -2,23 +2,40 @@
{% block phpdoc_method_header %}
* Get a {{ entity }} entity
*
+{% block documentation %}
+{% if document %}
+ * @ApiDoc(
+ * resource = true,
+ * description = "Get a {{ entity }} entity.",
+ * statusCodes = {
+ * 200 = "{{ entity }}'s object.",
+ * 404 = "Not Found."
+ * }
+ * )
+{% endif %}
+{% endblock documentation %}
* @View(serializerEnableMaxDepthChecks=true)
*
* @return Response
+ * @param $id
{% endblock phpdoc_method_header %}
- *
{% block phpdoc_method_annotations %}
{% if 'annotation' == format %}
+ *
+ * @RESTGet("/{id}.{_format}")
+ *
{% endif %}
{% endblock phpdoc_method_annotations %}
*/
{% block method_definition %}
- public function getAction({{ entity }} $entity)
+ public function getAction($id)
{% endblock method_definition %}
{
{% block method_body %}
- return $entity;
+ $answer{{ resource ? "['" ~ entity_class|lower ~ "']" }} = $this->getOr404($id);
+ return $answer;
{% endblock method_body %}
{% block method_return '' %}
}
{% block form '' %}
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/getOr404.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/getOr404.php.twig
new file mode 100755
index 0000000..19d2cc6
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/getOr404.php.twig
@@ -0,0 +1,21 @@
+ /**
+{% block phpdoc_method_header %}
+ * Get a entity or throw a exception
+ *
+ * @param $id
+ * @return {{ entity_class }} $entity
+{% endblock phpdoc_method_header %}
+ */
+{% block method_definition %}
+ protected function getOr404($id)
+{% endblock method_definition %}
+ {
+{% block method_body %}
+ if (!($entity = $this->container->get('{{ bundle|replace({'Bundle': ''})|lower}}.{{ entity|lower|replace({"\\":'.'}) }}.handler')->get($id))) {
+ throw new NotFoundHttpException(sprintf('The resource \'%s\' was not found.',$id));
+ }
+
+ return $entity;
+{% endblock method_body %}
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/patch.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/patch.php.twig
old mode 100644
new mode 100755
index 0a9b710..8788060
--- a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/patch.php.twig
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/patch.php.twig
@@ -2,24 +2,43 @@
{% block phpdoc_method_header %}
* Partial Update to a {{ entity }} entity.
*
+{% block documentation %}
+{% if document %}
+ * @ApiDoc(
+ * resource = true,
+ * description = "Partial Update to a {{ entity }} entity.",
+ * statusCodes = {
+ * 200 = "Updated object.",
+ * 400 = "Bad Request. Verify your params.",
+ * 404 = "Not Found."
+ * }
+ * )
+{% endif %}
+{% endblock documentation %}
* @View(serializerEnableMaxDepthChecks=true)
*
* @param Request $request
- * @param $entity
+ * @param $id
*
* @return Response
{% endblock phpdoc_method_header %}
{% block phpdoc_method_annotations %}
-{% if 'annotation' == format %}{% endif %}
+{% if 'annotation' == format %}
+ *
+ * @RESTPatch("/{id}.{_format}")
+ *
+{% endif %}
{% endblock phpdoc_method_annotations %}
*/
{% block method_definition %}
- public function patchAction(Request $request, {{ entity }} $entity)
+ public function patchAction(Request $request, $id)
{% endblock method_definition %}
{
{% block method_body %}
- return $this->putAction($request, $entity);
+ $answer{{ resource ? "['" ~ entity|lower ~ "']" }} = $this->container->get('{{ bundle|replace({'Bundle': ''})|lower}}.{{ entity|lower|replace({"\\":'.'}) }}.handler')->patch($this->getOr404($id), $request->request->all());
+ return $answer;
{% endblock method_body %}
{% block method_return '' %}
}
{% block form '' %}
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/post.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/post.php.twig
old mode 100644
new mode 100755
index 945a687..9a3be5a
--- a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/post.php.twig
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/post.php.twig
@@ -2,15 +2,31 @@
{% block phpdoc_method_header %}
* Create a {{ entity }} entity.
*
+{% block documentation %}
+{% if document %}
+ * @ApiDoc(
+ * resource = true,
+ * description = "Create a {{ entity }} entity.",
+ * statusCodes = {
+ * 201 = "Created object.",
+ * 400 = "Bad Request. Verify your params.",
+ * 404 = "Not Found."
+ * }
+ * )
+{% endif %}
+{% endblock documentation %}
* @View(statusCode=201, serializerEnableMaxDepthChecks=true)
*
* @param Request $request
*
* @return Response
{% endblock phpdoc_method_header %}
- *
{% block phpdoc_method_annotations %}
-{% if 'annotation' == format %}{% endif %}
+{% if 'annotation' == format %}
+ *
+ * @RESTPost(".{_format}")
+ *
+{% endif %}
{% endblock phpdoc_method_annotations %}
*/
{% block method_definition %}
@@ -18,22 +34,15 @@
{% endblock method_definition %}
{
{% block method_body %}
- $entity = new {{ entity }}();
- $form = $this->createForm(get_class(new {{ entity }}Type()), $entity, array("method" => $request->getMethod()));
- $this->removeExtraFields($request, $form);
- $form->handleRequest($request);
+ try {
+ $new = $this->container->get('{{ bundle|replace({'Bundle': ''})|lower}}.{{ entity|lower|replace({"\\":'.'}) }}.handler')->post($request->request->all());
+ $answer{{ resource ? "['" ~ entity|lower ~ "']" }} = $new;
- if ($form->isValid()) {
- $em = $this->getDoctrine()->getManager();
- $em->persist($entity);
- $em->flush();
-
- return $entity;
+ return $answer;
+ } catch (InvalidFormException $exception) {
+ return $exception->getForm();
}
-
{% endblock method_body %}
-{% block method_return %}
- return FOSView::create(array('errors' => $form->getErrors()), Codes::HTTP_INTERNAL_SERVER_ERROR);
-{% endblock method_return %}
}
{% block form '' %}
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/put.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/put.php.twig
old mode 100644
new mode 100755
index 7597729..b887391
--- a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/put.php.twig
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/actions/put.php.twig
@@ -2,39 +2,56 @@
{% block phpdoc_method_header %}
* Update a {{ entity }} entity.
*
+{% block documentation %}
+{% if document %}
+ * @ApiDoc(
+ * resource = true,
+ * description = "Update a {{ entity }} entity.",
+ * statusCodes = {
+ * 200 = "Updated object.",
+ * 201 = "Created object.",
+ * 400 = "Bad Request. Verify your params.",
+ * 404 = "Not Found."
+ * }
+ * )
+{% endif %}
+{% endblock documentation %}
* @View(serializerEnableMaxDepthChecks=true)
*
* @param Request $request
- * @param $entity
+ * @param $id
*
* @return Response
{% endblock phpdoc_method_header %}
{% block phpdoc_method_annotations %}
-{% if 'annotation' == format %}{% endif %}
+{% if 'annotation' == format %}
+ *
+ * @RESTPut("/{id}.{_format}")
+ *
+{% endif %}
{% endblock phpdoc_method_annotations %}
*/
{% block method_definition %}
- public function putAction(Request $request, {{ entity }} $entity)
+ public function putAction(Request $request, $id)
{% endblock method_definition %}
{
{% block method_body %}
try {
- $em = $this->getDoctrine()->getManager();
- $request->setMethod('PATCH'); //Treat all PUTs as PATCH
- $form = $this->createForm(get_class(new {{ entity }}Type()), $entity, array("method" => $request->getMethod()));
- $this->removeExtraFields($request, $form);
- $form->handleRequest($request);
- if ($form->isValid()) {
- $em->flush();
-
- return $entity;
+ if (${{ entity_class|lower }} = $this->container->get('{{ bundle|replace({'Bundle': ''})|lower}}.{{ entity|lower|replace({"\\":'.'}) }}.handler')->get($id)) {
+ $answer{{ resource ? "['" ~ entity|lower ~ "']" }}= $this->container->get('{{ bundle|replace({'Bundle': ''})|lower}}.{{ entity|lower|replace({"\\":'.'}) }}.handler')->put(${{ entity_class|lower }}, $request->request->all());
+ $code = Codes::HTTP_OK;
+ } else {
+ $answer{{ resource ? "['" ~ entity|lower ~ "']" }} = $this->container->get('{{ bundle|replace({'Bundle': ''})|lower}}.{{ entity|lower|replace({"\\":'.'}) }}.handler')->post($request->request->all());
+ $code = Codes::HTTP_CREATED;
}
-
- return FOSView::create(array('errors' => $form->getErrors()), Codes::HTTP_INTERNAL_SERVER_ERROR);
- } catch (\Exception $e) {
- return FOSView::create($e->getMessage(), Codes::HTTP_INTERNAL_SERVER_ERROR);
+ } catch (InvalidFormException $exception) {
+ return $exception->getForm();
}
+
+ $view = $this->view($answer, $code);
+ return $this->handleView($view);
{% endblock method_body %}
{% block method_return '' %}
}
{% block form '' %}
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/controller.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/controller.php.twig
old mode 100644
new mode 100755
index 6cf716a..57638d5
--- a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/controller.php.twig
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/controller.php.twig
@@ -5,6 +5,7 @@ namespace {{ namespace }}\Controller{{ entity_namespace ? '\\' ~ entity_namespac
{% block use_statements %}
use {{ namespace }}\Entity\{{ entity }};
use {{ namespace }}\Form\{{ entity }}Type;
+use {{ namespace }}\Exception\InvalidFormException;
use FOS\RestBundle\Controller\Annotations\QueryParam;
use FOS\RestBundle\Controller\Annotations\RouteResource;
@@ -17,28 +18,38 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
+use FOS\RestBundle\Controller\Annotations\Get as RESTGet;
+use FOS\RestBundle\Controller\Annotations\Put as RESTPut;
+use FOS\RestBundle\Controller\Annotations\Patch as RESTPatch;
+use FOS\RestBundle\Controller\Annotations\Post as RESTPost;
+use FOS\RestBundle\Controller\Annotations\Delete as RESTDelete;
{%- endif %}
+
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
-use Voryx\RESTGeneratorBundle\Controller\VoryxController;
+use FOS\RestBundle\Controller\FOSRestController;
+{% if document %}
+use Nelmio\ApiDocBundle\Annotation\ApiDoc;
+{% endif %}
{% endblock use_statements %}
/**
{% block phpdoc_class_header %}
- * {{ entity }} controller.
+ * {{ entity|replace({"\\":'/'}) }} controller.
{% endblock phpdoc_class_header %}
* @RouteResource("{{ entity }}")
{% block phpdoc_class_annotations %}
{% if 'annotation' == format %}
- * @Route("/{{ route_prefix }}")
+ * @Route("/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}")
{% endif %}
{% endblock phpdoc_class_annotations %}
*/
{% block class_definition %}
-class {{ entity_class }}RESTController extends VoryxController
+class {{ entity_class }}RESTController extends FOSRestController
{% endblock class_definition %}
{
{% block class_body %}
@@ -53,5 +64,7 @@ class {{ entity_class }}RESTController extends VoryxController
{%- include 'rest/actions/patch.php.twig' %}
{%- include 'rest/actions/delete.php.twig' %}
+
+ {%- include 'rest/actions/getOr404.php.twig' %}
{% endblock class_body %}
}
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/extension.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/extension.php.twig
new file mode 100644
index 0000000..0748229
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/extension.php.twig
@@ -0,0 +1,29 @@
+form = $form;
+ }
+
+ public function getForm()
+ {
+ return $this->form;
+ }
+{% endblock class_body %}
+}
\ No newline at end of file
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler.php.twig
new file mode 100644
index 0000000..bdeeef3
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler.php.twig
@@ -0,0 +1,49 @@
+om = $om;
+ $this->entityClass = $entityClass;
+ $this->repository = $this->om->getRepository($this->entityClass);
+ $this->formFactory = $formFactory;
+{% endblock method_body %}
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler/delete.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler/delete.php.twig
new file mode 100644
index 0000000..aa1acbe
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler/delete.php.twig
@@ -0,0 +1,23 @@
+ /**
+{% block phpdoc_method_header %}
+ * @param {{ entity_class }} ${{ entity_class|lower }}
+ * @return null
+ * @throws \RuntimeException
+{% endblock phpdoc_method_header %}
+ */
+{% block method_definition %}
+ public function delete({{ entity_class }} ${{ entity_class|lower }})
+{% endblock method_definition %}
+ {
+{% block method_body %}
+ try {
+ $this->om->remove(${{ entity_class|lower }});
+ $this->om->flush();
+
+ return null;
+ } catch (\Exception $e) {
+ throw new \RuntimeException();
+ }
+{% endblock method_body %}
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler/extras.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler/extras.php.twig
new file mode 100644
index 0000000..2972cd3
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler/extras.php.twig
@@ -0,0 +1,34 @@
+{% block extras_methods %}
+ /**
+{% block phpdoc_method_header %}
+ * @param {{ entity_class }} ${{ entity_class|lower }}
+ * @param array $parameters
+ * @param string $method
+ * @return {{ entity_class }} ${{ entity_class|lower }}
+ * @throws InvalidFormException
+{% endblock phpdoc_method_header %}
+ */
+ private function processForm({{ entity_class }} ${{ entity_class|lower }}, array $parameters, $method = "PUT")
+ {
+ $form = $this->formFactory->create(get_class(new {{ entity_class }}Type()), ${{ entity_class|lower }}, array('method' => $method));
+ $form->submit($parameters, 'PATCH' !== $method);
+ if ($form->isValid()) {
+ ${{ entity_class|lower }} = $form->getData();
+ $this->om->persist(${{ entity_class|lower }});
+ $this->om->flush();
+
+ return ${{ entity_class|lower }};
+ }
+ throw new InvalidFormException('Invalid submitted data', $form);
+ }
+
+{% if document %}
+ /**
+ * @return {{ entity_class }} ${{ entity_class|lower }}
+ */
+{% endif %}
+ private function create{{ entity_class }}()
+ {
+ return new $this->entityClass();
+ }
+{% endblock extras_methods %}
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler/get.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler/get.php.twig
new file mode 100644
index 0000000..f7adfb5
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler/get.php.twig
@@ -0,0 +1,15 @@
+ /**
+{% block phpdoc_method_header %}
+ * @param int $id
+ * @return {{ entity_class }} ${{ entity_class|lower }}
+ {% endblock phpdoc_method_header %}
+ */
+{% block method_definition %}
+ public function get($id)
+{% endblock method_definition %}
+ {
+{% block method_body %}
+ return $this->repository->find($id);
+{% endblock method_body %}
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler/getAll.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler/getAll.php.twig
new file mode 100644
index 0000000..b1f1dad
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler/getAll.php.twig
@@ -0,0 +1,18 @@
+ /**
+{% block phpdoc_method_header %}
+ * @param array $filters
+ * @param string|null $order_by The field to order by
+ * @param int|null $limit The limit
+ * @param int|null $offset The offset from the first record
+ * @return {{ entity_class }}[] ${{ entity_class|lower }}s
+{% endblock phpdoc_method_header %}
+ */
+{% block method_definition %}
+ public function getAll($filters = array(), $order_by = null, $limit = null, $offset = null)
+{% endblock method_definition %}
+ {
+{% block method_body %}
+ return $this->repository->findBy($filters, $order_by, $limit, $offset);
+{% endblock method_body %}
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler/patch.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler/patch.php.twig
new file mode 100644
index 0000000..8dce3d7
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler/patch.php.twig
@@ -0,0 +1,17 @@
+ /**
+{% block phpdoc_method_header %}
+ * @param {{ entity_class }} ${{ entity_class|lower }}
+ * @param array $parameters
+ * @return {{ entity_class }} ${{ entity_class|lower }}
+ * @throws InvalidFormException
+{% endblock phpdoc_method_header %}
+ */
+{% block method_definition %}
+ public function patch({{ entity_class }} ${{ entity_class|lower }}, array $parameters)
+{% endblock method_definition %}
+ {
+{% block method_body %}
+ return $this->processForm(${{ entity_class|lower }}, $parameters, 'PATCH');
+{% endblock method_body %}
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler/post.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler/post.php.twig
new file mode 100644
index 0000000..6b1d84d
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler/post.php.twig
@@ -0,0 +1,18 @@
+ /**
+{% block phpdoc_method_header %}
+ * @param array $parameters
+ * @return {{ entity_class }} ${{ entity_class|lower }}
+ * @throws InvalidFormException
+{% endblock phpdoc_method_header %}
+ */
+{% block method_definition %}
+ public function post($parameters)
+{% endblock method_definition %}
+ {
+{% block method_body %}
+ ${{ entity_class|lower }} = $this->create{{ entity_class }}();
+
+ return $this->processForm(${{ entity_class|lower }}, $parameters, 'POST');
+{% endblock method_body %}
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler/put.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler/put.php.twig
new file mode 100644
index 0000000..7c5eddf
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/handler/put.php.twig
@@ -0,0 +1,17 @@
+ /**
+{% block phpdoc_method_header %}
+ * @param {{ entity_class }} ${{ entity_class|lower }}
+ * @param array $parameters
+ * @return {{ entity_class }} ${{ entity_class|lower }}
+ * @throws InvalidFormException
+{% endblock phpdoc_method_header %}
+ */
+{% block method_definition %}
+ public function put({{ entity_class }} ${{ entity_class|lower }}, array $parameters)
+{% endblock method_definition %}
+ {
+{% block method_body %}
+ return $this->processForm(${{ entity_class|lower }}, $parameters, 'PUT');
+{% endblock method_body %}
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/service/services.xml.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/service/services.xml.twig
new file mode 100644
index 0000000..124152f
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/service/services.xml.twig
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/service/services.yml.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/service/services.yml.twig
new file mode 100644
index 0000000..12b6ebf
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/service/services.yml.twig
@@ -0,0 +1,3 @@
+parameters:
+
+services:
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/test.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/test.php.twig
new file mode 100644
index 0000000..c126411
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/test.php.twig
@@ -0,0 +1,40 @@
+username === null || $this->password === null)
+ {
+ die('Please make sure to fill in an API user username/password in this file (path/to/Tests/oauthBase.php)');
+ }
+ parent::__construct();
+ $client = static::createClient();
+ $clientManager = $client->getContainer()->get('fos_oauth_server.client_manager.default');
+ /** @var OAuthClient $oauth_client */
+ $oauth_client = $clientManager->createClient();
+ $oauth_client->setRedirectUris(array());
+ $oauth_client->setAllowedGrantTypes(array('authorization_code', 'password', 'refresh_token', 'client_credentials'));
+ $clientManager->updateClient($oauth_client);
+
+ $public_key_split = $oauth_client->getPublicId();
+ $secret_key_split = $oauth_client->getSecret();
+
+ self::$client_id = $public_key_split;
+ self::$client_secret = $secret_key_split;
+ $this->assertNotEquals(self::$client_id, null);
+ $this->assertNotEquals(self::$client_secret, null);
+
+ $url = "/oauth/v2/token?client_id=".self::$client_id.'&client_secret='.self::$client_secret.'&grant_type=password'.
+ '&username='.$this->username.'&password='.$this->password;
+ $crawler = $client->request('GET', $url, array(), array(), array('HTTP_ACCEPT' => 'application/json', 'HTTP_CONTENT_TYPE' => 'application/json'));
+ $json_object = json_decode($client->getResponse()->getContent());
+ self::$access_token = $json_object->access_token;
+ }
+}
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/delete.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/delete.php.twig
new file mode 100644
index 0000000..5dae00d
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/delete.php.twig
@@ -0,0 +1,6 @@
+ public function testDelete()
+ {
+ $crawler = self::$client->request('DELETE', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json');
+ $this->assertEquals(204, self::$client->getResponse()->getStatusCode(), 'Expected a 204 response status after requesting DELETE /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json, got '.self::$client->getResponse()->getStatusCode());
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/get.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/get.php.twig
new file mode 100644
index 0000000..3b829be
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/get.php.twig
@@ -0,0 +1,6 @@
+ public function testGet()
+ {
+ $crawler = self::$client->request('GET', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json');
+ $this->assertEquals(200, self::$client->getResponse()->getStatusCode(), 'Expected a 200 response status after requesting /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json, got '.self::$client->getResponse()->getStatusCode());
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/getAfterDelete.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/getAfterDelete.php.twig
new file mode 100644
index 0000000..a49ddb1
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/getAfterDelete.php.twig
@@ -0,0 +1,6 @@
+ public function testGetOnDeletedItem()
+ {
+ $crawler = self::$client->request('GET', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json');
+ $this->assertEquals(200, self::$client->getResponse()->getStatusCode(), 'Expected a 200 response status after requesting /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json, got '.self::$client->getResponse()->getStatusCode());
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/getAll.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/getAll.php.twig
new file mode 100644
index 0000000..c90c8d5
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/getAll.php.twig
@@ -0,0 +1,13 @@
+ public function testGetAllWithContent()
+ {
+ $crawler = self::$client->request('GET', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}.json');
+ $this->assertEquals(200, self::$client->getResponse()->getStatusCode(), 'Expected a 200 response status after requesting /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}.json, got '.self::$client->getResponse()->getStatusCode());
+
+ $obj = json_decode(self::$client->getResponse()->getContent());
+ $this->assertTrue(is_array($obj), 'expected decoded response content to be array, got '.gettype($obj));
+ $this->assertTrue(count($obj) > 0, 'expected decoded response content to be an array with more then 0 items, got '.count($obj));
+ $first = reset($obj);
+ $this->assertTrue(array_key_exists('id', $first), 'expected first array item to have a key id');
+ self::$id = $first->id;
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/getAllWithoutContent.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/getAllWithoutContent.php.twig
new file mode 100644
index 0000000..c722193
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/getAllWithoutContent.php.twig
@@ -0,0 +1,6 @@
+ public function testGetAllWithoutContent()
+ {
+ $crawler = self::$client->request('GET', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}.json');
+ $this->assertEquals(204, self::$client->getResponse()->getStatusCode(), 'Expected a 204 response status after requesting /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}.json, got '.self::$client->getResponse()->getStatusCode());
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/patch.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/patch.php.twig
new file mode 100644
index 0000000..cef4543
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/patch.php.twig
@@ -0,0 +1,13 @@
+ public function testPatch()
+ {
+ $json = json_encode(self::$sample_object);
+
+ $crawler = self::$client->request('PATCH', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json', array(), array(), ['HTTP_CONTENT_TYPE' => 'application/json', 'CONTENT_TYPE' => 'application/json'], $json);
+ $this->assertEquals(200, self::$client->getResponse()->getStatusCode(), 'Expected a 200 response status after requesting PATCH /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json, got '.self::$client->getResponse()->getStatusCode());
+
+ $obj = json_decode(self::$client->getResponse()->getContent());
+ //test response content
+ //$this->assertEquals($put_name, $obj->name, 'name did not change after PUT');
+ //$this->assertEquals($put_description, $obj->description, 'description did not change after PUT');
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/post.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/post.php.twig
new file mode 100644
index 0000000..f87aa36
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/post.php.twig
@@ -0,0 +1,7 @@
+ public function testPost()
+ {
+ $json = json_encode(self::$sample_object);
+ $crawler = self::$client->request('POST', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}.json', array(), array(), ['HTTP_CONTENT_TYPE' => 'application/json', 'CONTENT_TYPE' => 'application/json'], $json);
+ $this->assertEquals(201, self::$client->getResponse()->getStatusCode(), "Expected a 201 response status after posting to /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}.json with content, but got ".self::$client->getResponse()->getStatusCode());
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/put.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/put.php.twig
new file mode 100644
index 0000000..39a8c9b
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/put.php.twig
@@ -0,0 +1,13 @@
+ public function testPut()
+ {
+ $json = json_encode(self::$sample_object);
+
+ $crawler = self::$client->request('PUT', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json', array(), array(), ['HTTP_CONTENT_TYPE' => 'application/json', 'CONTENT_TYPE' => 'application/json'], $json);
+ $this->assertEquals(200, self::$client->getResponse()->getStatusCode(), 'Expected a 200 response status after requesting PUT /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json, got '.self::$client->getResponse()->getStatusCode());
+
+ $obj = json_decode(self::$client->getResponse()->getContent());
+ //test response content
+ //$this->assertEquals($put_name, $obj->name, 'name did not change after PUT');
+ //$this->assertEquals($put_description, $obj->description, 'description did not change after PUT');
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/setup.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/setup.php.twig
new file mode 100644
index 0000000..373e5bf
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/no-authentication/setup.php.twig
@@ -0,0 +1,86 @@
+/** @var Client */
+private static $client;
+
+private static $id;
+
+private static $sample_object;
+
+public function setUp()
+{
+parent::setUp();
+self::$client = static::createClient();
+//$em = self::$client->getContainer()->get('doctrine');
+
+//create assocation fields (relations from this entity)
+{%- for key, assoc in assoc_mapping %}
+ {%- if (assoc['type'] == 4 or assoc['type'] == 8) and assoc['targetEntity'] is defined and assoc['targetEntity'] is not null %} {# type 4 == OneToMany, type 8 == ManyToMany #}
+
+ $assoc_{{ key }} = array();
+ $field_{{ assoc['fieldName']|capitalize }} = new \{{ assoc['targetEntity'] }};
+ //set fields of $field_{{ assoc['fieldName']|capitalize }}.
+ $assoc_{{ key }}[] = $field_{{ assoc['fieldName']|capitalize }};
+
+
+ {%- elseif (assoc['type'] == 1 or assoc['type'] == 2) and assoc['targetEntity'] is defined and assoc['targetEntity'] is not null %} {# type 1 == OneToOne, type 2 == ManyToOne #}
+
+ $assoc_{{ key }} = new \{{ assoc['targetEntity'] }};
+
+
+ {%- endif %}
+{%- endfor %}
+
+//create main object
+${{ entity_class|lower }}_object = array();
+{%- for field, info in fields if info['id'] is not defined or info['id'] == false -%}
+ {%- if info['type'] == 'string' %}
+
+ ${{ entity_class|lower }}_object['{{ field }}'] = 'test_{{ field }}';
+
+ {%- elseif info['type'] == 'text' %}
+
+ ${{ entity_class|lower }}_object['{{ field }}'] = 'test_{{ field }}_'.substr(str_shuffle(md5(time())),0,255);
+
+ {%- elseif info['type'] == 'integer' or info['type'] == 'smallint' or info['type'] == 'bigint' %}
+
+ ${{ entity_class|lower }}_object['{{ field }}'] = 1;
+
+ {%- elseif info['type'] == 'decimal' or info['type'] == 'float' %}
+
+ ${{ entity_class|lower }}_object['{{ field }}'] = 1.11;
+
+ {%- elseif info['type'] == 'boolean' %}
+
+ ${{ entity_class|lower }}_object['{{ field }}'] = true;
+
+ {%- elseif info['type'] == 'date' or info['type'] == 'datetime' or info['type'] == 'datetimetz' or info['type'] == 'time' %}
+
+ ${{ entity_class|lower }}_object['{{ field }}'] = new \DateTime('now');
+
+ {%- elseif info['type'] == 'dateinterval' %}
+
+ ${{ entity_class|lower }}_object['{{ field }}'] = new \DateInterval('P2Y4DT6H8M');
+
+ {%- elseif info['type'] == 'array' or info['type'] == 'simple_array' or info['type'] == 'json_array' %}
+
+ ${{ entity_class|lower }}_object['{{ field }}'] = array('{{ field }}_1','{{ field }}_2');
+
+ {%- else %}
+
+ ${{ entity_class|lower }}_object['{{ field }}'] = 'unknown_field_value';
+
+ {%- endif -%}
+{%- endfor %}
+
+
+//add associations to mapping
+{%- for key, assoc in assoc_mapping %}
+ {%- if (assoc['type'] == 4 or assoc['type'] == 2) and assoc['targetEntity'] is defined and assoc['targetEntity'] is not null %} {# type 2 == ManyToOne, type 4 == OneToMany #}
+
+ ${{ entity_class|lower }}_object['{{ assoc['fieldName'] }}'] = $assoc_{{ key }};
+
+ {%- endif %}
+{%- endfor %}
+
+self::$sample_object = ${{ entity_class|lower }}_object;
+}
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/delete.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/delete.php.twig
new file mode 100644
index 0000000..6142af9
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/delete.php.twig
@@ -0,0 +1,12 @@
+ public function testDelete()
+ {
+ $crawler = self::$client->request('DELETE', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json');
+ $this->assertEquals(401, self::$client->getResponse()->getStatusCode(), 'Expected a 401 response status after requesting DELETE /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json without access token, but got '.self::$client->getResponse()->getStatusCode()."\r\nResponse:\r\n".self::$client->getResponse()->getContent());
+
+ $crawler = self::$client->request('DELETE', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/999.json?access_token='.self::$access_token);
+ $this->assertEquals(404, self::$client->getResponse()->getStatusCode(), 'Expected a 404 response status after requesting DELETE /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/999.json, got '.self::$client->getResponse()->getStatusCode()."\r\nResponse:\r\n".self::$client->getResponse()->getContent());
+
+ $crawler = self::$client->request('DELETE', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json?access_token='.self::$access_token);
+ $this->assertEquals(204, self::$client->getResponse()->getStatusCode(), 'Expected a 204 response status after requesting DELETE /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json, got '.self::$client->getResponse()->getStatusCode()."\r\nResponse:\r\n".self::$client->getResponse()->getContent());
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/get.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/get.php.twig
new file mode 100644
index 0000000..ed5239f
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/get.php.twig
@@ -0,0 +1,12 @@
+ public function testGet()
+ {
+ $crawler = self::$client->request('GET', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json');
+ $this->assertEquals(401, self::$client->getResponse()->getStatusCode(), 'Expected a 401 response status after requesting /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json without access token, but got '.self::$client->getResponse()->getStatusCode()."\r\nResponse:\r\n".self::$client->getResponse()->getContent());
+
+ $crawler = self::$client->request('GET', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/999.json?access_token='.self::$access_token);
+ $this->assertEquals(404, self::$client->getResponse()->getStatusCode(), 'Expected a 404 response status after requesting /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/999.json, got '.self::$client->getResponse()->getStatusCode()."\r\nResponse:\r\n".self::$client->getResponse()->getContent());
+
+ $crawler = self::$client->request('GET', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json?access_token='.self::$access_token);
+ $this->assertEquals(200, self::$client->getResponse()->getStatusCode(), 'Expected a 200 response status after requesting /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json, got '.self::$client->getResponse()->getStatusCode()."\r\nResponse:\r\n".self::$client->getResponse()->getContent());
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/getAfterDelete.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/getAfterDelete.php.twig
new file mode 100644
index 0000000..81cce64
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/getAfterDelete.php.twig
@@ -0,0 +1,6 @@
+ public function testGetOnDeletedItem()
+ {
+ $crawler = self::$client->request('GET', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json?access_token='.self::$access_token);
+ $this->assertEquals(404, self::$client->getResponse()->getStatusCode(), 'Expected a 404 response status after requesting /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json, got '.self::$client->getResponse()->getStatusCode()."\r\nResponse:\r\n".self::$client->getResponse()->getContent());
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/getAll.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/getAll.php.twig
new file mode 100644
index 0000000..91eb314
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/getAll.php.twig
@@ -0,0 +1,16 @@
+ public function testGetAllWithContent()
+ {
+ $crawler = self::$client->request('GET', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}.json');
+ $this->assertEquals(401, self::$client->getResponse()->getStatusCode(), 'Expected a 401 response status after requesting /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}.json without access token, but got '.self::$client->getResponse()->getStatusCode()."\r\nResponse:\r\n".self::$client->getResponse()->getContent());
+
+ $crawler = self::$client->request('GET', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}.json?access_token='.self::$access_token);
+ $this->assertEquals(200, self::$client->getResponse()->getStatusCode(), 'Expected a 200 response status after requesting /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}.json, got '.self::$client->getResponse()->getStatusCode()."\r\nResponse:\r\n".self::$client->getResponse()->getContent());
+
+ $obj = json_decode(self::$client->getResponse()->getContent());
+ $this->assertTrue(is_array($obj), 'expected decoded response content to be array, got '.gettype($obj));
+ $this->assertTrue(count($obj) > 0, 'expected decoded response content to be an array with more then 0 items, got '.count($obj));
+ $first = reset($obj);
+ $this->assertTrue(array_key_exists('id', $first), 'expected first array item to have a key id');
+ self::$id = $first->id;
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/getAllWithoutContent.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/getAllWithoutContent.php.twig
new file mode 100644
index 0000000..aaa5e31
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/getAllWithoutContent.php.twig
@@ -0,0 +1,9 @@
+ public function testGetAllWithoutContent()
+ {
+ $crawler = self::$client->request('GET', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}.json');
+ $this->assertEquals(401, self::$client->getResponse()->getStatusCode(), 'Expected a 401 response status after requesting /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}.json without access token, but got '.self::$client->getResponse()->getStatusCode()."\r\nResponse:\r\n".self::$client->getResponse()->getContent());
+
+ $crawler = self::$client->request('GET', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}.json?access_token='.self::$access_token);
+ $this->assertEquals(204, self::$client->getResponse()->getStatusCode(), 'Expected a 204 response status after requesting /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}.json, got '.self::$client->getResponse()->getStatusCode()."\r\nResponse:\r\n".self::$client->getResponse()->getContent());
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/patch.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/patch.php.twig
new file mode 100644
index 0000000..4d58134
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/patch.php.twig
@@ -0,0 +1,16 @@
+ public function testPatch()
+ {
+ $crawler = self::$client->request('PATCH', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json');
+ $this->assertEquals(401, self::$client->getResponse()->getStatusCode(), 'Expected a 401 response status after requesting PATCH /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json without access token, but got '.self::$client->getResponse()->getStatusCode()."\r\nResponse:\r\n".self::$client->getResponse()->getContent());
+
+ $crawler = self::$client->request('PATCH', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/999.json?access_token='.self::$access_token);
+ $this->assertEquals(404, self::$client->getResponse()->getStatusCode(), 'Expected a 404 response status after requesting PATCH /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/999.json, got '.self::$client->getResponse()->getStatusCode()."\r\nResponse:\r\n".self::$client->getResponse()->getContent());
+
+ $json = json_encode(self::$sample_object);
+
+ $crawler = self::$client->request('PATCH', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json?access_token='.self::$access_token, array(), array(), ['HTTP_CONTENT_TYPE' => 'application/json', 'CONTENT_TYPE' => 'application/json'], $json);
+ $this->assertEquals(200, self::$client->getResponse()->getStatusCode(), 'Expected a 200 response status after requesting PATCH /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json, got '.self::$client->getResponse()->getStatusCode()."\r\nResponse:\r\n".self::$client->getResponse()->getContent());
+
+ $obj = json_decode(self::$client->getResponse()->getContent());
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/post.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/post.php.twig
new file mode 100644
index 0000000..8c7b81b
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/post.php.twig
@@ -0,0 +1,14 @@
+ public function testPost()
+ {
+ $crawler = self::$client->request('POST', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}.json');
+ $this->assertEquals(401, self::$client->getResponse()->getStatusCode(), 'Expected a 401 response status after posting to api/post.json without access token, but got '.self::$client->getResponse()->getStatusCode()."\r\nResponse:\r\n".self::$client->getResponse()->getContent());
+
+ $extra_help = 'If this test fails with a 500 status code, make sure the properties in the entity that should be required are annotated with @Symfony\Component\Validator\Constraints\NotBlank (or @Constraints\NotBlank and use Symfony\Component\Validator\Constraints;), remove this test if it doesn\'t apply to u.';
+ $crawler = self::$client->request('POST', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}.json?access_token='.self::$access_token);
+ $this->assertEquals(400, self::$client->getResponse()->getStatusCode(), "Expected a 400 response status after posting to /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}.json without content, but got ".self::$client->getResponse()->getStatusCode().".\r\n".$extra_help."\r\n\r\nResponse:\r\n".self::$client->getResponse()->getContent());
+
+ $json = json_encode(self::$sample_object);
+ $crawler = self::$client->request('POST', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}.json?access_token='.self::$access_token, array(), array(), ['HTTP_CONTENT_TYPE' => 'application/json', 'CONTENT_TYPE' => 'application/json'], $json);
+ $this->assertEquals(201, self::$client->getResponse()->getStatusCode(), "Expected a 201 response status after posting to /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}.json with content, but got ".self::$client->getResponse()->getStatusCode()."\r\nResponse:\r\n".self::$client->getResponse()->getContent());
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/put.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/put.php.twig
new file mode 100644
index 0000000..6534038
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/put.php.twig
@@ -0,0 +1,16 @@
+ public function testPut()
+ {
+ $crawler = self::$client->request('PUT', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json');
+ $this->assertEquals(401, self::$client->getResponse()->getStatusCode(), 'Expected a 401 response status after requesting PUT /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json without access token, but got '.self::$client->getResponse()->getStatusCode()."\r\nResponse:\r\n".self::$client->getResponse()->getContent());
+
+ $crawler = self::$client->request('PUT', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/999.json?access_token='.self::$access_token);
+ $this->assertEquals(400, self::$client->getResponse()->getStatusCode(), 'Expected a 400 response status after requesting PUT /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/999.json, got '.self::$client->getResponse()->getStatusCode()."\r\nResponse:\r\n".self::$client->getResponse()->getContent());
+
+ $json = json_encode(self::$sample_object);
+
+ $crawler = self::$client->request('PUT', '/{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json?access_token='.self::$access_token, array(), array(), ['HTTP_CONTENT_TYPE' => 'application/json', 'CONTENT_TYPE' => 'application/json'], $json);
+ $this->assertEquals(200, self::$client->getResponse()->getStatusCode(), 'Expected a 200 response status after requesting PUT /{{ route_prefix }}/{{ entity|lower|replace({"\\":'/'}) }}/'.self::$id.'.json, got '.self::$client->getResponse()->getStatusCode()."\r\nResponse:\r\n".self::$client->getResponse()->getContent());
+
+ $obj = json_decode(self::$client->getResponse()->getContent());
+ }
+
diff --git a/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/setup.php.twig b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/setup.php.twig
new file mode 100644
index 0000000..176a5b2
--- /dev/null
+++ b/src/Voryx/RESTGeneratorBundle/Resources/skeleton/rest/tests/oauth2/setup.php.twig
@@ -0,0 +1,206 @@
+ /** @var Client */
+ private static $client;
+
+ private static $id;
+
+ private static $sample_object;
+
+ public function setUp()
+ {
+ parent::setUp();
+ self::$client = static::createClient();
+ //$em = self::$client->getContainer()->get('doctrine');
+
+ //create assocation fields (relations from this entity)
+ {%- for key, assoc in assoc_mapping %}
+ {%- if (assoc['type'] == 4 or assoc['type'] == 8) and assoc['targetEntity'] is defined and assoc['targetEntity'] is not null %} {# type 4 == OneToMany, type 8 == ManyToMany #}
+
+ $assoc_{{ key }} = array();
+ $field_{{ assoc['fieldName']|capitalize }} = new \{{ assoc['targetEntity'] }};
+ //set fields of $field_{{ assoc['fieldName']|capitalize }}.
+ $assoc_{{ key }}[] = $field_{{ assoc['fieldName']|capitalize }};
+
+
+ {%- elseif (assoc['type'] == 1 or assoc['type'] == 2) and assoc['targetEntity'] is defined and assoc['targetEntity'] is not null %} {# type 1 == OneToOne, type 2 == ManyToOne #}
+
+ $assoc_{{ key }} = new \{{ assoc['targetEntity'] }};
+
+
+ {%- endif %}
+ {%- endfor %}
+
+ //create main object
+ ${{ entity_class|lower }}_object = array();
+ {%- for field, info in fields if info['id'] is not defined or info['id'] == false -%}
+ {%- if entity_constraints[field] is defined and entity_constraints[field]|length > 0 %}
+ {# Check basic constraints #}
+ {%- for constraint in entity_constraints[field] %}
+ {%- if 'NotBlankValidator' in constraint.validatedBy() %}
+ {# do nothing, value will be set on type checking #}
+ {%- elseif 'BlankValidator' in constraint.validatedBy() %}
+ {%- set test_data = null %}
+ {%- elseif 'NotNullValidator' in constraint.validatedBy() %}
+ {# do nothing #}
+ {%- elseif 'IsNullValidator' in constraint.validatedBy() %}
+ {%- set test_data = null %}
+ {%- endif %}
+ {%- endfor %}
+ {% endif %}
+
+ {%- if info['type'] == 'string' %}
+ {%- set test_data = 'test_' ~ field %}
+ {%- if entity_constraints[field] is defined and entity_constraints[field]|length > 0 %}
+ {%- for constraint in entity_constraints[field] %}
+ {%- if 'NotBlankValidator' in constraint.validatedBy() %}
+ {%- set test_data = 'test_required_field_' ~ field %}
+ {%- elseif 'EmailValidator' in constraint.validatedBy() %}
+ {%- set test_data = field ~ '@domain.com' %}
+ {%- elseif 'UrlValidator' in constraint.validatedBy() %}
+ {%- set test_data = 'http://www.' ~ field ~ '.com' %}
+ {%- elseif 'IpValidator' in constraint.validatedBy() %}
+ {%- set test_data = '192.168.1.1' %}
+ {%- elseif 'RegexValidator' in constraint.validatedBy() %}
+ {%- set test_data = 'RegexValidator_test_data' %}
+ {%- elseif 'IsTrueValidator' in constraint.validatedBy() %}
+ {%- set test_data = '1' %}
+ {%- elseif 'IsFalseValidator' in constraint.validatedBy() %}
+ {%- set test_data = '0' %}
+ {%- elseif 'BicValidator' in constraint.validatedBy() %}
+ {%- set test_data = 'RABONL2U' %}
+ {%- elseif 'IbanValidator' in constraint.validatedBy() %}
+ {%- set test_data = 'NL10RABO1234556789' %}
+ {%- elseif 'CountryValidator' in constraint.validatedBy() %}
+ {%- set test_data = 'NL' %}
+ {%- elseif 'LocaleValidator' in constraint.validatedBy() %}
+ {%- set test_data = 'nl_NL' %}
+ {%- elseif 'LanguageValidator' in constraint.validatedBy() %}
+ {%- set test_data = 'nl' %}
+ {%- elseif 'CurrencyValidator' in constraint.validatedBy() %}
+ {%- set test_data = 'EUR' %}
+ {%- elseif 'LengthValidator' in constraint.validatedBy() %}
+ {% set random_number = random(constraint.max - constraint.min) + constraint.min %}
+ {% set test_data = '' %}
+ {% for i in 1..random_number %}
+ {% set test_data = test_data ~ random('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890') %}
+ {% endfor %}
+ {%- elseif 'UuidValidator' in constraint.validatedBy() %}
+ {#- might want to check uuid versions and strict mode? #}
+ {%- set test_data = '123e4567-e89b-12d3-a456-426655440000' %}
+ {%- endif %}
+ {%- endfor %}
+ {%- endif %}
+
+ ${{ entity_class|lower }}_object['{{ field }}'] = '{{ test_data }}';
+
+ {%- elseif info['type'] == 'text' %}
+
+ ${{ entity_class|lower }}_object['{{ field }}'] = 'test_{{ field }}_'.substr(str_shuffle(md5(time())),0,255);
+
+ {%- elseif info['type'] == 'integer' or info['type'] == 'smallint' or info['type'] == 'bigint' %}
+ {%- set test_data = 1 %}
+ {%- if entity_constraints[field] is defined and entity_constraints[field]|length > 0 %}
+ {%- for constraint in entity_constraints[field] %}
+ {%- if 'RangeValidator' in constraint.validatedBy() %}
+ {%- set test_data = random(constraint.max - constraint.min) + constraint.min %}
+ {%- elseif 'IsTrueValidator' in constraint.validatedBy() %}
+ {%- set test_data = 1 %}
+ {%- elseif 'IsFalseValidator' in constraint.validatedBy() %}
+ {%- set test_data = 0 %}
+ {%- endif %}
+ {%- endfor %}
+ {% endif %}
+
+ ${{ entity_class|lower }}_object['{{ field }}'] = {{ test_data }};
+
+ {%- elseif info['type'] == 'decimal' or info['type'] == 'float' %}
+ {%- set test_data = 1.11 %}
+ {%- if entity_constraints[field] is defined and entity_constraints[field]|length > 0 %}
+ {%- for constraint in entity_constraints[field] %}
+ {%- if 'RangeValidator' in constraint.validatedBy() %}
+ {%- set test_data = random(constraint.max - constraint.min) + constraint.min %}
+ {%- elseif 'IsTrueValidator' in constraint.validatedBy() %}
+ {%- set test_data = 1 %}
+ {%- elseif 'IsFalseValidator' in constraint.validatedBy() %}
+ {%- set test_data = 0 %}
+ {%- endif %}
+ {%- endfor %}
+ {% endif %}
+
+ ${{ entity_class|lower }}_object['{{ field }}'] = {{ test_data }};
+
+ {%- elseif info['type'] == 'boolean' %}
+ {%- set test_data = true %}
+ {%- if entity_constraints[field] is defined and entity_constraints[field]|length > 0 %}
+ {%- for constraint in entity_constraints[field] %}
+ {%- if 'RangeValidator' in constraint.validatedBy() %}
+ {%- set test_data = random(constraint.max - constraint.min) + constraint.min %}
+ {%- elseif 'IsFalseValidator' in constraint.validatedBy() %}
+ {%- set test_data = false %}
+ {%- endif %}
+ {%- endfor %}
+ {% endif %}
+
+ ${{ entity_class|lower }}_object['{{ field }}'] = {{ test_data }};
+
+ {%- elseif info['type'] == 'date' or info['type'] == 'datetime' or info['type'] == 'datetimetz' or info['type'] == 'time' %}
+ {%- set test_data = 'now' %}
+ {%- if entity_constraints[field] is defined and entity_constraints[field]|length > 0 %}
+ {%- for constraint in entity_constraints[field] %}
+ {%- if 'RangeValidator' in constraint.validatedBy() %}
+ {%- set test_data = constraint.min %}
+ {%- endif %}
+ {%- endfor %}
+ {% endif %}
+
+ ${{ entity_class|lower }}_object['{{ field }}'] = new \DateTime('{{ test_data }}');
+
+ {%- elseif info['type'] == 'dateinterval' %}
+
+ ${{ entity_class|lower }}_object['{{ field }}'] = new \DateInterval('P2Y4DT6H8M');
+
+ {%- elseif info['type'] == 'array' or info['type'] == 'simple_array' or info['type'] == 'json_array' %}
+ {%- set plain_test_data = 'array(' %}
+ {%- set test_data = [ field ~ '_1' ] %}
+ {%- if entity_constraints[field] is defined and entity_constraints[field]|length > 0 %}
+ {%- for constraint in entity_constraints[field] %}
+ {# the following 2 validators are incompatible at the moment #}
+ {%- if 'ChoiceValidator' in constraint.validatedBy() %}
+ {% if constraint.choices is defined and constraint.choices|length > 0 %}
+ {%- set test_data = constraint.choices %}
+ {% else %}
+ {# check the callback here #}
+ {% endif %}
+ {%- elseif 'CountValidator' in constraint.validatedBy() %}
+ {% for i in 1..constraint.min%}
+ {{ test_data|merge([ field ~ '_' ~ i+1 ]) }}
+ {% endfor %}
+ {%- endif %}
+ {%- endfor %}
+ {%- endif %}
+ {%- for array_item in test_data %}
+ {%- set plain_test_data = plain_test_data ~ "'" ~ array_item ~ "'," %}
+ {%- endfor %}
+ {%- set plain_test_data = plain_test_data|trim(',') ~ ')'%}
+
+ ${{ entity_class|lower }}_object['{{ field }}'] = {{ plain_test_data }};
+
+ {%- else %}
+
+ ${{ entity_class|lower }}_object['{{ field }}'] = 'unknown_field_value';
+
+ {%- endif -%}
+ {%- endfor %}
+
+
+ //add associations to mapping
+ {%- for key, assoc in assoc_mapping %}
+ {%- if (assoc['type'] == 4 or assoc['type'] == 2) and assoc['targetEntity'] is defined and assoc['targetEntity'] is not null %} {# type 2 == ManyToOne, type 4 == OneToMany #}
+
+ ${{ entity_class|lower }}_object['{{ assoc['fieldName'] }}'] = $assoc_{{ key }};
+
+ {%- endif %}
+ {%- endfor %}
+
+ self::$sample_object = ${{ entity_class|lower }}_object;
+ }
+