diff --git a/doxygen/lang/900_release_notes.dox.tmpl b/doxygen/lang/900_release_notes.dox.tmpl index 8f944ce823..529355b747 100644 --- a/doxygen/lang/900_release_notes.dox.tmpl +++ b/doxygen/lang/900_release_notes.dox.tmpl @@ -20,6 +20,9 @@ - fixed a bug in documentation post-processing for @ref hashdecl "hashdecl" declarations (issue 2298) - RestHandler module fixes: - updated to return a 400 Bad Request error when REST schema validation fails on messages received issue 2344) + - RestSchemaValidator module fixes: + - updated docs for @ref RestSchemaValidator::AbstractRestSchemaValidator::parseRequest() "AbstractRestSchemaValidator::parseRequest()" to reflect how validation exceptions should be raised for proper error reporting (issue 2344) + - fixed handling of messages with non-object (i.e. non-hash) bodies (issue 2366) - SqlUtil module changes - implemented support for custom column operators (issue 2314) - Swagger module fixes: @@ -27,6 +30,9 @@ - fixed example value for binary type (issue 2342) - fixed serialization of date/time values (issue 2349) - updated to return a 400 Bad Request error when REST schema validation fails on messages received issue 2344) + - fixed handling of non-string enum types (issue 2364) + - fixed confusing error messages with invalid parameter types (issue 2365) + - fixed handling of messages with non-object (i.e. non-hash) bodies (issue 2366) - added missing comparison methods in the QUnit module (issue 1588): - \c Test::assertRegex() - \c Test::assertNRegex() diff --git a/examples/test/qlib/Swagger/Swagger.qtest b/examples/test/qlib/Swagger/Swagger.qtest index 11562fd1b7..8267068cd3 100755 --- a/examples/test/qlib/Swagger/Swagger.qtest +++ b/examples/test/qlib/Swagger/Swagger.qtest @@ -97,6 +97,9 @@ public class SwaggerTest inherits QUnit::Test { const ValidPathsObject = { "/api/smtg": { "parameters": ( {"$ref": "#/parameters/a"}, ) + }, + "/api/enum": { + "parameters": ( {"$ref": "#/parameters/enum"}, ) } }; const ValidSwaggerSchema = { @@ -111,6 +114,16 @@ public class SwaggerTest inherits QUnit::Test { "required": False, "type": "string", ), + "enum": ( + "name": "enum", + "in": "body", + "description": "desc", + "required": False, + "schema": ( + "type": "integer", + "enum": (1, 2, 3), + ), + ), ), }; @@ -713,7 +726,7 @@ public class SwaggerTest inherits QUnit::Test { p = {}; assertTrue(loadyaml(my_make_yaml(base + {"paths": p}))); - p = {"a": {}}; + p = base.parameters + {"a": {}}; assertThrows("INVALID-FIELD-FORMAT", loadyaml, my_make_yaml(base + {"paths": p})); } @@ -879,149 +892,149 @@ public class SwaggerTest inherits QUnit::Test { hash p; # name and in checks - p = {"a": {"name": "a", "in": "query", "type": "string"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "string"}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"in": "query", "type": "string"}}; + p = base.parameters + {"a": {"in": "query", "type": "string"}}; assertThrows("REQUIRED-FIELD-MISSING", "\"name\".*missing", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": {"name": "a", "type": "string"}}; + p = base.parameters + {"a": {"name": "a", "type": "string"}}; assertThrows("REQUIRED-FIELD-MISSING", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": {"name": 5, "in": "query", "type": "string"}}; + p = base.parameters + {"a": {"name": 5, "in": "query", "type": "string"}}; assertThrows("INVALID-FIELD-TYPE", "name", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": {"name": "a", "in": 5, "type": "string"}}; + p = base.parameters + {"a": {"name": "a", "in": 5, "type": "string"}}; assertThrows("INVALID-FIELD-VALUE", "in", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": {"name": "a", "in": "a", "type": "string"}}; + p = base.parameters + {"a": {"name": "a", "in": "a", "type": "string"}}; assertThrows("INVALID-FIELD-VALUE", "in", loadyaml, my_make_yaml(base + {"parameters": p})); # description checks - p = {"a": {"name": "a", "in": "query", "type": "string", "description": 5}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "string", "description": 5}}; assertThrows("INVALID-FIELD-TYPE", "description", loadyaml, my_make_yaml(base + {"parameters": p})); # TODO - body location parameter - have to do SchemaObject first # in (location) checks - p = {"a": {"name": "a", "in": "formData", "type": "string"}}; + p = base.parameters + {"a": {"name": "a", "in": "formData", "type": "string"}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "header", "type": "string"}}; + p = base.parameters + {"a": {"name": "a", "in": "header", "type": "string"}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "string"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "string"}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); # "required" checks - p = {"a": {"name": "a", "in": "path", "type": "string", "required": False}}; + p = base.parameters + {"a": {"name": "a", "in": "path", "type": "string", "required": False}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "path", "type": "string"}}; + p = base.parameters + {"a": {"name": "a", "in": "path", "type": "string"}}; assertThrows("REQUIRED-FIELD-MISSING", "\"required\".*missing", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": {"name": "a", "in": "path", "type": "string", "required": "a"}}; + p = base.parameters + {"a": {"name": "a", "in": "path", "type": "string", "required": "a"}}; assertThrows("INVALID-FIELD-TYPE", "required", loadyaml, my_make_yaml(base + {"parameters": p})); # type checks - p = {"a": {"name": "a", "in": "query"}}; + p = base.parameters + {"a": {"name": "a", "in": "query"}}; assertThrows("REQUIRED-FIELD-MISSING", "\"type\".*missing", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": {"name": "a", "in": "query", "type": 5}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": 5}}; assertThrows("INVALID-FIELD-TYPE", "\"type\"", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": {"name": "a", "in": "query", "type": "a"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "a"}}; assertThrows("INVALID-FIELD-VALUE", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": {"name": "a", "in": "query", "type": "array", "items": {"type": "string"}}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "array", "items": {"type": "string"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "boolean"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "boolean"}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "file"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "file"}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "integer"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "integer"}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "number"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "number"}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "string"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "string"}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); # format checks - p = {"a": {"name": "a", "in": "query", "type": "string", "format": 5}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "string", "format": 5}}; assertThrows("INVALID-FIELD-TYPE", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": {"name": "a", "in": "query", "type": "integer", "format": "int32"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "integer", "format": "int32"}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "integer", "format": "int64"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "integer", "format": "int64"}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "integer", "format": "a"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "integer", "format": "a"}}; assertThrows("INVALID-FIELD-VALUE", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": {"name": "a", "in": "query", "type": "number", "format": "double"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "number", "format": "double"}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "number", "format": "float"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "number", "format": "float"}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "number", "format": "a"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "number", "format": "a"}}; assertThrows("INVALID-FIELD-VALUE", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": {"name": "a", "in": "query", "type": "string", "format": "binary"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "string", "format": "binary"}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "string", "format": "byte"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "string", "format": "byte"}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "string", "format": "date"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "string", "format": "date"}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "string", "format": "date-time"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "string", "format": "date-time"}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "string", "format": "password"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "string", "format": "password"}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "string", "format": "a"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "string", "format": "a"}}; assertThrows("INVALID-FIELD-VALUE", loadyaml, my_make_yaml(base + {"parameters": p})); # allowEmptyValue checks - p = {"a": {"name": "a", "in": "query", "type": "string", "allowEmptyValue": 5}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "string", "allowEmptyValue": 5}}; assertThrows("INVALID-FIELD-TYPE", "allowEmptyValue", loadyaml, my_make_yaml(base + {"parameters": p})); # collectionFormat checks - p = {"a": {"name": "a", "in": "query", "type": "array", "collectionFormat": "csv", "items": {"type": "string"}}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "array", "collectionFormat": "csv", "items": {"type": "string"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "array", "collectionFormat": "ssv", "items": {"type": "string"}}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "array", "collectionFormat": "ssv", "items": {"type": "string"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "array", "collectionFormat": "tsv", "items": {"type": "string"}}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "array", "collectionFormat": "tsv", "items": {"type": "string"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "array", "collectionFormat": "pipes", "items": {"type": "string"}}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "array", "collectionFormat": "pipes", "items": {"type": "string"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "array", "collectionFormat": "multi", "items": {"type": "string"}}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "array", "collectionFormat": "multi", "items": {"type": "string"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "array", "collectionFormat": 5, "items": {"type": "string"}}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "array", "collectionFormat": 5, "items": {"type": "string"}}}; assertThrows("INVALID-FIELD-TYPE", "collectionFormat", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": {"name": "a", "in": "query", "type": "array", "collectionFormat": "a", "items": {"type": "string"}}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "array", "collectionFormat": "a", "items": {"type": "string"}}}; assertThrows("INVALID-FIELD-VALUE", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": {"name": "a", "in": "header", "type": "array", "collectionFormat": "multi", "items": {"type": "string"}}}; + p = base.parameters + {"a": {"name": "a", "in": "header", "type": "array", "collectionFormat": "multi", "items": {"type": "string"}}}; assertThrows("INVALID-FIELD-VALUE", loadyaml, my_make_yaml(base + {"parameters": p})); # default checks # TODO - /*p = {"a": {"name": "a", "in": "query", "type": "array", "items": {"type": "string"}}}; + /*p = base.parameters + {"a": {"name": "a", "in": "query", "type": "array", "items": {"type": "string"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "array", "items": {"type": "string"}, "default": 5}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "array", "items": {"type": "string"}, "default": 5}}; assertThrows("INVALID-FIELD-TYPE", "default", loadyaml, my_make_yaml(base + {"parameters": p}));*/ - p = {"a": {"name": "a", "in": "query", "type": "boolean", "default": True}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "boolean", "default": True}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "boolean", "default": 5}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "boolean", "default": 5}}; assertThrows("INVALID-FIELD-TYPE", "default", loadyaml, my_make_yaml(base + {"parameters": p})); # TODO - /*p = {"a": {"name": "a", "in": "query", "type": "file"}}; + /*p = base.parameters + {"a": {"name": "a", "in": "query", "type": "file"}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "file"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "file"}}; assertThrows("INVALID-FIELD-TYPE", "default", loadyaml, my_make_yaml(base + {"parameters": p}));*/ - p = {"a": {"name": "a", "in": "query", "type": "integer", "default": 5}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "integer", "default": 5}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "integer", "default": "a"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "integer", "default": "a"}}; assertThrows("INVALID-FIELD-TYPE", "default", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": {"name": "a", "in": "query", "type": "number", "default": 2.0}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "number", "default": 2.0}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "number", "default": "a"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "number", "default": "a"}}; assertThrows("INVALID-FIELD-TYPE", "default", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": {"name": "a", "in": "query", "type": "string", "default": "a"}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "string", "default": "a"}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "string", "default": 5}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "string", "default": 5}}; assertThrows("INVALID-FIELD-TYPE", "default", loadyaml, my_make_yaml(base + {"parameters": p})); } @@ -1033,97 +1046,97 @@ public class SwaggerTest inherits QUnit::Test { hash p; # type checks - p = {"a": paramBase + {"items": {}}}; + p = base.parameters + {"a": paramBase + {"items": {}}}; assertThrows("REQUIRED-FIELD-MISSING", "\"type\".*missing", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": paramBase + {"items": {"type": 5}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": 5}}}; assertThrows("INVALID-FIELD-TYPE", "\"type\"", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": paramBase + {"items": {"type": "a"}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "a"}}}; assertThrows("INVALID-FIELD-VALUE", loadyaml, my_make_yaml(base + {"parameters": p})); # TODO array of array - /*p = {"a": {"name":"a", "in": "query", "type": "array", "items": {"type": "string"}}}; + /*p = base.parameters + {"a": {"name":"a", "in": "query", "type": "array", "items": {"type": "string"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p})));*/ - p = {"a": paramBase + {"items": {"type": "boolean"}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "boolean"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": paramBase + {"items": {"type": "integer"}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "integer"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": paramBase + {"items": {"type": "number"}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "number"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": paramBase + {"items": {"type": "string"}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "string"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); # format checks - p = {"a": paramBase + {"items": {"type": "string", "format": 5}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "string", "format": 5}}}; assertThrows("INVALID-FIELD-TYPE", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": paramBase + {"items": {"type": "integer", "format": "int32"}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "integer", "format": "int32"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": paramBase + {"items": {"type": "integer", "format": "int64"}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "integer", "format": "int64"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": paramBase + {"items": {"type": "integer", "format": "a"}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "integer", "format": "a"}}}; assertThrows("INVALID-FIELD-VALUE", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": paramBase + {"items": {"type": "number", "format": "double"}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "number", "format": "double"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": paramBase + {"items": {"type": "number", "format": "float"}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "number", "format": "float"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": paramBase + {"items": {"type": "number", "format": "a"}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "number", "format": "a"}}}; assertThrows("INVALID-FIELD-VALUE", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": paramBase + {"items": {"type": "string", "format": "binary"}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "string", "format": "binary"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": paramBase + {"items": {"type": "string", "format": "byte"}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "string", "format": "byte"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": paramBase + {"items": {"type": "string", "format": "date"}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "string", "format": "date"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": paramBase + {"items": {"type": "string", "format": "date-time"}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "string", "format": "date-time"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": paramBase + {"items": {"type": "string", "format": "password"}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "string", "format": "password"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": paramBase + {"items": {"type": "string", "format": "a"}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "string", "format": "a"}}}; assertThrows("INVALID-FIELD-VALUE", loadyaml, my_make_yaml(base + {"parameters": p})); # collectionFormat checks - p = {"a": paramBase + {"items": {"type": "array", "collectionFormat": "csv", "items": {"type": "string"}}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "array", "collectionFormat": "csv", "items": {"type": "string"}}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": paramBase + {"items": {"type": "array", "collectionFormat": "ssv", "items": {"type": "string"}}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "array", "collectionFormat": "ssv", "items": {"type": "string"}}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": paramBase + {"items": {"type": "array", "collectionFormat": "tsv", "items": {"type": "string"}}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "array", "collectionFormat": "tsv", "items": {"type": "string"}}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": paramBase + {"items": {"type": "array", "collectionFormat": "pipes", "items": {"type": "string"}}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "array", "collectionFormat": "pipes", "items": {"type": "string"}}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": paramBase + {"items": {"type": "array", "collectionFormat": 5, "items": {"type": "string"}}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "array", "collectionFormat": 5, "items": {"type": "string"}}}}; assertThrows("INVALID-FIELD-TYPE", "collectionFormat", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": paramBase + {"items": {"type": "array", "collectionFormat": "a", "items": {"type": "string"}}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "array", "collectionFormat": "a", "items": {"type": "string"}}}}; assertThrows("INVALID-FIELD-VALUE", "collectionFormat", loadyaml, my_make_yaml(base + {"parameters": p})); # default checks # TODO array of array - /*p = {"a": {"name": "a", "in": "query", "type": "array", "items": {"type": "string"}}}; + /*p = base.parameters + {"a": {"name": "a", "in": "query", "type": "array", "items": {"type": "string"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": {"name": "a", "in": "query", "type": "array", "items": {"type": "string"}, "default": 5}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "array", "items": {"type": "string"}, "default": 5}}; assertThrows("INVALID-FIELD-TYPE", "default", loadyaml, my_make_yaml(base + {"parameters": p}));*/ - p = {"a": paramBase + {"items": {"type": "boolean", "default": True}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "boolean", "default": True}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": paramBase + {"items": {"type": "boolean", "default": 5}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "boolean", "default": 5}}}; assertThrows("INVALID-FIELD-TYPE", "default", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": paramBase + {"items": {"type": "integer", "default": 5}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "integer", "default": 5}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": paramBase + {"items": {"type": "integer", "default": "a"}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "integer", "default": "a"}}}; assertThrows("INVALID-FIELD-TYPE", "default", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": paramBase + {"items": {"type": "number", "default": 2.0}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "number", "default": 2.0}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": paramBase + {"items": {"type": "number", "default": "a"}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "number", "default": "a"}}}; assertThrows("INVALID-FIELD-TYPE", "default", loadyaml, my_make_yaml(base + {"parameters": p})); - p = {"a": paramBase + {"items": {"type": "string", "default": "a"}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "string", "default": "a"}}}; assertTrue(loadyaml(my_make_yaml(base + {"parameters": p}))); - p = {"a": paramBase + {"items": {"type": "string", "default": 5}}}; + p = base.parameters + {"a": paramBase + {"items": {"type": "string", "default": 5}}}; assertThrows("INVALID-FIELD-TYPE", "default", loadyaml, my_make_yaml(base + {"parameters": p})); } @@ -1246,9 +1259,9 @@ public class SwaggerTest inherits QUnit::Test { # default checks # TODO array of array - /*p = {"a": {"name": "a", "in": "query", "type": "array", "items": {"type": "string"}}}; + /*p = base.parameters + {"a": {"name": "a", "in": "query", "type": "array", "items": {"type": "string"}}}; assertTrue(loadyaml(my_make_yaml(base + {"responses": r}))); - p = {"a": {"name": "a", "in": "query", "type": "array", "items": {"type": "string"}, "default": 5}}; + p = base.parameters + {"a": {"name": "a", "in": "query", "type": "array", "items": {"type": "string"}, "default": 5}}; assertThrows("INVALID-FIELD-TYPE", "default", loadyaml, my_make_yaml(base + {"responses": r}));*/ r = {"a": {"description": "a", "headers": {"a": {"type": "boolean", "default": True}}}}; @@ -1320,7 +1333,7 @@ public class SwaggerTest inherits QUnit::Test { assertThrows("INVALID-FIELD-VALUE", loadyaml, my_make_yaml(base + {"definitions": {"a": s}})); # TODO array of array - /*p = {"a": {"name":"a", "in": "query", "type": "array", "items": {"type": "string"}}}; + /*p = base.parameters + {"a": {"name":"a", "in": "query", "type": "array", "items": {"type": "string"}}}; assertTrue(loadyaml(my_make_yaml(base + {"definitions": {"a": s}})));*/ s = {"type": "boolean"}; assertTrue(loadyaml(my_make_yaml(base + {"definitions": {"a": s}}))); @@ -1723,6 +1736,26 @@ public class SwaggerTest inherits QUnit::Test { MimeTypeYamlRpc, ), ), + "put": ( + "parameters": (( + "$ref": "#/parameters/enum", + ),), + "responses": ( + "200": ( + "description": "successful response", + ), + ), + "consumes": ( + MimeTypeJson, + MimeTypeYamlRpc, + MimeTypeFormUrlEncoded, + MimeTypeMultipartFormData, + ), + "produces": ( + MimeTypeJson, + MimeTypeYamlRpc, + ), + ), ); remove b.parameters.a.type; @@ -1752,6 +1785,9 @@ public class SwaggerTest inherits QUnit::Test { assertThrows("SCHEMA-VALIDATION-ERROR", \so.parseRequest(), ("get", h.uri_path, h.body, \hdr)); } + # test for issue #2365 + assertThrows("SCHEMA-VALIDATION-ERROR", "Parameter \"a\".*invalid type", \so.processRequest(), ("get", "/api/smtg", ("a": 1))); + assertThrows("SERIALIZATION-ERROR", "application/octet-stream", \so.processRequest(), ("get", "/api/smtg", ("a": "a"), NOTHING, MimeTypeOctetStream)); h = so.processRequest("get", "/api/smtg", ("a": "a"), NOTHING, MimeTypeMultipartFormData); @@ -1766,6 +1802,14 @@ public class SwaggerTest inherits QUnit::Test { assertEq("/api/smtg", sh.path); assertEq("a", sh.body.a); } + + # issue #2364 enum check + h = so.processRequest("put", "/api/smtg", 1); + #printf("h: %y\n", h); + assertEq("/api/smtg", h.uri_path); + assertEq(Type::String, h.body.type()); + + assertThrows("SCHEMA-VALIDATION-ERROR", "enum.*values", \so.processRequest(), ("put", "/api/smtg", 4)); } { @@ -1922,7 +1966,7 @@ public class SwaggerTest inherits QUnit::Test { assertEq(MimeTypeJson, res.hdr."Content-Type"); body.shipDate = "1990-01-01"; - assertThrows("SCHEMA-VALIDATION-ERROR", "body.*date-time", \so.processResponse(), ("post", "/store/order", 200, body, NOTHING, MimeTypeJson)); + assertThrows("SCHEMA-VALIDATION-ERROR", "shipDate.*date-time", \so.processResponse(), ("post", "/store/order", 200, body, NOTHING, MimeTypeJson)); } } diff --git a/qlib/RestSchemaValidator.qm b/qlib/RestSchemaValidator.qm index a068f0c15f..b07d1814c1 100644 --- a/qlib/RestSchemaValidator.qm +++ b/qlib/RestSchemaValidator.qm @@ -73,6 +73,7 @@ module RestSchemaValidator { @subsection restschemavalidator_1_0_1 RestSchemaValidator v1.0.1 - updated docs for @ref RestSchemaValidator::AbstractRestSchemaValidator::parseRequest() "AbstractRestSchemaValidator::parseRequest()" to reflect how validation exceptions should be raised for proper error reporting (issue 2344) + - fixed handling of messages with non-object (i.e. non-hash) bodies (issue 2366) @subsection restschemavalidator_1_0 RestSchemaValidator v1.0 @@ -98,7 +99,7 @@ public namespace RestSchemaValidator { #! any query arguments *hash query; #! the deserialized message body data - any body; + auto body; #! miscellaneous free-form info about the parsed request hash info; } @@ -108,7 +109,7 @@ public namespace RestSchemaValidator { #! the HTTP status code int code; #! the deserialized message body - any body; + auto body; #! the HTTP headers received hash hdr; #! miscellaneous free-form info about the parsed response @@ -168,7 +169,7 @@ public namespace RestSchemaValidator { @throws SERIALIZATION-ERROR unsupported message body MIME type requested or missing serialization module */ - hash processRequest(string method, string path, any body, *hash headers, *softlist content_types) { + hash processRequest(string method, string path, auto body, *hash headers, *softlist content_types) { return processRequestImpl(method, path, body, headers, content_types); } @@ -204,7 +205,7 @@ public namespace RestSchemaValidator { @note processResponse(string method, string path, int code, any response_body, *hash headers, *softlist content_types) { + hash processResponse(string method, string path, int code, auto response_body, *hash headers, *softlist content_types) { return processResponseImpl(method, path, code, response_body, headers, content_types); } @@ -309,7 +310,7 @@ public namespace RestSchemaValidator { @throws SERIALIZATION-ERROR unsupported message body MIME type requested or missing serialization module */ - private abstract hash processRequestImpl(string method, string path, any body, *hash headers, *softlist content_types); + private abstract hash processRequestImpl(string method, string path, auto body, *hash headers, *softlist content_types); #! processes and parses a client request and returns the deserialized message body (if any) /** @param method the HTTP method (case-insensitive) @@ -448,7 +449,7 @@ public namespace RestSchemaValidator { const DeserializeXml = ( "code": "xml", "arg": True, - "in": any sub (string xml, reference type) { + "in": auto sub (string xml, reference type) { try { on_success type = "xml"; return parse_xmlrpc_value(xml); @@ -514,7 +515,7 @@ public namespace RestSchemaValidator { @throws SERIALIZATION-ERROR unsupported message body MIME type requested or missing serialization module */ - private hash processRequestImpl(string method, string path, any body, *hash headers, *softlist content_types) { + private hash processRequestImpl(string method, string path, auto body, *hash headers, *softlist content_types) { string ct = content_types[0] ?? DataSerializationSupportList[0]; return cast>(( "uri_path": path, @@ -578,7 +579,7 @@ public namespace RestSchemaValidator { @note processResponseImpl(string method, string path, int code, any response_body, *hash headers, *softlist content_types) { + private hash processResponseImpl(string method, string path, int code, auto response_body, *hash headers, *softlist content_types) { if (exists response_body) { string ct; if (!content_types) diff --git a/qlib/Swagger.qm b/qlib/Swagger.qm index 64f41d9db4..a61a44eb14 100644 --- a/qlib/Swagger.qm +++ b/qlib/Swagger.qm @@ -156,6 +156,9 @@ RestHandler handler(NOTHING, swagger); - fixed handling of string type date and date-time formats (issue 2341) - fixed example value for binary type (issue 2342) - fixed serialization of date/time values (issue 2349) + - fixed handling of non-string enum types (issue 2364) + - fixed confusing error messages with invalid parameter types (issue 2365) + - fixed handling of messages with non-object (i.e. non-hash) bodies (issue 2366) @subsection swagger_1_0 Swagger v1.0 @@ -196,7 +199,7 @@ public const MimeDataTypes = { %ifndef NoJson MimeTypeJson: ( "serialize": \make_json(), - "serialize_verbose": string sub (any val) {return make_json(val, JGF_ADD_FORMATTING);}, + "serialize_verbose": string sub (auto val) {return make_json(val, JGF_ADD_FORMATTING);}, "deserialize": \parse_json(), "module": "json", ), @@ -204,7 +207,7 @@ public const MimeDataTypes = { %ifndef NoYaml MimeTypeYamlRpc: ( "serialize": \make_yaml(), - "serialize_verbose": string sub (any val) {return make_yaml(val, BlockStyle);}, + "serialize_verbose": string sub (auto val) {return make_yaml(val, BlockStyle);}, "deserialize": \parse_yaml(), "module": "yaml", ), @@ -324,7 +327,7 @@ class SchemaBase { optional_field(objType, oh, "maxItems", NT_INT, \maxItems); optional_field(objType, oh, "minItems", NT_INT, \minItems); optional_field(objType, oh, "uniqueItems", NT_BOOLEAN, \uniqueItems); - optional_field(objType, oh, "enum", SwaggerListToSet, \enum); + optional_field(objType, oh, "enum", SwaggerListToAnySet, \enum); optional_field(objType, oh, "multipleOf", (NT_FLOAT: True, NT_INT: True), \multipleOf); } @@ -337,9 +340,9 @@ class SchemaBase { } #! returns an example value for the given type - private any getExampleValue(string type, *string format) { + private auto getExampleValue(string type, *string format) { if (enum) { - any rv = enum.firstKey(); + auto rv = enum.firstKey(); switch (type) { case "integer": rv = rv.toInt(); break; case "number": rv = (format && ValidNumberFormatsHash{format} ? rv.toFloat() : rv.toNumber()); break; @@ -618,7 +621,7 @@ class SchemaBase { # hash for checking uniqueness if uniqueItems is true #printf("v: %y self: %N\n", value, self); hash h; - foreach any v in (\value) { + foreach auto v in (\value) { items.check(serialize, request, path, method, name, \v); if (uniqueItems) { if (h{v}) @@ -630,7 +633,7 @@ class SchemaBase { else { # perform slow array uniqueness check list uniqueItems = (); - foreach any val in (\value) { + foreach auto val in (\value) { items.check(serialize, request, path, method, name, \val); if (uniqueItems.contains(val)) throw "SCHEMA-VALIDATION-ERROR", @@ -971,15 +974,15 @@ public class SwaggerSchema inherits ObjectBase, AbstractRestSchemaValidator { } if (oh.schemes) { - optional_field(objType, oh, "schemes", SwaggerListToSet, \schemes); + optional_field(objType, oh, "schemes", SwaggerListToStringSet, \schemes); # check for invalid schemes if ((hash h = (schemes - ValidSchemes))) throw "INVALID-FIELD-VALUE", sprintf("Swagger Object: invalid transfer protocol scheme%s: %y", h.size() == 1 ? "" : "s", keys h); } - optional_field(objType, oh, "consumes", SwaggerListToSet, \consumes); + optional_field(objType, oh, "consumes", SwaggerListToStringSet, \consumes); - optional_field(objType, oh, "produces", SwaggerListToSet, \produces); + optional_field(objType, oh, "produces", SwaggerListToStringSet, \produces); { hash obj; @@ -1003,7 +1006,7 @@ public class SwaggerSchema inherits ObjectBase, AbstractRestSchemaValidator { { list lobj = (); optional_field(objType, oh, "tags", NT_LIST, \lobj); - foreach any val in (lobj) { + foreach auto val in (lobj) { if (val.typeCode() != NT_HASH) throw "INVALID-FIELD-TYPE", "Swagger Object: invalid tag datatype: " + val.type(); tags += new TagObject(val); @@ -1113,7 +1116,7 @@ public class SwaggerSchema inherits ObjectBase, AbstractRestSchemaValidator { @throws SERIALIZATION-ERROR unsupported message body MIME type requested or missing serialization module */ - private hash processRequestImpl(string method, string path, any body, *hash headers, *softlist content_types) { + private hash processRequestImpl(string method, string path, auto body, *hash headers, *softlist content_types) { return processRequestIntern(method, path, body, headers, content_types, compact_serialization); } @@ -1140,7 +1143,7 @@ public class SwaggerSchema inherits ObjectBase, AbstractRestSchemaValidator { if (!ds) throw "DESERIALIZATION-ERROR", sprintf("%s %s: cannot deserialize message body with Content-Type: %y; supported MIME types: %y; available deserialization modules: %y", method.upr(), path, headers."content-type", MimeContentTypes, SerializationModules); - any any_body = ds(http_body); + auto any_body = ds(http_body); if (exists any_body) { if (any_body.typeCode() != NT_HASH) throw "DESERIALIZATION-ERROR", sprintf("%s %s: message body serialized to %y; expecting \"hash\"", method.upr(), path, any_body.type()); @@ -1186,7 +1189,7 @@ public class SwaggerSchema inherits ObjectBase, AbstractRestSchemaValidator { @note processResponseImpl(string method, string path, int code, any response_body, *hash headers, *softlist content_types) { + private hash processResponseImpl(string method, string path, int code, auto response_body, *hash headers, *softlist content_types) { return processResponseIntern(method, path, code, response_body, headers, content_types, compact_serialization); } @@ -1297,7 +1300,7 @@ public class SwaggerSchema inherits ObjectBase, AbstractRestSchemaValidator { PathItemObject pio = paths.match(path); OperationObject op = pio.getOperation(method, path); - any body; + auto body; hash rv = op.getExampleRequest(method, path, pio, self, \body); if (exists body) { # serialize example body @@ -1345,7 +1348,7 @@ public class SwaggerSchema inherits ObjectBase, AbstractRestSchemaValidator { PathItemObject pio = paths.match(path); OperationObject op = pio.getOperation(method, path); - any body; + auto body; hash rv = op.getExampleResponse(method, path, code, \body); if (exists body) { # serialize example body @@ -1399,7 +1402,7 @@ public class SwaggerSchema inherits ObjectBase, AbstractRestSchemaValidator { @throws SERIALIZATION-ERROR unsupported path, message body MIME type requested, or missing serialization module */ - private:internal hash processRequestIntern(string method, string path, any body, *hash headers, *softlist content_types, bool compact_serialization) { + private:internal hash processRequestIntern(string method, string path, auto body, *hash headers, *softlist content_types, bool compact_serialization) { hash mime_types; # remove basePath if present @@ -1484,7 +1487,7 @@ public class SwaggerSchema inherits ObjectBase, AbstractRestSchemaValidator { @note processResponseIntern(string method, string path, int code, any response_body, *hash headers, *softlist content_types, bool compact_serialization) { + private:internal hash processResponseIntern(string method, string path, int code, auto response_body, *hash headers, *softlist content_types, bool compact_serialization) { method = method.lwr(); PathItemObject pio = paths.match(path); OperationObject op = pio.getOperation(method, path); @@ -1856,7 +1859,7 @@ public class PathItemObject inherits ObjectBase { *list params; if (optional_field(objType, oh, "parameters", NT_LIST, \params)) { - foreach any param in (params) { + foreach auto param in (params) { if (param.typeCode() != NT_HASH) throw "INVALID-FIELD-TYPE", sprintf("Path Item Object: 'parameters' list value has invalid type %y", @@ -2008,7 +2011,7 @@ public class OperationObject inherits ObjectBase { string objType = "Operation"; *list tagsObj; if (optional_field(objType, oh, "tags", NT_LIST, \tagsObj)) { - foreach any tag in (tagsObj) { + foreach auto tag in (tagsObj) { if (tag.typeCode() == NT_STRING) tags += tag; else if (tag.typeCode() == NT_HASH) @@ -2030,17 +2033,17 @@ public class OperationObject inherits ObjectBase { # TODO check operationId uniqueness as per docs # use the global "consumes" definition if none is defined on the operation - if (!optional_field(objType, oh, "consumes", SwaggerListToSet, \consumes) && swagger.consumes) + if (!optional_field(objType, oh, "consumes", SwaggerListToStringSet, \consumes) && swagger.consumes) consumes = swagger.consumes; # use the global "produces" definition if none is defined on the operation - if (!optional_field(objType, oh, "produces", SwaggerListToSet, \produces) && swagger.produces) + if (!optional_field(objType, oh, "produces", SwaggerListToStringSet, \produces) && swagger.produces) produces = swagger.produces; { list listObj; optional_field(objType, oh, "parameters", NT_LIST, \listObj); - foreach any p in (listObj) { + foreach auto p in (listObj) { if (p.typeCode() != NT_HASH) error("INVALID-FIELD-TYPE", "invalid 'parameters' list value datatype %y; expecing \"hash\"", p.type()); AbstractParameterObject param = AbstractParameterObject::newParameter(sprintf("%d/%d", $# + 1, listObj.lsize()), p, swagger); @@ -2061,7 +2064,7 @@ public class OperationObject inherits ObjectBase { { list schemesList; optional_field(objType, oh, "schemes", NT_LIST, \schemesList); - foreach any val in (schemesList) { + foreach auto val in (schemesList) { if (val.typeCode() != NT_STRING) error("INVALID-FIELD-TYPE", "invalid 'schemes' list value datatype %y; expecting \"string\"", val.type()); if (!ValidSchemesHash{val}) @@ -2073,7 +2076,7 @@ public class OperationObject inherits ObjectBase { { list securityList; optional_field(objType, oh, "security", NT_LIST, \securityList); - foreach any val in (securityList) { + foreach auto val in (securityList) { if (val.typeCode() != NT_HASH) error("INVALID-FIELD-TYPE", "invalid 'security' list value datatype %y; expecting \"hash\"", val.type()); hash h = val; @@ -2102,7 +2105,7 @@ public class OperationObject inherits ObjectBase { @throws SCHEMA-VALIDATION-ERROR invalid parameter name or location, missing parameter and parameter has no default value */ - validateRequest(bool serialize, PathItemObject pio, reference> h, reference body, reference headers, *reference> mime_types) { + validateRequest(bool serialize, PathItemObject pio, reference> h, reference body, reference headers, *reference> mime_types) { # check query parameters foreach string key in (keys h.params) { # find URI parameter object @@ -2153,7 +2156,7 @@ public class OperationObject inherits ObjectBase { @throws INVALID-PARAMETER-LOCATION invalid parameter location @throws MISSING-PARAMETER missing parameter and parameter has no default value */ - parseRequest(PathItemObject pio, reference> h, reference body, reference headers) { + parseRequest(PathItemObject pio, reference> h, reference body, reference headers) { # validate message validateRequest(False, pio, \h, \body, \headers); @@ -2163,7 +2166,7 @@ public class OperationObject inherits ObjectBase { } #! validates a response against the response definition, if any - validateResponse(string method, string path, PathItemObject pio, int code, any response_body, reference> mime_types) { + validateResponse(string method, string path, PathItemObject pio, int code, auto response_body, reference> mime_types) { *ResponseObject res = getResponse(code); if (res.schema) res.schema.check(True, False, path, method, "response body", \response_body); @@ -2310,7 +2313,7 @@ public class OperationObject inherits ObjectBase { } #! add default parameters - private doDefaultParams(reference> h, reference headers, reference body, hash parameters, *hash child_params) { + private doDefaultParams(reference> h, reference headers, reference body, hash parameters, *hash child_params) { foreach hash ph in (parameters.pairIterator()) { # don't check if child params already checked or if it's the single message body param if (child_params{ph.key} || ph.value.inLoc == "body") @@ -2336,7 +2339,7 @@ public class OperationObject inherits ObjectBase { } #! checks for missing params - private checkMissingParams(hash h, *hash headers, *hash body, hash parameters, *hash child_params) { + private checkMissingParams(hash h, *hash headers, auto body, hash parameters, *hash child_params) { foreach hash ph in (parameters.pairIterator()) { # don't check if child params already checked if (child_params{ph.key} || ph.value.inLoc == "body") @@ -2499,7 +2502,7 @@ public class AbstractParameterObject inherits ObjectBase { abstract check(bool serialize, bool request, string path, string method, string name, reference value); #! returns the default value of the parameter (default: @ref nothing) - any getDefaultValue() { + auto getDefaultValue() { } #! gets a concrete instance of an AbstractParameterObject @@ -2549,7 +2552,7 @@ public class BodyParameter inherits AbstractParameterObject { } # returns an example value for a REST API call - any getExampleValue() { + auto getExampleValue() { return schema.getExampleValue(); } } @@ -2607,7 +2610,7 @@ public class OtherParameter inherits AbstractParameterObject, SchemaBase { See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-6.2. Unlike JSON Schema this value MUST conform to the defined type for this parameter. */ - any defaultVal; + auto defaultVal; #! valid parameter types const ParameterTypes = SchemaObject::ScalarTypes + ( @@ -2675,19 +2678,19 @@ public class OtherParameter inherits AbstractParameterObject, SchemaBase { } #! returns the default value of the parameter (default: @ref nothing) - any getDefaultValue() { + auto getDefaultValue() { return defaultVal; } #! returns an example value of the parameter (default: @ref nothing) - any getExampleValue() { + auto getExampleValue() { if (exists defaultVal) return defaultVal; return getExampleValue(type, format); } #! validates default values - private static checkValueType(any value, string type, *SchemaObject items, *string loc) { + private static checkValueType(auto value, string type, *SchemaObject items, *string loc) { int vt = value.typeCode(); if (type == "array") { if (vt != NT_LIST) @@ -2860,7 +2863,7 @@ public class HeaderObject inherits ObjectBase, SchemaBase { Unlike JSON Schema this value MUST conform to the defined @ref HeaderObject::type "type" for the header. */ - any defaultVal; + auto defaultVal; } #! Constructor. @@ -2907,7 +2910,7 @@ public class HeaderObject inherits ObjectBase, SchemaBase { } #! validates default values - private static checkValueType(any value, string type, *SchemaObject items, *string loc) { + private static checkValueType(auto value, string type, *SchemaObject items, *string loc) { int vt = value.typeCode(); if (type == "array") { if (vt != NT_LIST) @@ -2980,7 +2983,7 @@ public class SchemaObject inherits ObjectBase, SchemaBase { *SchemaObject items; #! See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-6.2. - any defaultVal; + auto defaultVal; #! See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.4.1. *int maxProperties; @@ -3000,7 +3003,7 @@ public class SchemaObject inherits ObjectBase, SchemaBase { MUST be a bool or an object. If it is an object, it MUST also be a valid JSON Schema (@ref SchemaObject). */ - any additionalProperties; + auto additionalProperties; #! See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.4.3. /** @@ -3064,7 +3067,7 @@ public class SchemaObject inherits ObjectBase, SchemaBase { *ExternalDocumentationObject externalDocs; #! A free-form property to include an example of an instance for this schema. - any example; + auto example; #! valid scalar types const ScalarTypes = ( @@ -3136,7 +3139,7 @@ public class SchemaObject inherits ObjectBase, SchemaBase { if (optional_field(objType, oh, "properties", NT_HASH, \props)) { checkObjectProperty(name, "properties"); foreach string k in (keys props) { - any val = props{k}; + auto val = props{k}; if (val.typeCode() != NT_HASH) throw "INVALID-FIELD-FORMAT", sprintf("Schema Object %y: 'properties' field is not a hash of Schema Objects", name); properties{k} = SchemaObject::newSchemaObject(k, val, swagger); @@ -3151,7 +3154,7 @@ public class SchemaObject inherits ObjectBase, SchemaBase { } # process "required" - if (optional_field(objType, oh, "required", SwaggerListToSet, \required)) { + if (optional_field(objType, oh, "required", SwaggerListToStringSet, \required)) { if (!required) throw "INVALID-FIELD-FORMAT", sprintf("Schema Object %y: field 'required' is an empty list", name); } @@ -3168,7 +3171,7 @@ public class SchemaObject inherits ObjectBase, SchemaBase { if (optional_field(objType, oh, "allOf", NT_LIST, \allOfList)) { if (allOfList.size() < 1) throw "INVALID-FIELD-FORMAT", sprintf("Schema Object %y: field 'allOf' is an empty list", name); - foreach any val in (allOfList) { + foreach auto val in (allOfList) { if (val.typeCode() != NT_HASH) throw "INVALID-FIELD-TYPE", sprintf("Schema Object %y: invalid 'allOf' list value datatype: %y", name, val.type()); allOf += SchemaObject::newSchemaObject(name + "AllOf", val, swagger); @@ -3199,7 +3202,7 @@ public class SchemaObject inherits ObjectBase, SchemaBase { } if (type != "object") { - any val = getExampleValue(); + auto val = getExampleValue(); string example; switch (val.typeCode()) { case NT_DATE: @@ -3262,7 +3265,7 @@ public class SchemaObject inherits ObjectBase, SchemaBase { } # returns an example value for a REST API call - any getExampleValue() { + auto getExampleValue() { if (exists example) return example; if (exists defaultVal) @@ -3314,7 +3317,7 @@ public class SchemaObject inherits ObjectBase, SchemaBase { if (!prop) throw "SCHEMA-VALIDATION-ERROR", sprintf("property %y is not valid for object %y", key, name); } - prop.check(serialize, request, path, method, name, \value{key}); + prop.check(serialize, request, path, method, key, \value{key}); } } # check for missing required properties @@ -3326,7 +3329,7 @@ public class SchemaObject inherits ObjectBase, SchemaBase { } #! validates default values - private static checkValueType(any value, string type, *SchemaObject items, *string loc) { + private static checkValueType(auto value, string type, *SchemaObject items, *string loc) { int vt = value.typeCode(); if (type == "array") { if (vt != NT_LIST) @@ -3363,7 +3366,7 @@ public class SchemaObject inherits ObjectBase, SchemaBase { #! throws an \c "INVALID-FIELD-TYPE" exception /** @throws INVALID-FIELD-TYPE due to a type error in the call */ - static SchemaObject newSchemaObject(string name, any error, SwaggerSchema swagger) { + static SchemaObject newSchemaObject(string name, auto error, SwaggerSchema swagger) { throw "INVALID-FIELD-TYPE", sprintf("invalid field type %y for key %y; expecting \"hash\"", error.type(), name); } } @@ -3435,7 +3438,7 @@ public class ParameterItemsSchemaObject inherits SchemaObject { #! throws an \c "INVALID-FIELD-TYPE" exception /** @throws INVALID-FIELD-TYPE due to a type error in the call */ - static ParameterItemsSchemaObject newSchemaObject(string name, any error, SwaggerSchema swagger) { + static ParameterItemsSchemaObject newSchemaObject(string name, auto error, SwaggerSchema swagger) { throw "INVALID-FIELD-TYPE", sprintf("invalid field type %y for key %y; expecting \"hash\"", error.type(), name); } } @@ -3627,8 +3630,11 @@ public class ScopesObject inherits ObjectBase { # private namespace for internal definitions namespace Priv { - const SwaggerListToSet = -1; - const SwaggerListToHashOfStrings = -2; + # a set of string values + const SwaggerListToStringSet = -1; + # a set of any type that can be converted to a string + const SwaggerListToAnySet = -2; + const SwaggerListToHashOfStrings = -3; const TypeMap = ( NT_INT: "int", @@ -3638,7 +3644,8 @@ namespace Priv { NT_HASH: "hash", NT_NOTHING: "nothing", NT_BOOLEAN: "bool", - SwaggerListToSet: "list", + SwaggerListToStringSet: "list", + SwaggerListToAnySet: "list", ); #! Check and assign a required field. @@ -3652,10 +3659,10 @@ namespace Priv { @throws INVALID-FIELD-TYPE field has invalid type @throws REQUIRED-FIELD-MISSING required field is missing */ - sub required_field(string objType, hash oh, string name, int typeCode, reference target) { + sub required_field(string objType, hash oh, string name, int typeCode, reference target) { if (!oh.hasKey(name)) throw "REQUIRED-FIELD-MISSING", sprintf("%s Object: %y field is missing; fields present: %y", objType, name, keys oh); - any val = oh{name}; + auto val = oh{name}; check_type_code(objType, name, val, typeCode); get_value(objType, name, typeCode, val, \target); } @@ -3671,10 +3678,10 @@ namespace Priv { @throws INVALID-FIELD-TYPE field has invalid type @throws REQUIRED-FIELD-MISSING required field is missing */ - sub required_field(string objType, hash oh, string name, hash typeCodes, reference target) { + sub required_field(string objType, hash oh, string name, hash typeCodes, reference target) { if (!oh.hasKey(name)) throw "REQUIRED-FIELD-MISSING", sprintf("%s Object: %y field is missing; fields present: %y", objType, name, keys oh); - any val = oh{name}; + auto val = oh{name}; if (!typeCodes{val.typeCode()}) throw "INVALID-FIELD-TYPE", sprintf("%s Object: %y field has invalid type %y", objType, name, val.type()); @@ -3693,10 +3700,10 @@ namespace Priv { @throws INVALID-FIELD-TYPE field has invalid type */ - bool sub optional_field(string objType, hash oh, string name, int typeCode, reference target) { + bool sub optional_field(string objType, hash oh, string name, int typeCode, reference target) { if (!oh.hasKey(name)) return False; - any val = oh{name}; + auto val = oh{name}; check_type_code(objType, name, val, typeCode); get_value(objType, name, typeCode, val, \target); return True; @@ -3714,10 +3721,10 @@ namespace Priv { @throws INVALID-FIELD-TYPE field has invalid type */ - bool sub optional_field(string objType, hash oh, string name, hash typeCodes, reference target) { + bool sub optional_field(string objType, hash oh, string name, hash typeCodes, reference target) { if (!oh.hasKey(name)) return False; - any val = oh{name}; + auto val = oh{name}; if (!typeCodes{val.typeCode()}) throw "INVALID-FIELD-TYPE", sprintf("%s Object: %y field has invalid type %y", objType, name, val.type()); get_value(objType, name, val.typeCode(), val, \target); @@ -3725,25 +3732,32 @@ namespace Priv { } #! verifies the type of a field - sub check_type_code(string objType, string name, any val, int typeCode) { + sub check_type_code(string objType, string name, auto val, int typeCode) { int vtc = val.typeCode(); - if ((typeCode == SwaggerListToSet || typeCode == SwaggerListToHashOfStrings) && vtc == NT_LIST) + if ((typeCode == SwaggerListToStringSet || typeCode == SwaggerListToAnySet || typeCode == SwaggerListToHashOfStrings) && vtc == NT_LIST) return; if (vtc != typeCode) throw "INVALID-FIELD-TYPE", sprintf("%s Object: %y field has invalid type %y; expecting %y", objType, name, val.type(), TypeMap{typeCode}); } #! assigns a value to a field - sub get_value(string objType, string name, int typeCode, any val, reference target) { - if (typeCode == SwaggerListToSet) { - foreach any v in (val) { + sub get_value(string objType, string name, int typeCode, auto val, reference target) { + if (typeCode == SwaggerListToStringSet) { + foreach auto v in (val) { if (v.typeCode() != NT_STRING) - throw "INVALID-FIELD-TYPE", sprintf("%s Object: %y element %d/%d in %y list value has type %y; expecting \"string\"", objType, name, $# + 1, val.size(), v.type()); + throw "INVALID-FIELD-TYPE", sprintf("%s Object: %y element %d/%d in list value has type %y; expecting \"string\"", objType, name, $# + 1, val.size(), v.type()); + target{v} = True; + } + } + else if (typeCode == SwaggerListToAnySet) { + foreach auto v in (val) { + if (!v.strp()) + throw "INVALID-FIELD-TYPE", sprintf("%s Object: %y element %d/%d in list value has type %y which cannot be converted to a string; the Swagger module only supports types that can be converted to a string for this field", objType, name, $# + 1, val.size(), v.type()); target{v} = True; } } else if (typeCode == SwaggerListToHashOfStrings) { - foreach any v in (val) { + foreach auto v in (val) { int pos = $# + 1; if (v.typeCode() != NT_HASH) throw "INVALID-FIELD-TYPE", sprintf("%s Object: %y element %d/%d value has type %y; expecting \"hash\"", objType, name, pos, val.size(), v.type()); @@ -3752,7 +3766,7 @@ namespace Priv { string key = v.firstKey(); if (target.hasKey(key)) throw "INVALID-FIELD-TYPE", sprintf("%s Object: %y element %d/%d has duplicate key %y", objType, name, pos, val.size(), key); - foreach any fv in (v.firstValue()) { + foreach auto fv in (v.firstValue()) { if (fv.typeCode() != NT_STRING) throw "INVALID-FIELD-TYPE", sprintf("%s Object: %y element %d/%d in %y list value has type %y in position %d/%d; expecting \"string\"", objType, name, pos, val.size(), key, fv.type(), $# + 1, fv.lsize()); }