Implement validation rule for settings

This commit is contained in:
David Bomba 2019-10-10 12:01:38 +11:00
parent 7a4decdd3d
commit 3123547e55
6 changed files with 104 additions and 11 deletions

View File

@ -105,7 +105,7 @@ class CompanySettings extends BaseSettings
public $shared_invoice_quote_counter = false; public $shared_invoice_quote_counter = false;
public $recurring_invoice_number_prefix = 'R'; public $recurring_invoice_number_prefix = 'R';
public $reset_counter_frequency_id = ''; public $reset_counter_frequency_id = '0';
public $reset_counter_date = ''; public $reset_counter_date = '';
public $counter_padding = 0; public $counter_padding = 0;

View File

@ -18,9 +18,10 @@ use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundExceptio
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\Exceptions\ThrottleRequestsException; use Illuminate\Http\Exceptions\ThrottleRequestsException;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Validation\ValidationException;
use Symfony\Component\Debug\Exception\FatalThrowableError; use Symfony\Component\Debug\Exception\FatalThrowableError;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class Handler extends ExceptionHandler class Handler extends ExceptionHandler
{ {
@ -100,6 +101,9 @@ class Handler extends ExceptionHandler
else if($exception instanceof MethodNotAllowedHttpException){ else if($exception instanceof MethodNotAllowedHttpException){
return response()->json(['message'=>'Method not support for this route'],404); return response()->json(['message'=>'Method not support for this route'],404);
} }
else if ($exception instanceof ValidationException) {
return response()->json(['message' => 'The given data was invalid.', 'errors' => $exception->validator->getMessageBag()], 422);
}
return parent::render($request, $exception); return parent::render($request, $exception);

View File

@ -12,6 +12,7 @@
namespace App\Http\Requests\Company; namespace App\Http\Requests\Company;
use App\Http\Requests\Request; use App\Http\Requests\Request;
use App\Http\ValidationRules\ValidSettingsRule;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Rule; use Illuminate\Validation\Rule;
@ -40,7 +41,7 @@ class UpdateCompanyRequest extends Request
'size_id' => 'integer|nullable', 'size_id' => 'integer|nullable',
'country_id' => 'integer|nullable', 'country_id' => 'integer|nullable',
'work_email' => 'email|nullable', 'work_email' => 'email|nullable',
// 'settings' => 'json', 'settings' => new ValidSettingsRule(),
]; ];
} }

View File

@ -0,0 +1,59 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2019. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\ValidationRules;
use App\Libraries\MultiDB;
use App\Models\User;
use App\Utils\Traits\CompanySettingsSaver;
use Illuminate\Contracts\Validation\Rule;
/**
* Class ValidSettingsRule
* @package App\Http\ValidationRules
*/
class ValidSettingsRule implements Rule
{
use CompanySettingsSaver;
/**
* @param string $attribute
* @param mixed $value
* @return bool
*/
public $return_data;
public function passes($attribute, $value)
{
$data = $this->validateSettings($value);
if (is_array($data))
{
$this->return_data = $data;
return false;
}
else
return true;
}
/**
* @return string
*/
public function message()
{
return $this->return_data[0]." is not a valid ".$this->return_data[1];
}
}

View File

@ -32,21 +32,48 @@ trait CompanySettingsSaver
foreach(CompanySettings::$protected_fields as $field) foreach(CompanySettings::$protected_fields as $field)
unset($settings[$field]); unset($settings[$field]);
//make sure the inbound settings have the correct casts!
//$settings = CompanySettings::setCasts($settings, CompanySettings::$casts);
$settings = $this->checkSettingType($settings, CompanySettings::$casts); $settings = $this->checkSettingType($settings, CompanySettings::$casts);
//iterate through set properties with new values; //iterate through set properties with new values;
foreach($settings as $key => $value) foreach($settings as $key => $value)
$company_settings->{$key} = $value; $company_settings->{$key} = $value;
//$company_settings = CompanySettings::setCasts($company_settings, CompanySettings::$casts);
$this->settings = $company_settings; $this->settings = $company_settings;
$this->save(); $this->save();
} }
public function validateSettings($settings)
{
$settings = (object)$settings;
$casts = CompanySettings::$casts;
foreach ($casts as $key => $value){
/*Separate loop if it is a _id field which is an integer cast as a string*/
if(substr($key, -3) == '_id' || substr($key, -14) == 'number_counter'){
$value = "integer";
if(!$this->checkAttribute($value, $settings->{$key})){
return [$key, $value];
}
continue;
}
/* Handles unset settings or blank strings */
if(is_null($settings->{$key}) || !isset($settings->{$key}) || $settings->{$key} == ''){
continue;
}
/*Catch all filter */
if(!$this->checkAttribute($value, $settings->{$key})){
return [$key, $value];
}
}
return true;
}
private function checkSettingType($settings, $casts) private function checkSettingType($settings, $casts)
{ {
@ -55,7 +82,7 @@ trait CompanySettingsSaver
foreach ($casts as $key => $value){ foreach ($casts as $key => $value){
/*Separate loop if it is a _id field which is an integer cast as a string*/ /*Separate loop if it is a _id field which is an integer cast as a string*/
if(substr($key, -3) == '_id' || substr($key, -8) == '_counter'){ if(substr($key, -3) == '_id' || substr($key, -14) == 'number_counter'){
$value = "integer"; $value = "integer";
if($this->checkAttribute($value, $settings->{$key})){ if($this->checkAttribute($value, $settings->{$key})){
@ -101,7 +128,6 @@ trait CompanySettingsSaver
return method_exists($value, '__toString' ) || is_null($value) || is_string($value); return method_exists($value, '__toString' ) || is_null($value) || is_string($value);
case 'bool': case 'bool':
case 'boolean': case 'boolean':
//\Log::error("is {$value} boolean ? = ".is_bool($value) || (int) filter_var($value, FILTER_VALIDATE_BOOLEAN));
return is_bool($value) || (int) filter_var($value, FILTER_VALIDATE_BOOLEAN); return is_bool($value) || (int) filter_var($value, FILTER_VALIDATE_BOOLEAN);
case 'object': case 'object':
return is_object($value); return is_object($value);
@ -117,5 +143,6 @@ trait CompanySettingsSaver
// \Log::error('popping '.$key.' '.$value.' '.$settings->{$key}.' off the stack'); // \Log::error('popping '.$key.' '.$value.' '.$settings->{$key}.' off the stack');
// \Log::error('popping '.$key.' '.$value.' '.$settings->{$key}.' off the stack'); // \Log::error('popping '.$key.' '.$value.' '.$settings->{$key}.' off the stack');
// \Log::error("integer testing {$key} - {$value} - ".$settings->{$key});
} }

View File

@ -82,9 +82,11 @@ class CompanySettingsTest extends TestCase
'X-API-Token' => $this->token, 'X-API-Token' => $this->token,
])->put('/api/v1/companies/'.$this->encodePrimaryKey($this->company->id), $this->company->toArray()); ])->put('/api/v1/companies/'.$this->encodePrimaryKey($this->company->id), $this->company->toArray());
$arr = $response->json();
\Log::error(print_r($arr,1));
$response->assertStatus(200); $response->assertStatus(200);
$arr = $response->json();
$this->assertEquals($arr['data']['settings']['client_number_counter'],1); $this->assertEquals($arr['data']['settings']['client_number_counter'],1);
$this->assertEquals($arr['data']['settings']['quote_number_counter'],1); $this->assertEquals($arr['data']['settings']['quote_number_counter'],1);