Marshal expect with validate true doesn't work with nested field
Hello Folks,
When running a test case against an endpoint I'm getting the following error:
"Unresolvable JSON pointer: %r" % fragment
jsonschema.exceptions.RefResolutionError: Unresolvable JSON pointer: 'definitions/test_case_params'
Pip freeze:
alembic==1.4.0
aniso8601==8.0.0
astroid==2.3.3
attrs==19.3.0
autopep8==1.5
certifi==2019.11.28
chardet==3.0.4
Click==7.0
Flask==1.1.1
Flask-Cors==3.0.8
Flask-Migrate==2.5.2
flask-restx==0.1.1
Flask-SQLAlchemy==2.4.1
Flask-Testing==0.7.1
gunicorn==20.0.0
idna==2.8
importlib-metadata==1.5.0
isort==4.3.21
itsdangerous==1.1.0
Jinja2==2.11.1
jsonschema==3.2.0
lazy-object-proxy==1.4.3
Mako==1.1.1
MarkupSafe==1.1.1
mccabe==0.6.1
psycopg2-binary==2.8.3
pycodestyle==2.5.0
pylint==2.4.4
pyrsistent==0.15.7
python-dateutil==2.8.1
python-dotenv==0.10.3
python-editor==1.0.4
pytz==2019.3
requests==2.22.0
six==1.14.0
SQLAlchemy==1.3.13
typed-ast==1.4.1
urllib3==1.25.8
Werkzeug==0.16.1
wrapt==1.11.2
zipp==2.2.0
Full traceback:
ERROR: test_test_case_post_success_only_required_params (app.test.test_test_case_controller.TestTestCaseController)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/validators.py", line 811, in resolve_fragment
document = document[part]
KeyError: 'definitions'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/cleivianecosta/apps/geekhunter-dexter/app/test/test_test_case_controller.py", line 124, in test_test_case_post_success_only_required_params
follow_redirects=True)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/werkzeug/test.py", line 1039, in post
return self.open(*args, **kw)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/testing.py", line 227, in open
follow_redirects=follow_redirects,
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/werkzeug/test.py", line 993, in open
response = self.run_wsgi_app(environ.copy(), buffered=buffered)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/werkzeug/test.py", line 884, in run_wsgi_app
rv = run_wsgi_app(self.application, environ, buffered=buffered)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/werkzeug/test.py", line 1119, in run_wsgi_app
app_rv = app(environ, start_response)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/app.py", line 2463, in __call__
return self.wsgi_app(environ, start_response)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/app.py", line 2449, in wsgi_app
response = self.handle_exception(e)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_cors/extension.py", line 161, in wrapped_function
return cors_after_request(app.make_response(f(*args, **kwargs)))
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_restx/api.py", line 599, in error_router
return original_handler(f)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/app.py", line 1866, in handle_exception
reraise(exc_type, exc_value, tb)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_restx/api.py", line 597, in error_router
return self.handle_error(e)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/app.py", line 2446, in wsgi_app
response = self.full_dispatch_request()
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/app.py", line 1951, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_cors/extension.py", line 161, in wrapped_function
return cors_after_request(app.make_response(f(*args, **kwargs)))
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_restx/api.py", line 599, in error_router
return original_handler(f)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/app.py", line 1820, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_restx/api.py", line 597, in error_router
return self.handle_error(e)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/app.py", line 1949, in full_dispatch_request
rv = self.dispatch_request()
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/app.py", line 1935, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_restx/api.py", line 339, in wrapper
resp = resource(*args, **kwargs)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask/views.py", line 89, in view
return self.dispatch_request(*args, **kwargs)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_restx/resource.py", line 42, in dispatch_request
self.validate_payload(meth)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_restx/resource.py", line 88, in validate_payload
self.__validate_payload(expect, collection=False)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_restx/resource.py", line 73, in __validate_payload
expect.validate(data, self.api.refresolver, self.api.format_checker)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/flask_restx/model.py", line 104, in validate
validator.validate(data)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/validators.py", line 352, in validate
for error in self.iter_errors(*args, **kwargs):
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/validators.py", line 328, in iter_errors
for error in errors:
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/_validators.py", line 286, in properties
schema_path=property,
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/validators.py", line 344, in descend
for error in self.iter_errors(instance, schema):
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/validators.py", line 328, in iter_errors
for error in errors:
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/_legacy_validators.py", line 55, in items_draft3_draft4
for error in validator.descend(item, items, path=index):
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/validators.py", line 344, in descend
for error in self.iter_errors(instance, schema):
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/validators.py", line 328, in iter_errors
for error in errors:
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/_validators.py", line 259, in ref
scope, resolved = validator.resolver.resolve(ref)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/validators.py", line 766, in resolve
return url, self._remote_cache(url)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/validators.py", line 781, in resolve_from_url
return self.resolve_fragment(document, fragment)
File "/Users/cleivianecosta/apps/geekhunter-dexter/.env/lib/python3.7/site-packages/jsonschema/validators.py", line 814, in resolve_fragment
"Unresolvable JSON pointer: %r" % fragment
jsonschema.exceptions.RefResolutionError: Unresolvable JSON pointer: 'definitions/test_case_params'
The endpoint
...
API = Namespace("test_cases", description="test cases related operations")
TEST_CASE_PARAMS = API.model("test_case_params", {
"question_id": fields.Integer(
required=True, description="The related question id"
),
"name": fields.String(
required=True, description="The test case name"
),
"points": fields.Integer(
required=True, description="How many points is the test worth"
),
"test_input": fields.String(
required=True, description="The input for the test"
),
"test_output": fields.String(
required=True, description="The expected output for the test"
),
"is_visible": fields.Boolean(description="True if the test case is an example")
})
TEST_CASES_POST_PARAMS = API.model("test_cases_post_params", {
"test_cases": fields.List(fields.Nested(TEST_CASE_PARAMS), required=True)
})
...
@API.route("/")
@API.expect(PARSER)
class TestCaseList(Resource):
@API.expect(TEST_CASES_POST_PARAMS, validate=True)
@API.marshal_with(LIST_TEST_CASES_MODEL)
@token_required
def post(self):
data = request.json
test_cases = create_test_cases(data["test_cases"])
if test_cases is None:
return {
"error": "Could not create test_cases at this time"
}, HTTPStatus.INTERNAL_SERVER_ERROR
result = {"test_cases": [CreateGetTestCase(
test_case) for test_case in test_cases]}
return result, HTTPStatus.OK
The Test Case
def test_test_case_post_success_only_required_params(self):
"""
Test if the /test-cases/ endpoint is working in case of success
when only required params are passed
"""
breakpoint()
temp_params = {"test_cases": [self.test_case_required_params]}
response = self.client.post("/test-cases/",
headers={
"Content-Type": "application/json",
"Authorization": APP.config["DEFAULT_AUTH_TOKEN"]
},
data=json.dumps(temp_params),
follow_redirects=True)
self.assertTrue(response.status_code == HTTPStatus.OK)
output_data = json.loads(response.data)
for key in self.list_output_keys:
self.assertTrue(key in output_data)
self.assertTrue(output_data["error"] is None)
Note that before trying to migrate restplus to restX this was working as expected but on restX this error starts to pop up and if I set on the endpoint validate=False the problem would go away! So I think that there's some kind of problem when restX is trying to validate the params
hi, all. how to fixed it?I already get this problem
I'm seeing this as well, and my project relies heavily on nested validation. My example is pretty similar to the OP but I have a production endpoint using Nested and I get this trace in postman and in integration tests.
EDIT: For reference I'm running restx 0.3.0
alembic==1.5.8
AMQPStorm==2.8.4
aniso8601==9.0.1
attrs==20.3.0
certifi==2020.12.5
cffi==1.14.5
chardet==4.0.0
click==7.1.2
coverage==5.5
cryptography==3.4.7
decorator==5.0.7
directly==0.0.30
Flask==1.1.2
Flask-Cors==3.0.10
flask-restx==0.3.0
flask-talisman==0.7.0
greenlet==1.0.0
idna==2.10
iso8601==0.1.14
itsdangerous==1.1.0
Jinja2==2.11.3
jsonpath-ng==1.5.2
jsonschema==3.2.0
Mako==1.1.4
MarkupSafe==1.1.1
mysqlclient==2.0.3
pamqp==2.3.0
ply==3.11
psutil==5.8.0
pycparser==2.20
pycrypto==2.6.1
PyJWT==1.7.1
pyrsistent==0.17.3
python-dateutil==2.8.1
python-editor==1.0.4
pytz==2021.1
regex==2021.4.4
requests==2.25.1
six==1.15.0
SQLAlchemy==1.4.8
urllib3==1.26.4
Werkzeug==0.16.1
I came across this issue while I was editing a model and started using a Nested field. When running my tests, I got the error referenced in the issue:
"Unresolvable JSON pointer: %r" % fragment
jsonschema.exceptions.RefResolutionError: Unresolvable JSON pointer: 'definitions/test_case_params'
Having a look to the error message and to the endpoints, I realised that the endpoint was defined with doc=False, so I guessed that no schema was being generated. After removing doc=False from the endpoint, the errors disappeared and validations started to work.
I know it's not the same use case as in the original issue, but it may help someone in the future. Tested in flask-restx 0.4.0
I have been running into the same problem. If anyone has a solution it'd be much appreciated
I have been running into the same problem. If anyone has a solution it'd be much appreciated
Just realised people are remaking the whole marshalling.
Same problem while parsing open API specification via yaml.loader. Is there any other way to validate nested schemas from open API specification apart from parsing it into json and using json schema validation?