Skip to content

Commit

Permalink
Input validation is now supported as strict JSON validation
Browse files Browse the repository at this point in the history
  • Loading branch information
Nael Rashdeen committed Sep 28, 2017
1 parent 66f3158 commit eaf1a30
Show file tree
Hide file tree
Showing 10 changed files with 642 additions and 329 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ public class SymfonyServerCodegen extends AbstractPhpCodegen implements CodegenC
protected String bundleExtensionName;
protected String bundleAlias;
protected String controllerDirName = "Controller";
protected String serviceDirName = "Service";
protected String controllerPackage;
protected String servicePackage;
protected Boolean phpLegacySupport = Boolean.TRUE;

protected HashSet<String> typeHintable;
Expand Down Expand Up @@ -239,6 +241,7 @@ public void processOpts() {

additionalProperties.put("escapedInvokerPackage", invokerPackage.replace("\\", "\\\\"));
additionalProperties.put("controllerPackage", controllerPackage);
additionalProperties.put("servicePackage", servicePackage);
additionalProperties.put("apiTestsPackage", apiTestsPackage);
additionalProperties.put("modelTestsPackage", modelTestsPackage);

Expand Down Expand Up @@ -274,6 +277,8 @@ public void processOpts() {
supportingFiles.add(new SupportingFile("ApiServer.mustache", toPackagePath(apiPackage, srcBasePath), "ApiServer.php"));
supportingFiles.add(new SupportingFile("ModelSerializer.mustache", toPackagePath(modelPackage, srcBasePath), "ModelSerializer.php"));
supportingFiles.add(new SupportingFile("ModelInterface.mustache", toPackagePath(modelPackage, srcBasePath), "ModelInterface.php"));
supportingFiles.add(new SupportingFile("StrictJsonDeserializationVisitor.mustache", toPackagePath(servicePackage, srcBasePath), "StrictJsonDeserializationVisitor.php"));
supportingFiles.add(new SupportingFile("TypeMismatchException.mustache", toPackagePath(servicePackage, srcBasePath), "TypeMismatchException.php"));
supportingFiles.add(new SupportingFile("routing.mustache", configDir, "routing.yml"));
supportingFiles.add(new SupportingFile("services.mustache", configDir, "services.yml"));
supportingFiles.add(new SupportingFile("composer.mustache", getPackagePath(), "composer.json"));
Expand Down Expand Up @@ -419,6 +424,7 @@ public void setInvokerPackage(String invokerPackage) {
apiTestsPackage = testsPackage + "\\" + apiDirName;
modelTestsPackage = testsPackage + "\\" + modelDirName;
controllerPackage = invokerPackage + "\\" + controllerDirName;
servicePackage = invokerPackage + "\\" + serviceDirName;
}

@Override
Expand Down Expand Up @@ -479,6 +485,15 @@ public String toModelImport(String name) {
}
}

@Override
public String toEnumValue(String value, String datatype) {
if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) {
return value;
} else {
return "\"" + escapeText(value) + "\"";
}
}

public String toApiName(String name) {
if (name.isEmpty()) {
return "DefaultApiInterface";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ namespace {{controllerPackage}};
use Symfony\Bundle\FrameworkBundle\Controller\Controller as BaseController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;
use {{servicePackage}}\StrictJsonDeserializationVisitor;
use JMS\Serializer\SerializerBuilder;
use JMS\Serializer\Naming\CamelCaseNamingStrategy;
use JMS\Serializer\Naming\SerializedNameAnnotationStrategy;

/**
* Controller Class Doc Comment
Expand All @@ -33,7 +37,6 @@ use Symfony\Component\HttpKernel\Exception\HttpException;
*/
class Controller extends BaseController
{
/**
* This will return a response with code 400. Usage example:
* return $this->createBadRequestResponse('Unable to access this page!');
Expand Down Expand Up @@ -75,9 +78,9 @@ class Controller extends BaseController
*
* @return string A serialized data string.
*/
public function serialize($data, $format)
protected function serialize($data, $format)
{
return $this->get('{{bundleAlias}}.model.model_serializer')->serialize($data, $format);
return $this->get('jms_serializer')->serialize($data, $format);
}

/**
Expand All @@ -89,11 +92,26 @@ class Controller extends BaseController
*
* @return mixed A deserialized data.
*/
public function deserialize($data, $class, $format)
protected function deserialize($data, $class, $format)
{
return $this->get('{{bundleAlias}}.model.model_serializer')->deserialize($data, $class, $format);
$naming_strategy = new SerializedNameAnnotationStrategy(new CamelCaseNamingStrategy());
$serializer = SerializerBuilder::create()
->setDeserializationVisitor($format, new StrictJsonDeserializationVisitor($naming_strategy))
->build();
return $serializer->deserialize($data, $class, $format);
}

protected function validate($data, $asserts = null)
{
$errors = $this->get('validator')->validate($data, $asserts);
if (count($errors) > 0) {
$errorsString = (string)$errors;
return $this->createBadRequestResponse($errorsString);
}
}

/**
* Decodes a string value.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php
/*
* Copyright 2017 Dmitriy Simushev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace {{servicePackage}};

use JMS\Serializer\Context;
use JMS\Serializer\JsonDeserializationVisitor;

class StrictJsonDeserializationVisitor extends JsonDeserializationVisitor
{
/**
* {@inheritdoc}
*/
public function visitString($data, array $type, Context $context)
{
if (!is_string($data)) {
throw TypeMismatchException::fromValue('string', $data, $context);
}

return parent::visitString($data, $type, $context);
}

/**
* {@inheritdoc}
*/
public function visitBoolean($data, array $type, Context $context)
{
if (!is_bool($data)) {
throw TypeMismatchException::fromValue('boolean', $data, $context);
}

return parent::visitBoolean($data, $type, $context);
}

/**
* {@inheritdoc}
*/
public function visitInteger($data, array $type, Context $context)
{
if (!is_int($data)) {
throw TypeMismatchException::fromValue('integer', $data, $context);
}

return parent::visitInteger($data, $type, $context);
}

/**
* {@inheritdoc}
*/
public function visitDouble($data, array $type, Context $context)
{
if (!is_float($data) && !is_integer($data)) {
throw TypeMismatchException::fromValue('double', $data, $context);
}

return parent::visitDouble($data, $type, $context);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php
/*
* Copyright 2017 Dmitriy Simushev
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace {{servicePackage}};

use JMS\Serializer\DeserializationContext;
use JMS\Serializer\Exception\RuntimeException;

class TypeMismatchException extends RuntimeException
{
/**
* A handy method for building exception instance.
*
* @param string $expected_type
* @param mixed $actual_value
* @param DeserializationContext|null $context
* @return TypeMismatchException
*/
public static function fromValue(
$expected_type,
$actual_value,
DeserializationContext $context = null
) {
if (null !== $context && count($context->getCurrentPath()) > 0) {
$property = sprintf('property "%s" to be ', implode('.', $context->getCurrentPath()));
} else {
$property = '';
}

return new static(sprintf(
'Expected %s%s, but got %s: %s',
$property,
$expected_type,
gettype($actual_value),
json_encode($actual_value)
));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ class {{controllerName}} extends Controller
{{#bodyParams}}
// Handle body params
${{paramName}} = $this->deserialize($request->getContent(), '{{{dataType}}}', 'json');
$response = $this->validate(${{paramName}});
if ($response instanceof Response) {
return $response;
}
{{/bodyParams}}

// Parse incoming parameters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
"ext-curl": "*",
"ext-json": "*",
"ext-mbstring": "*",
"symfony/validator": "*",
"jms/serializer-bundle": "*",
"symfony/framework-bundle": "^2.3|^3.0"
},
"require-dev": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@

namespace {{modelPackage}};

use Symfony\Component\Validator\Constraints as Assert;
use JMS\Serializer\Annotation\Type;
use JMS\Serializer\Annotation\SerializedName;
{{^isEnum}}
{{#useStatements}}use {{this}};
{{/useStatements}}
Expand All @@ -36,5 +39,5 @@ namespace {{modelPackage}};
* @package {{modelPackage}}
* @author Swagger Codegen team
*/
{{#isEnum}}{{>model_enum}}{{/isEnum}}{{^isEnum}}{{>model_generic}}{{/isEnum}}
{{>model_generic}}
{{/model}}{{/models}}
Loading

0 comments on commit eaf1a30

Please sign in to comment.