Browse Source

refactor types validation #648

Maurits van der Schee 4 years ago
parent
commit
b455d66ca8

+ 42
- 45
README.md View File

923
 #### Validation types
923
 #### Validation types
924
 
924
 
925
 The default types validations return the following error messages:
925
 The default types validations return the following error messages:
926
-| error message | applies to types |
927
-| ---- | ---- |
928
-| cannot be null | any non-nullable column |
929
-| must be numeric | integer bigint |
930
-| exceeds range | integer bigint |
931
-| too long | varchar varbinary |
932
-| not a float | decimal float double |
933
-| not a valid boolean | boolean |
934
-| invalid date format use yyyy-mm-dd | date timestamp |
935
-| not a valid date | date timestamp |
936
-| invalid time format use hh:mm:ss | time timestamp |
937
-| non-numeric time value | time timestamp |
938
-| not a valid time | time timestamp |
939
-| invalid timestamp format use yyyy-mm-dd hh:mm:ss | timestamp |
940
-
941
-If you want the types validation to apply to all the types, you must activate the `validation` middleware.
926
+
927
+| error message       | reason                      | applies to types                            |
928
+| ------------------- | --------------------------- | ------------------------------------------- |
929
+| cannot be null      | unexpected null value       | (any non-nullable column)                   |
930
+| illegal whitespace  | leading/trailing whitespace | integer bigint decimal float double boolean |
931
+| invalid integer     | illegal characters          | integer bigint                              |
932
+| string too long     | too many characters         | varchar varbinary                           |
933
+| invalid decimal     | illegal characters          | decimal                                     |
934
+| decimal too large   | too many digits before dot  | decimal                                     |
935
+| decimal too precise | too many digits after dot   | decimal                                     |
936
+| invalid float       | illegal characters          | float double                                |
937
+| invalid boolean     | use 1, 0, true or false     | boolean                                     |
938
+| invalid date        | use yyyy-mm-dd              | date                                        |
939
+| invalid time        | use hh:mm:ss                | time                                        |
940
+| invalid timestamp   | use yyyy-mm-dd hh:mm:ss     | timestamp                                   |
941
+
942
+If you want the types validation to apply to all the types, you must activate the "`validation`" middleware.
942
 By default, all types are enabled. Which is equivalent to the two configuration possibilities:
943
 By default, all types are enabled. Which is equivalent to the two configuration possibilities:
943
 
944
 
944
     'validation.types' => 'all',
945
     'validation.types' => 'all',
945
     
946
     
946
 or
947
 or
947
-    
948
-    'validation.types'=> 'integer,bigint,varchar,decimal,float,double,boolean,date,time,timestamp,clob,blob,varbinary,geometry',
949
 
948
 
950
-Types with no declared error message can be checked whether null when the column is non-nullable.
949
+    'validation.types'=> 'integer,bigint,varchar,decimal,float,double,boolean,date,time,timestamp,clob,blob,varbinary,geometry',
951
 
950
 
952
-In case you want to use a validation handler but don't want any types validation, use either:
951
+In case you want to use a validation handler but don't want any types validation, use:
953
 
952
 
954
     'validation.types' => '',
953
     'validation.types' => '',
955
-    
956
-or
957
-    
958
-    'validation.types'=> 'none',
954
+
955
+NB: Types that are enabled will be checked for null values when the column is non-nullable.
959
 
956
 
960
 ### Multi-tenancy support
957
 ### Multi-tenancy support
961
 
958
 
1211
     [3/4] Starting SQLServer 2017 ... skipped
1208
     [3/4] Starting SQLServer 2017 ... skipped
1212
     [4/4] Cloning PHP-CRUD-API v2 ... skipped
1209
     [4/4] Cloning PHP-CRUD-API v2 ... skipped
1213
     ------------------------------------------------
1210
     ------------------------------------------------
1214
-    mysql: 104 tests ran in 2869 ms, 1 skipped, 0 failed
1215
-    pgsql: 104 tests ran in 850 ms, 1 skipped, 0 failed
1211
+    mysql: 105 tests ran in 2986 ms, 1 skipped, 0 failed
1212
+    pgsql: 105 tests ran in 976 ms, 1 skipped, 0 failed
1216
     sqlsrv: skipped, driver not loaded
1213
     sqlsrv: skipped, driver not loaded
1217
-    sqlite: 104 tests ran in 883 ms, 12 skipped, 0 failed
1214
+    sqlite: 105 tests ran in 933 ms, 12 skipped, 0 failed
1218
     ================================================
1215
     ================================================
1219
     Debian 10 (PHP 7.3)
1216
     Debian 10 (PHP 7.3)
1220
     ================================================
1217
     ================================================
1223
     [3/4] Starting SQLServer 2017 ... skipped
1220
     [3/4] Starting SQLServer 2017 ... skipped
1224
     [4/4] Cloning PHP-CRUD-API v2 ... skipped
1221
     [4/4] Cloning PHP-CRUD-API v2 ... skipped
1225
     ------------------------------------------------
1222
     ------------------------------------------------
1226
-    mysql: 104 tests ran in 3056 ms, 1 skipped, 0 failed
1227
-    pgsql: 104 tests ran in 869 ms, 1 skipped, 0 failed
1223
+    mysql: 105 tests ran in 3214 ms, 1 skipped, 0 failed
1224
+    pgsql: 105 tests ran in 904 ms, 1 skipped, 0 failed
1228
     sqlsrv: skipped, driver not loaded
1225
     sqlsrv: skipped, driver not loaded
1229
-    sqlite: 104 tests ran in 891 ms, 12 skipped, 0 failed
1226
+    sqlite: 105 tests ran in 1145 ms, 12 skipped, 0 failed
1230
     ================================================
1227
     ================================================
1231
     Debian 9 (PHP 7.0)
1228
     Debian 9 (PHP 7.0)
1232
     ================================================
1229
     ================================================
1235
     [3/4] Starting SQLServer 2017 ... skipped
1232
     [3/4] Starting SQLServer 2017 ... skipped
1236
     [4/4] Cloning PHP-CRUD-API v2 ... skipped
1233
     [4/4] Cloning PHP-CRUD-API v2 ... skipped
1237
     ------------------------------------------------
1234
     ------------------------------------------------
1238
-    mysql: 104 tests ran in 2800 ms, 1 skipped, 0 failed
1239
-    pgsql: 104 tests ran in 867 ms, 1 skipped, 0 failed
1235
+    mysql: 105 tests ran in 2940 ms, 1 skipped, 0 failed
1236
+    pgsql: 105 tests ran in 992 ms, 1 skipped, 0 failed
1240
     sqlsrv: skipped, driver not loaded
1237
     sqlsrv: skipped, driver not loaded
1241
-    sqlite: 104 tests ran in 1051 ms, 12 skipped, 0 failed
1238
+    sqlite: 105 tests ran in 1063 ms, 12 skipped, 0 failed
1242
     ================================================
1239
     ================================================
1243
     Ubuntu 16.04 (PHP 7.0)
1240
     Ubuntu 16.04 (PHP 7.0)
1244
     ================================================
1241
     ================================================
1247
     [3/4] Starting SQLServer 2017 ... done
1244
     [3/4] Starting SQLServer 2017 ... done
1248
     [4/4] Cloning PHP-CRUD-API v2 ... skipped
1245
     [4/4] Cloning PHP-CRUD-API v2 ... skipped
1249
     ------------------------------------------------
1246
     ------------------------------------------------
1250
-    mysql: 104 tests ran in 2789 ms, 1 skipped, 0 failed
1251
-    pgsql: 104 tests ran in 872 ms, 1 skipped, 0 failed
1252
-    sqlsrv: 104 tests ran in 7728 ms, 1 skipped, 0 failed
1247
+    mysql: 105 tests ran in 3015 ms, 1 skipped, 0 failed
1248
+    pgsql: 105 tests ran in 992 ms, 1 skipped, 0 failed
1249
+    sqlsrv: 105 tests ran in 10515 ms, 1 skipped, 0 failed
1253
     sqlite: skipped, driver not loaded
1250
     sqlite: skipped, driver not loaded
1254
     ================================================
1251
     ================================================
1255
     Ubuntu 18.04 (PHP 7.2)
1252
     Ubuntu 18.04 (PHP 7.2)
1259
     [3/4] Starting SQLServer 2017 ... skipped
1256
     [3/4] Starting SQLServer 2017 ... skipped
1260
     [4/4] Cloning PHP-CRUD-API v2 ... skipped
1257
     [4/4] Cloning PHP-CRUD-API v2 ... skipped
1261
     ------------------------------------------------
1258
     ------------------------------------------------
1262
-    mysql: 104 tests ran in 3282 ms, 1 skipped, 0 failed
1263
-    pgsql: 104 tests ran in 856 ms, 1 skipped, 0 failed
1259
+    mysql: 105 tests ran in 3390 ms, 1 skipped, 0 failed
1260
+    pgsql: 105 tests ran in 936 ms, 1 skipped, 0 failed
1264
     sqlsrv: skipped, driver not loaded
1261
     sqlsrv: skipped, driver not loaded
1265
-    sqlite: 104 tests ran in 972 ms, 12 skipped, 0 failed
1262
+    sqlite: 105 tests ran in 1063 ms, 12 skipped, 0 failed
1266
     ================================================
1263
     ================================================
1267
     Ubuntu 20.04 (PHP 7.3)
1264
     Ubuntu 20.04 (PHP 7.3)
1268
     ================================================
1265
     ================================================
1271
     [3/4] Starting SQLServer 2017 ... skipped
1268
     [3/4] Starting SQLServer 2017 ... skipped
1272
     [4/4] Cloning PHP-CRUD-API v2 ... skipped
1269
     [4/4] Cloning PHP-CRUD-API v2 ... skipped
1273
     ------------------------------------------------
1270
     ------------------------------------------------
1274
-    mysql: 104 tests ran in 5996 ms, 1 skipped, 0 failed
1275
-    pgsql: 104 tests ran in 942 ms, 1 skipped, 0 failed
1271
+    mysql: 105 tests ran in 6434 ms, 1 skipped, 0 failed
1272
+    pgsql: 105 tests ran in 979 ms, 1 skipped, 0 failed
1276
     sqlsrv: skipped, driver not loaded
1273
     sqlsrv: skipped, driver not loaded
1277
-    sqlite: 104 tests ran in 961 ms, 12 skipped, 0 failed
1274
+    sqlite: 105 tests ran in 1373 ms, 12 skipped, 0 failed
1278
 
1275
 
1279
 The above test run (including starting up the databases) takes less than 5 minutes on my slow laptop.
1276
 The above test run (including starting up the databases) takes less than 5 minutes on my slow laptop.
1280
 
1277
 
1294
     [3/4] Starting SQLServer 2017 ... skipped
1291
     [3/4] Starting SQLServer 2017 ... skipped
1295
     [4/4] Cloning PHP-CRUD-API v2 ... skipped
1292
     [4/4] Cloning PHP-CRUD-API v2 ... skipped
1296
     ------------------------------------------------
1293
     ------------------------------------------------
1297
-    mysql: 104 tests ran in 3282 ms, 1 skipped, 0 failed
1298
-    pgsql: 104 tests ran in 856 ms, 1 skipped, 0 failed
1294
+    mysql: 105 tests ran in 3390 ms, 1 skipped, 0 failed
1295
+    pgsql: 105 tests ran in 936 ms, 1 skipped, 0 failed
1299
     sqlsrv: skipped, driver not loaded
1296
     sqlsrv: skipped, driver not loaded
1300
-    sqlite: 104 tests ran in 972 ms, 12 skipped, 0 failed
1297
+    sqlite: 105 tests ran in 1063 ms, 12 skipped, 0 failed
1301
     root@b7ab9472e08f:/php-crud-api# 
1298
     root@b7ab9472e08f:/php-crud-api# 
1302
 
1299
 
1303
 As you can see the "run.sh" script gives you access to a prompt in a chosen the docker environment.
1300
 As you can see the "run.sh" script gives you access to a prompt in a chosen the docker environment.

+ 148
- 147
api.php View File

8220
     use Psr\Http\Server\RequestHandlerInterface;
8220
     use Psr\Http\Server\RequestHandlerInterface;
8221
     use Tqdev\PhpCrudApi\Column\ReflectionService;
8221
     use Tqdev\PhpCrudApi\Column\ReflectionService;
8222
     use Tqdev\PhpCrudApi\Column\Reflection\ReflectedTable;
8222
     use Tqdev\PhpCrudApi\Column\Reflection\ReflectedTable;
8223
+    use Tqdev\PhpCrudApi\Column\Reflection\ReflectedColumn;
8223
     use Tqdev\PhpCrudApi\Controller\Responder;
8224
     use Tqdev\PhpCrudApi\Controller\Responder;
8224
     use Tqdev\PhpCrudApi\Middleware\Base\Middleware;
8225
     use Tqdev\PhpCrudApi\Middleware\Base\Middleware;
8225
     use Tqdev\PhpCrudApi\Middleware\Router\Router;
8226
     use Tqdev\PhpCrudApi\Middleware\Router\Router;
8226
     use Tqdev\PhpCrudApi\Record\ErrorCode;
8227
     use Tqdev\PhpCrudApi\Record\ErrorCode;
8227
     use Tqdev\PhpCrudApi\RequestUtils;
8228
     use Tqdev\PhpCrudApi\RequestUtils;
8228
 
8229
 
8229
-    class ValidationMiddleware extends Middleware {
8230
+    class ValidationMiddleware extends Middleware
8231
+    {
8230
     	private $reflection;
8232
     	private $reflection;
8231
-    	private $typesToValidate;
8232
 
8233
 
8233
-    	public function __construct(Router $router, Responder $responder, array $properties, ReflectionService $reflection) {
8234
+    	public function __construct(Router $router, Responder $responder, array $properties, ReflectionService $reflection)
8235
+    	{
8234
     		parent::__construct($router, $responder, $properties);
8236
     		parent::__construct($router, $responder, $properties);
8235
     		$this->reflection = $reflection;
8237
     		$this->reflection = $reflection;
8236
-    		$typesStr = $this->getProperty('types', 'all');
8237
-    		if (is_null($typesStr)) {
8238
-    			$typesStr = 'all';
8239
-    		}
8240
-    		if (strlen($typesStr) == 0) {
8241
-    			$typesStr = 'none';
8242
-    		}
8243
-    		$this->typesToValidate = explode(',', $typesStr);
8244
-    		if (is_null($this->typesToValidate) || count($this->typesToValidate) == 0) {
8245
-    			$this->typesToValidate = ['all'];
8246
-    		}
8247
     	}
8238
     	}
8248
 
8239
 
8249
-    	private function callHandler($handler, $record, string $operation, ReflectedTable $table) /*: ResponseInterface?*/ {
8240
+    	private function callHandler($handler, $record, string $operation, ReflectedTable $table) /*: ResponseInterface?*/
8241
+    	{
8250
     		$context = (array) $record;
8242
     		$context = (array) $record;
8251
     		$details = array();
8243
     		$details = array();
8252
     		$tableName = $table->getName();
8244
     		$tableName = $table->getName();
8254
     			if ($table->hasColumn($columnName)) {
8246
     			if ($table->hasColumn($columnName)) {
8255
     				$column = $table->getColumn($columnName);
8247
     				$column = $table->getColumn($columnName);
8256
     				$valid = call_user_func($handler, $operation, $tableName, $column->serialize(), $value, $context);
8248
     				$valid = call_user_func($handler, $operation, $tableName, $column->serialize(), $value, $context);
8257
-    				if ($valid || $valid == '') {
8258
-    					$valid = $this->validateType($column->serialize(), $value);
8249
+    				if ($valid === true || $valid === '') {
8250
+    					$valid = $this->validateType($column, $value);
8259
     				}
8251
     				}
8260
     				if ($valid !== true && $valid !== '') {
8252
     				if ($valid !== true && $valid !== '') {
8261
     					$details[$columnName] = $valid;
8253
     					$details[$columnName] = $valid;
8268
     		return null;
8260
     		return null;
8269
     	}
8261
     	}
8270
 
8262
 
8271
-    	private function validateType($column, $value) {
8272
-    		if ($this->typesToValidate[0] == 'none') {
8273
-    			return (true);
8274
-    		}
8275
-    		if ($this->typesToValidate[0] != 'all') {
8276
-    			if (!in_array($column['type'], $this->typesToValidate)) {
8277
-    				return (true);
8278
-    			}
8279
-    		}
8280
-    		if (is_null($value)) {
8281
-    			return ($column["nullable"] ? true : "cannot be null");
8282
-    		}
8283
-    		switch ($column['type']) {
8284
-    		case 'integer':
8285
-    			if (!is_numeric($value)) {
8286
-    				return ('must be numeric');
8287
-    			}
8288
-
8289
-    			if (strlen($value) > 20) {
8290
-    				return ('exceeds range');
8263
+    	private function validateType(ReflectedColumn $column, $value)
8264
+    	{
8265
+    		$types = $this->getArrayProperty('types', 'all');
8266
+    		if (in_array('all', $types) || in_array($column->getType(), $types)) {
8267
+    			if (is_null($value)) {
8268
+    				return ($column->getNullable() ? true : "cannot be null");
8291
     			}
8269
     			}
8292
-
8293
-    			break;
8294
-    		case 'bigint':
8295
-    			if (!is_numeric($value)) {
8296
-    				return ('must be numeric');
8297
-    			}
8298
-
8299
-    			if (strlen($value) > 20) {
8300
-    				return ('exceeds range');
8301
-    			}
8302
-
8303
-    			break;
8304
-    		case 'varchar':
8305
-    			if (strlen($value) > $column['length']) {
8306
-    				return ('too long');
8307
-    			}
8308
-
8309
-    			break;
8310
-    		case 'decimal':
8311
-    			if (!is_float($value) && !is_numeric($value)) {
8312
-    				return ('not a float');
8313
-    			}
8314
-
8315
-    			break;
8316
-    		case 'float':
8317
-    			if (!is_float($value) && !is_numeric($value)) {
8318
-    				return ('not a float');
8319
-    			}
8320
-
8321
-    			break;
8322
-    		case 'double':
8323
-    			if (!is_float($value) && !is_numeric($value)) {
8324
-    				return ('not a float');
8325
-    			}
8326
-
8327
-    			break;
8328
-    		case 'boolean':
8329
-    			if ($value != 0 && $value != 1) {
8330
-    				return ('not a valid boolean');
8331
-    			}
8332
-
8333
-    			break;
8334
-    		case 'date':
8335
-    			$date_array = explode('-', $value);
8336
-    			if (count($date_array) != 3) {
8337
-    				return ('invalid date format use yyyy-mm-dd');
8338
-    			}
8339
-
8340
-    			if (!@checkdate($date_array[1], $date_array[2], $date_array[0])) {
8341
-    				return ('not a valid date');
8342
-    			}
8343
-
8344
-    			break;
8345
-    		case 'time':
8346
-    			$time_array = explode(':', $value);
8347
-    			if (count($time_array) != 3) {
8348
-    				return ('invalid time format use hh:mm:ss');
8349
-    			}
8350
-
8351
-    			foreach ($time_array as $t) {
8352
-    				if (!is_numeric($t)) {
8353
-    					return ('non-numeric time value');
8270
+    			if (is_string($value)) {
8271
+    				// check for whitespace
8272
+    				switch ($column->getType()) {
8273
+    					case 'varchar':
8274
+    					case 'clob':
8275
+    						break;
8276
+    					default:
8277
+    						if (strlen(trim($value)) != strlen($value)) {
8278
+    							return 'illegal whitespace';
8279
+    						}
8280
+    						break;
8354
     				}
8281
     				}
8355
-    			}
8356
-
8357
-    			if ($time_array[1] < 0 || $time_array[2] < 0 || $time_array[0] < -838 || $time_array[1] > 59 || $time_array[2] > 59 || $time_array[0] > 838) {
8358
-    				return ('not a valid time');
8359
-    			}
8360
-
8361
-    			break;
8362
-    		case 'timestamp':
8363
-    			$split_timestamp = explode(' ', $value);
8364
-    			if (count($split_timestamp) != 2) {
8365
-    				return ('invalid timestamp format use yyyy-mm-dd hh:mm:ss');
8366
-    			}
8367
-
8368
-    			$date_array = explode('-', $split_timestamp[0]);
8369
-    			if (count($date_array) != 3) {
8370
-    				return ('invalid date format use yyyy-mm-dd');
8371
-    			}
8372
-
8373
-    			if (!@checkdate($date_array[1], $date_array[2], $date_array[0])) {
8374
-    				return ('not a valid date');
8375
-    			}
8376
-
8377
-    			$time_array = explode(':', $split_timestamp[1]);
8378
-    			if (count($time_array) != 3) {
8379
-    				return ('invalid time format use hh:mm:ss');
8380
-    			}
8381
-
8382
-    			foreach ($time_array as $t) {
8383
-    				if (!is_numeric($t)) {
8384
-    					return ('non-numeric time value');
8282
+    				// try to parse
8283
+    				switch ($column->getType()) {
8284
+    					case 'integer':
8285
+    					case 'bigint':
8286
+    						if (
8287
+    							filter_var($value, FILTER_SANITIZE_NUMBER_INT) !== $value ||
8288
+    							filter_var($value, FILTER_VALIDATE_INT) === false
8289
+    						) {
8290
+    							return 'invalid integer';
8291
+    						}
8292
+    						break;
8293
+    					case 'varchar':
8294
+    						if (mb_strlen($value, 'UTF-8') > $column->getLength()) {
8295
+    							return 'string too long';
8296
+    						}
8297
+    						break;
8298
+    					case 'decimal':
8299
+    						if (!is_numeric($value)) {
8300
+    							return 'invalid decimal';
8301
+    						}
8302
+    						break;
8303
+    					case 'float':
8304
+    					case 'double':
8305
+    						if (
8306
+    							filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT) !== $value ||
8307
+    							filter_var($value, FILTER_VALIDATE_FLOAT) === false
8308
+    						) {
8309
+    							return 'invalid float';
8310
+    						}
8311
+    						break;
8312
+    					case 'boolean':
8313
+    						if (
8314
+    							filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) === null
8315
+    						) {
8316
+    							return 'invalid boolean';
8317
+    						}
8318
+    						break;
8319
+    					case 'date':
8320
+    						if (date_create_from_format('Y-m-d', $value) === false) {
8321
+    							return 'invalid date';
8322
+    						}
8323
+    						break;
8324
+    					case 'time':
8325
+    						if (date_create_from_format('H:i:s', $value) === false) {
8326
+    							return 'invalid time';
8327
+    						}
8328
+    						break;
8329
+    					case 'timestamp':
8330
+    						if (date_create_from_format('Y-m-d H:i:s', $value) === false) {
8331
+    							return 'invalid timestamp';
8332
+    						}
8333
+    						break;
8334
+    					case 'clob':
8335
+    						// no checks needed
8336
+    						break;
8337
+    					case 'blob':
8338
+    					case 'varbinary':
8339
+    						if (base64_decode($value, true) === false) {
8340
+    							return 'invalid base64';
8341
+    						}
8342
+    						break;
8343
+    					case 'geometry':
8344
+    						// no checks yet
8345
+    						break;
8346
+    				}
8347
+    			} else { // check non-string types
8348
+    				switch ($column->getType()) {
8349
+    					case 'integer':
8350
+    					case 'bigint':
8351
+    						if (!is_int($value)) {
8352
+    							return 'invalid integer';
8353
+    						}
8354
+    						break;
8355
+    					case 'decimal':
8356
+    					case 'float':
8357
+    					case 'double':
8358
+    						if (!is_float($value) && !is_int($value)) {
8359
+    							return 'invalid float';
8360
+    						}
8361
+    						break;
8362
+    					case 'boolean':
8363
+    						if (!(is_int($value) && ($value === 1 || $value === 0)) && !is_bool($value)) {
8364
+    							return 'invalid boolean';
8365
+    						}
8366
+    						break;
8367
+    					default:
8368
+    						return 'invalid ' . $column->getType();
8385
     				}
8369
     				}
8386
     			}
8370
     			}
8387
-
8388
-    			if ($time_array[1] < 0 || $time_array[2] < 0 || $time_array[0] < 0 || $time_array[1] > 59 || $time_array[2] > 59 || $time_array[0] > 23) {
8389
-    				return ('not a valid time');
8390
-    			}
8391
-
8392
-    			break;
8393
-    		case 'clob':
8394
-    			break;
8395
-    		case 'blob':
8396
-    			break;
8397
-    		case 'varbinary':
8398
-    			if (((strlen($value) * 3 / 4) - substr_count(substr($value, -2), '=')) > $column['length']) {
8399
-    				return ('too long');
8371
+    			// extra checks
8372
+    			switch ($column->getType()) {
8373
+    				case 'integer': // 4 byte signed
8374
+    					$value = filter_var($value, FILTER_VALIDATE_INT);
8375
+    					if ($value > 2147483647 || $value < -2147483648) {
8376
+    						return 'invalid integer';
8377
+    					}
8378
+    					break;
8379
+    				case 'decimal':
8380
+    					$value = "$value";
8381
+    					if (strpos($value, '.') !== false) {
8382
+    						list($whole, $decimals) = explode('.', $value, 2);
8383
+    					} else {
8384
+    						list($whole, $decimals) = array($value, '');
8385
+    					}
8386
+    					if (strlen($whole) > 0 && !ctype_digit($whole)) {
8387
+    						return 'invalid decimal';
8388
+    					}
8389
+    					if (strlen($decimals) > 0 && !ctype_digit($decimals)) {
8390
+    						return 'invalid decimal';
8391
+    					}
8392
+    					if (strlen($whole) > $column->getPrecision() - $column->getScale()) {
8393
+    						return 'decimal too large';
8394
+    					}
8395
+    					if (strlen($decimals) > $column->getScale()) {
8396
+    						return 'decimal too precise';
8397
+    					}
8398
+    					break;
8399
+    				case 'varbinary':
8400
+    					if (strlen(base64_decode($value)) > $column->getLength()) {
8401
+    						return 'string too long';
8402
+    					}
8403
+    					break;
8400
     			}
8404
     			}
8401
-
8402
-    			break;
8403
-    		case 'geometry':
8404
-    			break;
8405
     		}
8405
     		}
8406
     		return (true);
8406
     		return (true);
8407
     	}
8407
     	}
8408
 
8408
 
8409
-    	public function process(ServerRequestInterface $request, RequestHandlerInterface $next): ResponseInterface{
8409
+    	public function process(ServerRequestInterface $request, RequestHandlerInterface $next): ResponseInterface
8410
+    	{
8410
     		$operation = RequestUtils::getOperation($request);
8411
     		$operation = RequestUtils::getOperation($request);
8411
     		if (in_array($operation, ['create', 'update', 'increment'])) {
8412
     		if (in_array($operation, ['create', 'update', 'increment'])) {
8412
     			$tableName = RequestUtils::getPathSegment($request, 2);
8413
     			$tableName = RequestUtils::getPathSegment($request, 2);

+ 1
- 1
docker/centos8/Dockerfile View File

25
 RUN dnf -y module disable postgresql
25
 RUN dnf -y module disable postgresql
26
 
26
 
27
 RUN dnf -y install \
27
 RUN dnf -y install \
28
-php-cli php-xml php-json \
28
+php-cli php-xml php-json php-mbstring \
29
 MariaDB-server MariaDB-client php-mysqlnd \
29
 MariaDB-server MariaDB-client php-mysqlnd \
30
 postgresql12 postgresql12-server php-pgsql postgis30_12 \
30
 postgresql12 postgresql12-server php-pgsql postgis30_12 \
31
 sqlite php-sqlite3 \
31
 sqlite php-sqlite3 \

+ 1
- 1
docker/debian10/Dockerfile View File

4
 
4
 
5
 # install: php / mysql / postgres / sqlite / tools / mssql deps
5
 # install: php / mysql / postgres / sqlite / tools / mssql deps
6
 RUN apt-get update && apt-get -y install \
6
 RUN apt-get update && apt-get -y install \
7
-php-cli php-xml \
7
+php-cli php-xml php-mbstring \
8
 mariadb-server mariadb-client php-mysql \
8
 mariadb-server mariadb-client php-mysql \
9
 postgresql php-pgsql \
9
 postgresql php-pgsql \
10
 postgresql-11-postgis-2.5 \
10
 postgresql-11-postgis-2.5 \

+ 1
- 1
docker/debian9/Dockerfile View File

4
 
4
 
5
 # install: php / mysql / postgres / sqlite / tools / mssql deps
5
 # install: php / mysql / postgres / sqlite / tools / mssql deps
6
 RUN apt-get update && apt-get -y install \
6
 RUN apt-get update && apt-get -y install \
7
-php-cli php-xml \
7
+php-cli php-xml php-mbstring \
8
 mariadb-server mariadb-client php-mysql \
8
 mariadb-server mariadb-client php-mysql \
9
 postgresql php-pgsql \
9
 postgresql php-pgsql \
10
 postgresql-9.6-postgis-2.3 \
10
 postgresql-9.6-postgis-2.3 \

+ 1
- 1
docker/ubuntu16/Dockerfile View File

4
 
4
 
5
 # install: php / mysql / postgres / tools / mssql deps
5
 # install: php / mysql / postgres / tools / mssql deps
6
 RUN apt-get update && apt-get -y install \
6
 RUN apt-get update && apt-get -y install \
7
-php-cli php-xml \
7
+php-cli php-xml php-mbstring \
8
 mariadb-server mariadb-client php-mysql \
8
 mariadb-server mariadb-client php-mysql \
9
 postgresql php-pgsql \
9
 postgresql php-pgsql \
10
 postgresql-9.5-postgis-2.2 \
10
 postgresql-9.5-postgis-2.2 \

+ 1
- 4
docker/ubuntu18/Dockerfile View File

2
 
2
 
3
 ARG DEBIAN_FRONTEND=noninteractive
3
 ARG DEBIAN_FRONTEND=noninteractive
4
 
4
 
5
-ENV TZ=Etc/UTC
6
-RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
7
-
8
 # install: php / mysql / postgres / sqlite / tools
5
 # install: php / mysql / postgres / sqlite / tools
9
 RUN apt-get update && apt-get -y install \
6
 RUN apt-get update && apt-get -y install \
10
-php-cli php-xml \
7
+php-cli php-xml php-mbstring \
11
 mysql-server mysql-client php-mysql \
8
 mysql-server mysql-client php-mysql \
12
 postgresql php-pgsql \
9
 postgresql php-pgsql \
13
 postgresql-10-postgis-2.4 \
10
 postgresql-10-postgis-2.4 \

+ 1
- 4
docker/ubuntu20/Dockerfile View File

2
 
2
 
3
 ARG DEBIAN_FRONTEND=noninteractive
3
 ARG DEBIAN_FRONTEND=noninteractive
4
 
4
 
5
-ENV TZ=Etc/UTC
6
-RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
7
-
8
 # install: php / mysql / postgres / sqlite / tools
5
 # install: php / mysql / postgres / sqlite / tools
9
 RUN apt-get update && apt-get -y install \
6
 RUN apt-get update && apt-get -y install \
10
-php-cli php-xml \
7
+php-cli php-xml php-mbstring \
11
 mysql-server mysql-client php-mysql \
8
 mysql-server mysql-client php-mysql \
12
 postgresql php-pgsql \
9
 postgresql php-pgsql \
13
 postgresql-12-postgis-3 \
10
 postgresql-12-postgis-3 \

+ 149
- 148
src/Tqdev/PhpCrudApi/Middleware/ValidationMiddleware.php View File

7
 use Psr\Http\Server\RequestHandlerInterface;
7
 use Psr\Http\Server\RequestHandlerInterface;
8
 use Tqdev\PhpCrudApi\Column\ReflectionService;
8
 use Tqdev\PhpCrudApi\Column\ReflectionService;
9
 use Tqdev\PhpCrudApi\Column\Reflection\ReflectedTable;
9
 use Tqdev\PhpCrudApi\Column\Reflection\ReflectedTable;
10
+use Tqdev\PhpCrudApi\Column\Reflection\ReflectedColumn;
10
 use Tqdev\PhpCrudApi\Controller\Responder;
11
 use Tqdev\PhpCrudApi\Controller\Responder;
11
 use Tqdev\PhpCrudApi\Middleware\Base\Middleware;
12
 use Tqdev\PhpCrudApi\Middleware\Base\Middleware;
12
 use Tqdev\PhpCrudApi\Middleware\Router\Router;
13
 use Tqdev\PhpCrudApi\Middleware\Router\Router;
13
 use Tqdev\PhpCrudApi\Record\ErrorCode;
14
 use Tqdev\PhpCrudApi\Record\ErrorCode;
14
 use Tqdev\PhpCrudApi\RequestUtils;
15
 use Tqdev\PhpCrudApi\RequestUtils;
15
 
16
 
16
-class ValidationMiddleware extends Middleware {
17
+class ValidationMiddleware extends Middleware
18
+{
17
 	private $reflection;
19
 	private $reflection;
18
-	private $typesToValidate;
19
 
20
 
20
-	public function __construct(Router $router, Responder $responder, array $properties, ReflectionService $reflection) {
21
+	public function __construct(Router $router, Responder $responder, array $properties, ReflectionService $reflection)
22
+	{
21
 		parent::__construct($router, $responder, $properties);
23
 		parent::__construct($router, $responder, $properties);
22
 		$this->reflection = $reflection;
24
 		$this->reflection = $reflection;
23
-		$typesStr = $this->getProperty('types', 'all');
24
-		if (is_null($typesStr)) {
25
-			$typesStr = 'all';
26
-		}
27
-		if (strlen($typesStr) == 0) {
28
-			$typesStr = 'none';
29
-		}
30
-		$this->typesToValidate = explode(',', $typesStr);
31
-		if (is_null($this->typesToValidate) || count($this->typesToValidate) == 0) {
32
-			$this->typesToValidate = ['all'];
33
-		}
34
 	}
25
 	}
35
 
26
 
36
-	private function callHandler($handler, $record, string $operation, ReflectedTable $table) /*: ResponseInterface?*/ {
27
+	private function callHandler($handler, $record, string $operation, ReflectedTable $table) /*: ResponseInterface?*/
28
+	{
37
 		$context = (array) $record;
29
 		$context = (array) $record;
38
 		$details = array();
30
 		$details = array();
39
 		$tableName = $table->getName();
31
 		$tableName = $table->getName();
41
 			if ($table->hasColumn($columnName)) {
33
 			if ($table->hasColumn($columnName)) {
42
 				$column = $table->getColumn($columnName);
34
 				$column = $table->getColumn($columnName);
43
 				$valid = call_user_func($handler, $operation, $tableName, $column->serialize(), $value, $context);
35
 				$valid = call_user_func($handler, $operation, $tableName, $column->serialize(), $value, $context);
44
-				if ($valid || $valid == '') {
45
-					$valid = $this->validateType($column->serialize(), $value);
36
+				if ($valid === true || $valid === '') {
37
+					$valid = $this->validateType($column, $value);
46
 				}
38
 				}
47
 				if ($valid !== true && $valid !== '') {
39
 				if ($valid !== true && $valid !== '') {
48
 					$details[$columnName] = $valid;
40
 					$details[$columnName] = $valid;
55
 		return null;
47
 		return null;
56
 	}
48
 	}
57
 
49
 
58
-	private function validateType($column, $value) {
59
-		if ($this->typesToValidate[0] == 'none') {
60
-			return (true);
61
-		}
62
-		if ($this->typesToValidate[0] != 'all') {
63
-			if (!in_array($column['type'], $this->typesToValidate)) {
64
-				return (true);
65
-			}
66
-		}
67
-		if (is_null($value)) {
68
-			return ($column["nullable"] ? true : "cannot be null");
69
-		}
70
-		switch ($column['type']) {
71
-		case 'integer':
72
-			if (!is_numeric($value)) {
73
-				return ('must be numeric');
74
-			}
75
-
76
-			if (strlen($value) > 20) {
77
-				return ('exceeds range');
78
-			}
79
-
80
-			break;
81
-		case 'bigint':
82
-			if (!is_numeric($value)) {
83
-				return ('must be numeric');
84
-			}
85
-
86
-			if (strlen($value) > 20) {
87
-				return ('exceeds range');
88
-			}
89
-
90
-			break;
91
-		case 'varchar':
92
-			if (strlen($value) > $column['length']) {
93
-				return ('too long');
94
-			}
95
-
96
-			break;
97
-		case 'decimal':
98
-			if (!is_float($value) && !is_numeric($value)) {
99
-				return ('not a float');
100
-			}
101
-
102
-			break;
103
-		case 'float':
104
-			if (!is_float($value) && !is_numeric($value)) {
105
-				return ('not a float');
106
-			}
107
-
108
-			break;
109
-		case 'double':
110
-			if (!is_float($value) && !is_numeric($value)) {
111
-				return ('not a float');
112
-			}
113
-
114
-			break;
115
-		case 'boolean':
116
-			if ($value != 0 && $value != 1) {
117
-				return ('not a valid boolean');
118
-			}
119
-
120
-			break;
121
-		case 'date':
122
-			$date_array = explode('-', $value);
123
-			if (count($date_array) != 3) {
124
-				return ('invalid date format use yyyy-mm-dd');
125
-			}
126
-
127
-			if (!@checkdate($date_array[1], $date_array[2], $date_array[0])) {
128
-				return ('not a valid date');
129
-			}
130
-
131
-			break;
132
-		case 'time':
133
-			$time_array = explode(':', $value);
134
-			if (count($time_array) != 3) {
135
-				return ('invalid time format use hh:mm:ss');
136
-			}
137
-
138
-			foreach ($time_array as $t) {
139
-				if (!is_numeric($t)) {
140
-					return ('non-numeric time value');
50
+	private function validateType(ReflectedColumn $column, $value)
51
+	{
52
+		$types = $this->getArrayProperty('types', 'all');
53
+		if (in_array('all', $types) || in_array($column->getType(), $types)) {
54
+			if (is_null($value)) {
55
+				return ($column->getNullable() ? true : "cannot be null");
56
+			}
57
+			if (is_string($value)) {
58
+				// check for whitespace
59
+				switch ($column->getType()) {
60
+					case 'varchar':
61
+					case 'clob':
62
+						break;
63
+					default:
64
+						if (strlen(trim($value)) != strlen($value)) {
65
+							return 'illegal whitespace';
66
+						}
67
+						break;
141
 				}
68
 				}
142
-			}
143
-
144
-			if ($time_array[1] < 0 || $time_array[2] < 0 || $time_array[0] < -838 || $time_array[1] > 59 || $time_array[2] > 59 || $time_array[0] > 838) {
145
-				return ('not a valid time');
146
-			}
147
-
148
-			break;
149
-		case 'timestamp':
150
-			$split_timestamp = explode(' ', $value);
151
-			if (count($split_timestamp) != 2) {
152
-				return ('invalid timestamp format use yyyy-mm-dd hh:mm:ss');
153
-			}
154
-
155
-			$date_array = explode('-', $split_timestamp[0]);
156
-			if (count($date_array) != 3) {
157
-				return ('invalid date format use yyyy-mm-dd');
158
-			}
159
-
160
-			if (!@checkdate($date_array[1], $date_array[2], $date_array[0])) {
161
-				return ('not a valid date');
162
-			}
163
-
164
-			$time_array = explode(':', $split_timestamp[1]);
165
-			if (count($time_array) != 3) {
166
-				return ('invalid time format use hh:mm:ss');
167
-			}
168
-
169
-			foreach ($time_array as $t) {
170
-				if (!is_numeric($t)) {
171
-					return ('non-numeric time value');
69
+				// try to parse
70
+				switch ($column->getType()) {
71
+					case 'integer':
72
+					case 'bigint':
73
+						if (
74
+							filter_var($value, FILTER_SANITIZE_NUMBER_INT) !== $value ||
75
+							filter_var($value, FILTER_VALIDATE_INT) === false
76
+						) {
77
+							return 'invalid integer';
78
+						}
79
+						break;
80
+					case 'varchar':
81
+						if (mb_strlen($value, 'UTF-8') > $column->getLength()) {
82
+							return 'string too long';
83
+						}
84
+						break;
85
+					case 'decimal':
86
+						if (!is_numeric($value)) {
87
+							return 'invalid decimal';
88
+						}
89
+						break;
90
+					case 'float':
91
+					case 'double':
92
+						if (
93
+							filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT) !== $value ||
94
+							filter_var($value, FILTER_VALIDATE_FLOAT) === false
95
+						) {
96
+							return 'invalid float';
97
+						}
98
+						break;
99
+					case 'boolean':
100
+						if (
101
+							filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) === null
102
+						) {
103
+							return 'invalid boolean';
104
+						}
105
+						break;
106
+					case 'date':
107
+						if (date_create_from_format('Y-m-d', $value) === false) {
108
+							return 'invalid date';
109
+						}
110
+						break;
111
+					case 'time':
112
+						if (date_create_from_format('H:i:s', $value) === false) {
113
+							return 'invalid time';
114
+						}
115
+						break;
116
+					case 'timestamp':
117
+						if (date_create_from_format('Y-m-d H:i:s', $value) === false) {
118
+							return 'invalid timestamp';
119
+						}
120
+						break;
121
+					case 'clob':
122
+						// no checks needed
123
+						break;
124
+					case 'blob':
125
+					case 'varbinary':
126
+						if (base64_decode($value, true) === false) {
127
+							return 'invalid base64';
128
+						}
129
+						break;
130
+					case 'geometry':
131
+						// no checks yet
132
+						break;
133
+				}
134
+			} else { // check non-string types
135
+				switch ($column->getType()) {
136
+					case 'integer':
137
+					case 'bigint':
138
+						if (!is_int($value)) {
139
+							return 'invalid integer';
140
+						}
141
+						break;
142
+					case 'decimal':
143
+					case 'float':
144
+					case 'double':
145
+						if (!is_float($value) && !is_int($value)) {
146
+							return 'invalid float';
147
+						}
148
+						break;
149
+					case 'boolean':
150
+						if (!(is_int($value) && ($value === 1 || $value === 0)) && !is_bool($value)) {
151
+							return 'invalid boolean';
152
+						}
153
+						break;
154
+					default:
155
+						return 'invalid ' . $column->getType();
172
 				}
156
 				}
173
 			}
157
 			}
174
-
175
-			if ($time_array[1] < 0 || $time_array[2] < 0 || $time_array[0] < 0 || $time_array[1] > 59 || $time_array[2] > 59 || $time_array[0] > 23) {
176
-				return ('not a valid time');
177
-			}
178
-
179
-			break;
180
-		case 'clob':
181
-			break;
182
-		case 'blob':
183
-			break;
184
-		case 'varbinary':
185
-			if (((strlen($value) * 3 / 4) - substr_count(substr($value, -2), '=')) > $column['length']) {
186
-				return ('too long');
158
+			// extra checks
159
+			switch ($column->getType()) {
160
+				case 'integer': // 4 byte signed
161
+					$value = filter_var($value, FILTER_VALIDATE_INT);
162
+					if ($value > 2147483647 || $value < -2147483648) {
163
+						return 'invalid integer';
164
+					}
165
+					break;
166
+				case 'decimal':
167
+					$value = "$value";
168
+					if (strpos($value, '.') !== false) {
169
+						list($whole, $decimals) = explode('.', $value, 2);
170
+					} else {
171
+						list($whole, $decimals) = array($value, '');
172
+					}
173
+					if (strlen($whole) > 0 && !ctype_digit($whole)) {
174
+						return 'invalid decimal';
175
+					}
176
+					if (strlen($decimals) > 0 && !ctype_digit($decimals)) {
177
+						return 'invalid decimal';
178
+					}
179
+					if (strlen($whole) > $column->getPrecision() - $column->getScale()) {
180
+						return 'decimal too large';
181
+					}
182
+					if (strlen($decimals) > $column->getScale()) {
183
+						return 'decimal too precise';
184
+					}
185
+					break;
186
+				case 'varbinary':
187
+					if (strlen(base64_decode($value)) > $column->getLength()) {
188
+						return 'string too long';
189
+					}
190
+					break;
187
 			}
191
 			}
188
-
189
-			break;
190
-		case 'geometry':
191
-			break;
192
 		}
192
 		}
193
 		return (true);
193
 		return (true);
194
 	}
194
 	}
195
 
195
 
196
-	public function process(ServerRequestInterface $request, RequestHandlerInterface $next): ResponseInterface{
196
+	public function process(ServerRequestInterface $request, RequestHandlerInterface $next): ResponseInterface
197
+	{
197
 		$operation = RequestUtils::getOperation($request);
198
 		$operation = RequestUtils::getOperation($request);
198
 		if (in_array($operation, ['create', 'update', 'increment'])) {
199
 		if (in_array($operation, ['create', 'update', 'increment'])) {
199
 			$tableName = RequestUtils::getPathSegment($request, 2);
200
 			$tableName = RequestUtils::getPathSegment($request, 2);

+ 11
- 0
tests/functional/003_columns/014_create_types_table.log View File

20
 POST /records/types
20
 POST /records/types
21
 Content-Type: application/json
21
 Content-Type: application/json
22
 
22
 
23
+{"integer":3,"bigint":4,"varchar":"bcd","decimal":"2.34","float":2,"double":34.56,"boolean":false,"date":"2020-02-02","time":"23:55:59","timestamp":"2002-03-04 05:06:07","clob":"b","blob":"Yg==","geometry":"POINT(2 3)"}
24
+===
25
+200
26
+Content-Type: application/json
27
+Content-Length: 1
28
+
29
+1
30
+===
31
+PUT /records/types/1
32
+Content-Type: application/json
33
+
23
 {"integer":2,"bigint":3,"varchar":"abc","decimal":"1.23","float":1,"double":23.45,"boolean":true,"date":"1970-01-01","time":"00:00:01","timestamp":"2001-02-03 04:05:06","clob":"a","blob":"YQ==","geometry":"POINT(1 2)"}
34
 {"integer":2,"bigint":3,"varchar":"abc","decimal":"1.23","float":1,"double":23.45,"boolean":true,"date":"1970-01-01","time":"00:00:01","timestamp":"2001-02-03 04:05:06","clob":"a","blob":"YQ==","geometry":"POINT(1 2)"}
24
 ===
35
 ===
25
 200
36
 200

+ 97
- 73
tests/functional/003_columns/015_update_types_table.log View File

12
 POST /records/types
12
 POST /records/types
13
 Content-Type: application/json
13
 Content-Type: application/json
14
 
14
 
15
-{"integer":2,"bigint":3,"varchar":"abc","decimal":"1.23","float":1,"double":23.45,"boolean":true,"date":"1970-01-01","time":"00:00:01","timestamp":"2001-02-03 04:05:06","clob":"a","blob":"YQ==","geometry":"POINT(1 2)"}
15
+{"integer":2,"bigint":3,"varchar":"abc","decimal":1.23,"float":1,"double":23.45,"boolean":true,"date":"1970-01-01","time":"00:00:01","timestamp":"2001-02-03 04:05:06","clob":"a","blob":"YQ==","geometry":"POINT(1 2)"}
16
 ===
16
 ===
17
 200
17
 200
18
 Content-Type: application/json
18
 Content-Type: application/json
22
 ===
22
 ===
23
 PUT /records/types/1
23
 PUT /records/types/1
24
 
24
 
25
-{"integer":2,"bigint":2,"varchar":"b","decimal":2,"float":2,"double":2,"boolean":false,"date":"2000-01-02","time":"02:01:01","timestamp":"2000-01-02 02:01:01","clob":"b","blob":"Cg==","geometry":"POINT(2 2)"}
25
+{"boolean":null}
26
 ===
26
 ===
27
-200
27
+422
28
 Content-Type: application/json
28
 Content-Type: application/json
29
-Content-Length: 1
29
+Content-Length: 100
30
 
30
 
31
-1
31
+{"code":1013,"message":"Input validation failed for 'types'","details":{"boolean":"cannot be null"}}
32
 ===
32
 ===
33
-GET /records/types/1
33
+PUT /records/types/1
34
+
35
+{"integer":" 1\n"}
34
 ===
36
 ===
35
-200
37
+422
36
 Content-Type: application/json
38
 Content-Type: application/json
37
-Content-Length: 222
39
+Content-Length: 104
38
 
40
 
39
-{"id":1,"integer":2,"bigint":2,"varchar":"b","decimal":"2.0000","float":2,"double":2,"boolean":false,"date":"2000-01-02","time":"02:01:01","timestamp":"2000-01-02 02:01:01","clob":"b","blob":"Cg==","geometry":"POINT(2 2)"}
41
+{"code":1013,"message":"Input validation failed for 'types'","details":{"integer":"illegal whitespace"}}
40
 ===
42
 ===
41
 PUT /records/types/1
43
 PUT /records/types/1
42
 
44
 
43
-{"integer":3,"bigint":3,"varchar":"c","decimal":3,"float":3,"double":3,"boolean":true,"date":"2000-01-03","time":"03:01:01","timestamp":"2000-01-03 03:01:01","clob":"c","blob":"Yw==","geometry":"POINT(3 3)"}
45
+{"integer":"23e"}
44
 ===
46
 ===
45
-200
47
+422
46
 Content-Type: application/json
48
 Content-Type: application/json
47
-Content-Length: 1
49
+Content-Length: 101
48
 
50
 
49
-1
51
+{"code":1013,"message":"Input validation failed for 'types'","details":{"integer":"invalid integer"}}
50
 ===
52
 ===
51
-GET /records/types/1
53
+PUT /records/types/1
54
+
55
+integer=23e
52
 ===
56
 ===
53
-200
57
+422
54
 Content-Type: application/json
58
 Content-Type: application/json
55
-Content-Length: 221
59
+Content-Length: 101
56
 
60
 
57
-{"id":1,"integer":3,"bigint":3,"varchar":"c","decimal":"3.0000","float":3,"double":3,"boolean":true,"date":"2000-01-03","time":"03:01:01","timestamp":"2000-01-03 03:01:01","clob":"c","blob":"Yw==","geometry":"POINT(3 3)"}
61
+{"code":1013,"message":"Input validation failed for 'types'","details":{"integer":"invalid integer"}}
58
 ===
62
 ===
59
 PUT /records/types/1
63
 PUT /records/types/1
60
 
64
 
61
-{"integer":"string"}
65
+{"integer":"12345678901"}
62
 ===
66
 ===
63
 422
67
 422
64
 Content-Type: application/json
68
 Content-Type: application/json
65
 Content-Length: 101
69
 Content-Length: 101
66
 
70
 
67
-{"code":1013,"message":"Input validation failed for 'types'","details":{"integer":"must be numeric"}}
71
+{"code":1013,"message":"Input validation failed for 'types'","details":{"integer":"invalid integer"}}
68
 ===
72
 ===
69
 PUT /records/types/1
73
 PUT /records/types/1
70
 
74
 
71
-{"bigint":"string"}
75
+{"bigint":"12345678901234567890"}
72
 ===
76
 ===
73
 422
77
 422
74
 Content-Type: application/json
78
 Content-Type: application/json
75
 Content-Length: 100
79
 Content-Length: 100
76
 
80
 
77
-{"code":1013,"message":"Input validation failed for 'types'","details":{"bigint":"must be numeric"}}
81
+{"code":1013,"message":"Input validation failed for 'types'","details":{"bigint":"invalid integer"}}
78
 ===
82
 ===
79
 PUT /records/types/1
83
 PUT /records/types/1
80
 
84
 
82
 ===
86
 ===
83
 422
87
 422
84
 Content-Type: application/json
88
 Content-Type: application/json
85
-Content-Length: 94
89
+Content-Length: 101
86
 
90
 
87
-{"code":1013,"message":"Input validation failed for 'types'","details":{"varchar":"too long"}}
91
+{"code":1013,"message":"Input validation failed for 'types'","details":{"varchar":"string too long"}}
88
 ===
92
 ===
89
 PUT /records/types/1
93
 PUT /records/types/1
90
 
94
 
91
-{"decimal":"string"}
95
+{"decimal":"12.23.34"}
92
 ===
96
 ===
93
 422
97
 422
94
 Content-Type: application/json
98
 Content-Type: application/json
95
-Content-Length: 97
99
+Content-Length: 101
96
 
100
 
97
-{"code":1013,"message":"Input validation failed for 'types'","details":{"decimal":"not a float"}}
101
+{"code":1013,"message":"Input validation failed for 'types'","details":{"decimal":"invalid decimal"}}
98
 ===
102
 ===
99
 PUT /records/types/1
103
 PUT /records/types/1
100
 
104
 
101
-{"float":"string"}
105
+{"decimal":1131313145345}
102
 ===
106
 ===
103
 422
107
 422
104
 Content-Type: application/json
108
 Content-Type: application/json
105
-Content-Length: 95
109
+Content-Length: 103
106
 
110
 
107
-{"code":1013,"message":"Input validation failed for 'types'","details":{"float":"not a float"}}
111
+{"code":1013,"message":"Input validation failed for 'types'","details":{"decimal":"decimal too large"}}
108
 ===
112
 ===
109
 PUT /records/types/1
113
 PUT /records/types/1
110
 
114
 
111
-{"double":"string"}
115
+{"decimal":"1234567.123"}
112
 ===
116
 ===
113
 422
117
 422
114
 Content-Type: application/json
118
 Content-Type: application/json
115
-Content-Length: 96
119
+Content-Length: 103
116
 
120
 
117
-{"code":1013,"message":"Input validation failed for 'types'","details":{"double":"not a float"}}
121
+{"code":1013,"message":"Input validation failed for 'types'","details":{"decimal":"decimal too large"}}
118
 ===
122
 ===
119
 PUT /records/types/1
123
 PUT /records/types/1
120
 
124
 
121
-{"boolean":-1}
125
+{"decimal":11313131e+5}
126
+===
127
+422
128
+Content-Type: application/json
129
+Content-Length: 103
130
+
131
+{"code":1013,"message":"Input validation failed for 'types'","details":{"decimal":"decimal too large"}}
132
+===
133
+PUT /records/types/1
134
+
135
+{"decimal":"123456.12345"}
122
 ===
136
 ===
123
 422
137
 422
124
 Content-Type: application/json
138
 Content-Type: application/json
125
 Content-Length: 105
139
 Content-Length: 105
126
 
140
 
127
-{"code":1013,"message":"Input validation failed for 'types'","details":{"boolean":"not a valid boolean"}}
141
+{"code":1013,"message":"Input validation failed for 'types'","details":{"decimal":"decimal too precise"}}
128
 ===
142
 ===
129
 PUT /records/types/1
143
 PUT /records/types/1
130
 
144
 
131
-{"boolean":null}
145
+{"decimal":113131.3145345}
132
 ===
146
 ===
133
 422
147
 422
134
 Content-Type: application/json
148
 Content-Type: application/json
135
-Content-Length: 100
149
+Content-Length: 105
136
 
150
 
137
-{"code":1013,"message":"Input validation failed for 'types'","details":{"boolean":"cannot be null"}}
151
+{"code":1013,"message":"Input validation failed for 'types'","details":{"decimal":"decimal too precise"}}
138
 ===
152
 ===
139
 PUT /records/types/1
153
 PUT /records/types/1
140
 
154
 
141
-{"date":"string"}
155
+{"decimal":11313131E-5}
142
 ===
156
 ===
143
 422
157
 422
144
 Content-Type: application/json
158
 Content-Type: application/json
145
-Content-Length: 117
159
+Content-Length: 105
146
 
160
 
147
-{"code":1013,"message":"Input validation failed for 'types'","details":{"date":"invalid date format use yyyy-mm-dd"}}
161
+{"code":1013,"message":"Input validation failed for 'types'","details":{"decimal":"decimal too precise"}}
148
 ===
162
 ===
149
 PUT /records/types/1
163
 PUT /records/types/1
150
 
164
 
151
-{"date":"still-no-date"}
165
+{"float":"string"}
152
 ===
166
 ===
153
 422
167
 422
154
 Content-Type: application/json
168
 Content-Type: application/json
155
-Content-Length: 99
169
+Content-Length: 97
156
 
170
 
157
-{"code":1013,"message":"Input validation failed for 'types'","details":{"date":"not a valid date"}}
171
+{"code":1013,"message":"Input validation failed for 'types'","details":{"float":"invalid float"}}
158
 ===
172
 ===
159
 PUT /records/types/1
173
 PUT /records/types/1
160
 
174
 
161
-{"time":"string"}
175
+{"double":"string"}
162
 ===
176
 ===
163
 422
177
 422
164
 Content-Type: application/json
178
 Content-Type: application/json
165
-Content-Length: 115
179
+Content-Length: 98
166
 
180
 
167
-{"code":1013,"message":"Input validation failed for 'types'","details":{"time":"invalid time format use hh:mm:ss"}}
181
+{"code":1013,"message":"Input validation failed for 'types'","details":{"double":"invalid float"}}
168
 ===
182
 ===
169
 PUT /records/types/1
183
 PUT /records/types/1
170
 
184
 
171
-{"time":"still:no:time"}
185
+{"boolean":-1}
172
 ===
186
 ===
173
 422
187
 422
174
 Content-Type: application/json
188
 Content-Type: application/json
175
-Content-Length: 105
189
+Content-Length: 101
176
 
190
 
177
-{"code":1013,"message":"Input validation failed for 'types'","details":{"time":"non-numeric time value"}}
191
+{"code":1013,"message":"Input validation failed for 'types'","details":{"boolean":"invalid boolean"}}
178
 ===
192
 ===
179
 PUT /records/types/1
193
 PUT /records/types/1
180
 
194
 
181
-{"time":"999:999:999"}
195
+{"date":"string"}
182
 ===
196
 ===
183
 422
197
 422
184
 Content-Type: application/json
198
 Content-Type: application/json
185
-Content-Length: 99
199
+Content-Length: 95
186
 
200
 
187
-{"code":1013,"message":"Input validation failed for 'types'","details":{"time":"not a valid time"}}
201
+{"code":1013,"message":"Input validation failed for 'types'","details":{"date":"invalid date"}}
188
 ===
202
 ===
189
 PUT /records/types/1
203
 PUT /records/types/1
190
 
204
 
191
-{"timestamp":"string"}
205
+{"date":"still-no-date"}
192
 ===
206
 ===
193
 422
207
 422
194
 Content-Type: application/json
208
 Content-Type: application/json
195
-Content-Length: 136
209
+Content-Length: 95
196
 
210
 
197
-{"code":1013,"message":"Input validation failed for 'types'","details":{"timestamp":"invalid timestamp format use yyyy-mm-dd hh:mm:ss"}}
211
+{"code":1013,"message":"Input validation failed for 'types'","details":{"date":"invalid date"}}
198
 ===
212
 ===
199
 PUT /records/types/1
213
 PUT /records/types/1
200
 
214
 
201
-{"timestamp":"string 01:01:01"}
215
+{"time":"string"}
202
 ===
216
 ===
203
 422
217
 422
204
 Content-Type: application/json
218
 Content-Type: application/json
205
-Content-Length: 122
219
+Content-Length: 95
206
 
220
 
207
-{"code":1013,"message":"Input validation failed for 'types'","details":{"timestamp":"invalid date format use yyyy-mm-dd"}}
221
+{"code":1013,"message":"Input validation failed for 'types'","details":{"time":"invalid time"}}
208
 ===
222
 ===
209
 PUT /records/types/1
223
 PUT /records/types/1
210
 
224
 
211
-{"timestamp":"still-no-date 01:01:01"}
225
+{"time":"still:no:time"}
212
 ===
226
 ===
213
 422
227
 422
214
 Content-Type: application/json
228
 Content-Type: application/json
215
-Content-Length: 104
229
+Content-Length: 95
216
 
230
 
217
-{"code":1013,"message":"Input validation failed for 'types'","details":{"timestamp":"not a valid date"}}
231
+{"code":1013,"message":"Input validation failed for 'types'","details":{"time":"invalid time"}}
218
 ===
232
 ===
219
 PUT /records/types/1
233
 PUT /records/types/1
220
 
234
 
221
-{"timestamp":"2001-01-01 string"}
235
+{"time":"999:999:999"}
222
 ===
236
 ===
223
 422
237
 422
224
 Content-Type: application/json
238
 Content-Type: application/json
225
-Content-Length: 120
239
+Content-Length: 95
226
 
240
 
227
-{"code":1013,"message":"Input validation failed for 'types'","details":{"timestamp":"invalid time format use hh:mm:ss"}}
241
+{"code":1013,"message":"Input validation failed for 'types'","details":{"time":"invalid time"}}
228
 ===
242
 ===
229
 PUT /records/types/1
243
 PUT /records/types/1
230
 
244
 
231
-{"timestamp":"2001-01-01 still:no:time"}
245
+{"timestamp":"string"}
232
 ===
246
 ===
233
 422
247
 422
234
 Content-Type: application/json
248
 Content-Type: application/json
235
-Content-Length: 110
249
+Content-Length: 105
236
 
250
 
237
-{"code":1013,"message":"Input validation failed for 'types'","details":{"timestamp":"non-numeric time value"}}
251
+{"code":1013,"message":"Input validation failed for 'types'","details":{"timestamp":"invalid timestamp"}}
238
 ===
252
 ===
239
 PUT /records/types/1
253
 PUT /records/types/1
240
 
254
 
242
 ===
256
 ===
243
 422
257
 422
244
 Content-Type: application/json
258
 Content-Type: application/json
245
-Content-Length: 104
259
+Content-Length: 105
246
 
260
 
247
-{"code":1013,"message":"Input validation failed for 'types'","details":{"timestamp":"not a valid time"}}
261
+{"code":1013,"message":"Input validation failed for 'types'","details":{"timestamp":"invalid timestamp"}}
248
 ===
262
 ===
249
 PUT /records/types/1
263
 PUT /records/types/1
250
 
264
 
251
-{"clob":"HLnVf8PrxX/vPMElDxrlFLIHoBd1fnLVSXVEDLjt0bk="}
265
+{"clob":"𠜎𠜱𠝹𠱓𠱸𠲖𠳏𠳕𠴕𠵼𠵿𠸎𠸏𠹷𠺝𠺢𠻗"}
252
 ===
266
 ===
253
 200
267
 200
254
 Content-Type: application/json
268
 Content-Type: application/json
258
 ===
272
 ===
259
 PUT /records/types/1
273
 PUT /records/types/1
260
 
274
 
261
-{"blob":"T8O5IGVzdCBsZSBjYWbDqSBsZSBwbHVzIHByb2NoZT8="}
275
+{"blob":"!"}
276
+===
277
+422
278
+Content-Type: application/json
279
+Content-Length: 97
280
+
281
+{"code":1013,"message":"Input validation failed for 'types'","details":{"blob":"invalid base64"}}
282
+===
283
+PUT /records/types/1
284
+
285
+{"blob":"T8O5IGVzdCBsZSBjYWbDqSBsZSBwbHVzIHByb2NoZT8"}
262
 ===
286
 ===
263
 200
287
 200
264
 Content-Type: application/json
288
 Content-Type: application/json
270
 ===
294
 ===
271
 200
295
 200
272
 Content-Type: application/json
296
 Content-Type: application/json
273
-Content-Length: 305
297
+Content-Length: 334
274
 
298
 
275
-{"id":1,"integer":3,"bigint":3,"varchar":"c","decimal":"3.0000","float":3,"double":3,"boolean":true,"date":"2000-01-03","time":"03:01:01","timestamp":"2000-01-03 03:01:01","clob":"HLnVf8PrxX\/vPMElDxrlFLIHoBd1fnLVSXVEDLjt0bk=","blob":"T8O5IGVzdCBsZSBjYWbDqSBsZSBwbHVzIHByb2NoZT8=","geometry":"POINT(3 3)"}
299
+{"id":1,"integer":2,"bigint":3,"varchar":"abc","decimal":"1.2300","float":1,"double":23.45,"boolean":true,"date":"1970-01-01","time":"00:00:01","timestamp":"2001-02-03 04:05:06","clob":"𠜎𠜱𠝹𠱓𠱸𠲖𠳏𠳕𠴕𠵼𠵿𠸎𠸏𠹷𠺝𠺢𠻗","blob":"T8O5IGVzdCBsZSBjYWbDqSBsZSBwbHVzIHByb2NoZT8=","geometry":"POINT(1 2)"}
276
 ===
300
 ===
277
 DELETE /columns/types
301
 DELETE /columns/types
278
 ===
302
 ===

Loading…
Cancel
Save