Browse Source

refactor types validation #648

Maurits van der Schee 4 years ago
parent
commit
b455d66ca8

+ 42
- 45
README.md View File

@@ -923,39 +923,36 @@ You can parse this output to make form fields show up with a red border and thei
923 923
 #### Validation types
924 924
 
925 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 943
 By default, all types are enabled. Which is equivalent to the two configuration possibilities:
943 944
 
944 945
     'validation.types' => 'all',
945 946
     
946 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 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 957
 ### Multi-tenancy support
961 958
 
@@ -1211,10 +1208,10 @@ To run the docker tests run "build_all.sh" and "run_all.sh" from the docker dire
1211 1208
     [3/4] Starting SQLServer 2017 ... skipped
1212 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 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 1216
     Debian 10 (PHP 7.3)
1220 1217
     ================================================
@@ -1223,10 +1220,10 @@ To run the docker tests run "build_all.sh" and "run_all.sh" from the docker dire
1223 1220
     [3/4] Starting SQLServer 2017 ... skipped
1224 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 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 1228
     Debian 9 (PHP 7.0)
1232 1229
     ================================================
@@ -1235,10 +1232,10 @@ To run the docker tests run "build_all.sh" and "run_all.sh" from the docker dire
1235 1232
     [3/4] Starting SQLServer 2017 ... skipped
1236 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 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 1240
     Ubuntu 16.04 (PHP 7.0)
1244 1241
     ================================================
@@ -1247,9 +1244,9 @@ To run the docker tests run "build_all.sh" and "run_all.sh" from the docker dire
1247 1244
     [3/4] Starting SQLServer 2017 ... done
1248 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 1250
     sqlite: skipped, driver not loaded
1254 1251
     ================================================
1255 1252
     Ubuntu 18.04 (PHP 7.2)
@@ -1259,10 +1256,10 @@ To run the docker tests run "build_all.sh" and "run_all.sh" from the docker dire
1259 1256
     [3/4] Starting SQLServer 2017 ... skipped
1260 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 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 1264
     Ubuntu 20.04 (PHP 7.3)
1268 1265
     ================================================
@@ -1271,10 +1268,10 @@ To run the docker tests run "build_all.sh" and "run_all.sh" from the docker dire
1271 1268
     [3/4] Starting SQLServer 2017 ... skipped
1272 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 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 1276
 The above test run (including starting up the databases) takes less than 5 minutes on my slow laptop.
1280 1277
 
@@ -1294,10 +1291,10 @@ The above test run (including starting up the databases) takes less than 5 minut
1294 1291
     [3/4] Starting SQLServer 2017 ... skipped
1295 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 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 1298
     root@b7ab9472e08f:/php-crud-api# 
1302 1299
 
1303 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,33 +8220,25 @@ namespace Tqdev\PhpCrudApi\Middleware {
8220 8220
     use Psr\Http\Server\RequestHandlerInterface;
8221 8221
     use Tqdev\PhpCrudApi\Column\ReflectionService;
8222 8222
     use Tqdev\PhpCrudApi\Column\Reflection\ReflectedTable;
8223
+    use Tqdev\PhpCrudApi\Column\Reflection\ReflectedColumn;
8223 8224
     use Tqdev\PhpCrudApi\Controller\Responder;
8224 8225
     use Tqdev\PhpCrudApi\Middleware\Base\Middleware;
8225 8226
     use Tqdev\PhpCrudApi\Middleware\Router\Router;
8226 8227
     use Tqdev\PhpCrudApi\Record\ErrorCode;
8227 8228
     use Tqdev\PhpCrudApi\RequestUtils;
8228 8229
 
8229
-    class ValidationMiddleware extends Middleware {
8230
+    class ValidationMiddleware extends Middleware
8231
+    {
8230 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 8236
     		parent::__construct($router, $responder, $properties);
8235 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 8242
     		$context = (array) $record;
8251 8243
     		$details = array();
8252 8244
     		$tableName = $table->getName();
@@ -8254,8 +8246,8 @@ namespace Tqdev\PhpCrudApi\Middleware {
8254 8246
     			if ($table->hasColumn($columnName)) {
8255 8247
     				$column = $table->getColumn($columnName);
8256 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 8252
     				if ($valid !== true && $valid !== '') {
8261 8253
     					$details[$columnName] = $valid;
@@ -8268,145 +8260,154 @@ namespace Tqdev\PhpCrudApi\Middleware {
8268 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 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 8411
     		$operation = RequestUtils::getOperation($request);
8411 8412
     		if (in_array($operation, ['create', 'update', 'increment'])) {
8412 8413
     			$tableName = RequestUtils::getPathSegment($request, 2);

+ 1
- 1
docker/centos8/Dockerfile View File

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

+ 1
- 1
docker/debian10/Dockerfile View File

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

+ 1
- 1
docker/debian9/Dockerfile View File

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

+ 1
- 1
docker/ubuntu16/Dockerfile View File

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

+ 1
- 4
docker/ubuntu18/Dockerfile View File

@@ -2,12 +2,9 @@ FROM ubuntu:18.04
2 2
 
3 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 5
 # install: php / mysql / postgres / sqlite / tools
9 6
 RUN apt-get update && apt-get -y install \
10
-php-cli php-xml \
7
+php-cli php-xml php-mbstring \
11 8
 mysql-server mysql-client php-mysql \
12 9
 postgresql php-pgsql \
13 10
 postgresql-10-postgis-2.4 \

+ 1
- 4
docker/ubuntu20/Dockerfile View File

@@ -2,12 +2,9 @@ FROM ubuntu:20.04
2 2
 
3 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 5
 # install: php / mysql / postgres / sqlite / tools
9 6
 RUN apt-get update && apt-get -y install \
10
-php-cli php-xml \
7
+php-cli php-xml php-mbstring \
11 8
 mysql-server mysql-client php-mysql \
12 9
 postgresql php-pgsql \
13 10
 postgresql-12-postgis-3 \

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

@@ -7,33 +7,25 @@ use Psr\Http\Message\ServerRequestInterface;
7 7
 use Psr\Http\Server\RequestHandlerInterface;
8 8
 use Tqdev\PhpCrudApi\Column\ReflectionService;
9 9
 use Tqdev\PhpCrudApi\Column\Reflection\ReflectedTable;
10
+use Tqdev\PhpCrudApi\Column\Reflection\ReflectedColumn;
10 11
 use Tqdev\PhpCrudApi\Controller\Responder;
11 12
 use Tqdev\PhpCrudApi\Middleware\Base\Middleware;
12 13
 use Tqdev\PhpCrudApi\Middleware\Router\Router;
13 14
 use Tqdev\PhpCrudApi\Record\ErrorCode;
14 15
 use Tqdev\PhpCrudApi\RequestUtils;
15 16
 
16
-class ValidationMiddleware extends Middleware {
17
+class ValidationMiddleware extends Middleware
18
+{
17 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 23
 		parent::__construct($router, $responder, $properties);
22 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 29
 		$context = (array) $record;
38 30
 		$details = array();
39 31
 		$tableName = $table->getName();
@@ -41,8 +33,8 @@ class ValidationMiddleware extends Middleware {
41 33
 			if ($table->hasColumn($columnName)) {
42 34
 				$column = $table->getColumn($columnName);
43 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 39
 				if ($valid !== true && $valid !== '') {
48 40
 					$details[$columnName] = $valid;
@@ -55,145 +47,154 @@ class ValidationMiddleware extends Middleware {
55 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 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 198
 		$operation = RequestUtils::getOperation($request);
198 199
 		if (in_array($operation, ['create', 'update', 'increment'])) {
199 200
 			$tableName = RequestUtils::getPathSegment($request, 2);

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

@@ -20,6 +20,17 @@ Content-Length: 563
20 20
 POST /records/types
21 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 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 36
 200

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

@@ -12,7 +12,7 @@ true
12 12
 POST /records/types
13 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 17
 200
18 18
 Content-Type: application/json
@@ -22,59 +22,63 @@ Content-Length: 1
22 22
 ===
23 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 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 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 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 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 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 63
 PUT /records/types/1
60 64
 
61
-{"integer":"string"}
65
+{"integer":"12345678901"}
62 66
 ===
63 67
 422
64 68
 Content-Type: application/json
65 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 73
 PUT /records/types/1
70 74
 
71
-{"bigint":"string"}
75
+{"bigint":"12345678901234567890"}
72 76
 ===
73 77
 422
74 78
 Content-Type: application/json
75 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 83
 PUT /records/types/1
80 84
 
@@ -82,159 +86,169 @@ PUT /records/types/1
82 86
 ===
83 87
 422
84 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 93
 PUT /records/types/1
90 94
 
91
-{"decimal":"string"}
95
+{"decimal":"12.23.34"}
92 96
 ===
93 97
 422
94 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 103
 PUT /records/types/1
100 104
 
101
-{"float":"string"}
105
+{"decimal":1131313145345}
102 106
 ===
103 107
 422
104 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 113
 PUT /records/types/1
110 114
 
111
-{"double":"string"}
115
+{"decimal":"1234567.123"}
112 116
 ===
113 117
 422
114 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 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 137
 422
124 138
 Content-Type: application/json
125 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 143
 PUT /records/types/1
130 144
 
131
-{"boolean":null}
145
+{"decimal":113131.3145345}
132 146
 ===
133 147
 422
134 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 153
 PUT /records/types/1
140 154
 
141
-{"date":"string"}
155
+{"decimal":11313131E-5}
142 156
 ===
143 157
 422
144 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 163
 PUT /records/types/1
150 164
 
151
-{"date":"still-no-date"}
165
+{"float":"string"}
152 166
 ===
153 167
 422
154 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 173
 PUT /records/types/1
160 174
 
161
-{"time":"string"}
175
+{"double":"string"}
162 176
 ===
163 177
 422
164 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 183
 PUT /records/types/1
170 184
 
171
-{"time":"still:no:time"}
185
+{"boolean":-1}
172 186
 ===
173 187
 422
174 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 193
 PUT /records/types/1
180 194
 
181
-{"time":"999:999:999"}
195
+{"date":"string"}
182 196
 ===
183 197
 422
184 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 203
 PUT /records/types/1
190 204
 
191
-{"timestamp":"string"}
205
+{"date":"still-no-date"}
192 206
 ===
193 207
 422
194 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 213
 PUT /records/types/1
200 214
 
201
-{"timestamp":"string 01:01:01"}
215
+{"time":"string"}
202 216
 ===
203 217
 422
204 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 223
 PUT /records/types/1
210 224
 
211
-{"timestamp":"still-no-date 01:01:01"}
225
+{"time":"still:no:time"}
212 226
 ===
213 227
 422
214 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 233
 PUT /records/types/1
220 234
 
221
-{"timestamp":"2001-01-01 string"}
235
+{"time":"999:999:999"}
222 236
 ===
223 237
 422
224 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 243
 PUT /records/types/1
230 244
 
231
-{"timestamp":"2001-01-01 still:no:time"}
245
+{"timestamp":"string"}
232 246
 ===
233 247
 422
234 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 253
 PUT /records/types/1
240 254
 
@@ -242,13 +256,13 @@ PUT /records/types/1
242 256
 ===
243 257
 422
244 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 263
 PUT /records/types/1
250 264
 
251
-{"clob":"HLnVf8PrxX/vPMElDxrlFLIHoBd1fnLVSXVEDLjt0bk="}
265
+{"clob":"𠜎𠜱𠝹𠱓𠱸𠲖𠳏𠳕𠴕𠵼𠵿𠸎𠸏𠹷𠺝𠺢𠻗"}
252 266
 ===
253 267
 200
254 268
 Content-Type: application/json
@@ -258,7 +272,17 @@ Content-Length: 1
258 272
 ===
259 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 287
 200
264 288
 Content-Type: application/json
@@ -270,9 +294,9 @@ GET /records/types/1
270 294
 ===
271 295
 200
272 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 301
 DELETE /columns/types
278 302
 ===

Loading…
Cancel
Save