Connexion: Issue with api endpoint requiring single quotation mark

Created on 23 Feb 2018  路  7Comments  路  Source: zalando/connexion

Issue with being forced to put a single quote on the end of api path with query parameters specified The following will NOT run in Python 3.6 on Flask in Pycharm 2017.3

paths:
/phish_finder:
get:
parameters:
- name: alpha2
in: query
type: string
minimum: 2
maximum: 2
- name: link
in: query
type: string
minimum: 5

Expected behaviour

Should run as expected

Actual behaviour

"/Users/chez 1/.virtualenvs/flask-apps/bin/python3.6" "/Users/chez 1/flask-apps/phishfinder/phishfinder.py"
ERROR:connexion.apis.abstract:Failed to add operation for GET /v1.0/phish_finder
Traceback (most recent call last):
File "/Users/chez 1/.virtualenvs/flask-apps/lib/python3.6/site-packages/connexion/apis/abstract.py", line 243, in add_paths
self.add_operation(method, path, endpoint, path_parameters)
File "/Users/chez 1/.virtualenvs/flask-apps/lib/python3.6/site-packages/connexion/apis/abstract.py", line 195, in add_operation
pythonic_params=self.pythonic_params)
File "/Users/chez 1/.virtualenvs/flask-apps/lib/python3.6/site-packages/connexion/operation.py", line 223, in __init__
resolution = resolver.resolve(self)
File "/Users/chez 1/.virtualenvs/flask-apps/lib/python3.6/site-packages/connexion/resolver.py", line 40, in resolve
return Resolution(self.resolve_function_from_operation_id(operation_id), operation_id)
File "/Users/chez 1/.virtualenvs/flask-apps/lib/python3.6/site-packages/connexion/resolver.py", line 67, in resolve_function_from_operation_id
raise ResolverError(str(e), sys.exc_info())
connexion.exceptions.ResolverError:

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/Users/chez 1/flask-apps/phishfinder/phishfinder.py", line 24, in
resolver=RestyResolver('api'))
File "/Users/chez 1/.virtualenvs/flask-apps/lib/python3.6/site-packages/connexion/apps/flask_app.py", line 54, in add_api
api = super(FlaskApp, self).add_api(specification, *kwargs)
File "/Users/chez 1/.virtualenvs/flask-apps/lib/python3.6/site-packages/connexion/apps/abstract.py", line 159, in add_api
options=api_options.as_dict())
File "/Users/chez 1/.virtualenvs/flask-apps/lib/python3.6/site-packages/connexion/apis/abstract.py", line 131, in __init__
self.add_paths()
File "/Users/chez 1/.virtualenvs/flask-apps/lib/python3.6/site-packages/connexion/apis/abstract.py", line 250, in add_paths
self._handle_add_operation_error(path, method, err.exc_info)
File "/Users/chez 1/.virtualenvs/flask-apps/lib/python3.6/site-packages/connexion/apis/abstract.py", line 264, in _handle_add_operation_error
six.reraise(
exc_info)
File "/Users/chez 1/.virtualenvs/flask-apps/lib/python3.6/site-packages/six.py", line 693, in reraise
raise value
File "/Users/chez 1/.virtualenvs/flask-apps/lib/python3.6/site-packages/connexion/resolver.py", line 62, in resolve_function_from_operation_id
return self.function_resolver(operation_id)
File "/Users/chez 1/.virtualenvs/flask-apps/lib/python3.6/site-packages/connexion/utils.py", line 43, in get_function_from_name
function = deep_getattr(module, attr_path)
File "/Users/chez 1/.virtualenvs/flask-apps/lib/python3.6/site-packages/connexion/utils.py", line 11, in deep_getattr
return functools.reduce(getattr, attr.split('.'), obj)
AttributeError: module 'api.phish_finder' has no attribute 'search'

Process finished with exit code 1

Steps to reproduce

As above

Additional info:

## Adding a single quote to the path makes it work:

/phish_finder'

Output of the commands:

  • python --version 3.6
  • pip show connexion | grep "^Version\:" 1.3

Most helpful comment

I also had this issue. Looking at the code it seems its expected behaviour, although the error is super opaque.

In the file connexion/connexion/resolver.py:

class RestyResolver(Resolver):
    """
    Resolves endpoint functions using REST semantics (unless overridden by specifying operationId)
    """

    def __init__(self, default_module_name, collection_endpoint_name='search'):
        """
        :param default_module_name: Default module name for operations
        :type default_module_name: str
        """
        Resolver.__init__(self)
        self.default_module_name = default_module_name
        self.collection_endpoint_name = collection_endpoint_name

Basically if you do not pass the collection_endpoint_name kwarg to the RestyResolver call, it will assume that a general get on the collection endpoint maps to the search method. So basically you either add a search method, or pass the method you are using as a collection_endpoint_name.

All 7 comments

Any chance of a response to this please?

@Chez could you please provide the complete swagger.yaml or the project structure? The exception seems about resolving the operationId but not sure how a single quote would solve it, and it doesn't appear in you swagger def. I tried reproducing the issue but couldn't.

Here is the swagger doc:

swagger: "2.0"

info:
title: "{{title}}"
version: "1.0"

schemes:

  • http
  • https

consumes:

  • application/json
    produces:
  • application/json

basePath: /v1.0

securityDefinitions:
jwt:
type: apiKey
name: Authorization
in: header
x-authentication-scheme: Bearer

security:

  • jwt: [] # use the same name as above

paths:
/phish_finder':
get:
parameters:
- name: alpha2
in: query
description: Valid two character Alpha-2 country code
type: string
minimum: 2
maximum: 2
format: query
- name: link
in: query
description: Valid URL for checking against Phishing Database
type: string
minimum: 5
format: query
responses:
'200':
description: 'Test URL Link'
schema:
$ref: '#/definitions/PhishMatch'

definitions:
PhishMatch:
type: object
properties:
match: { type: boolean }

Hi @Chez

I have adjusted the swagger and the application and I find the following to be working fine:

swagger: "2.0"

info:
  title: "{{title}}"
  version: "1.0"

schemes:
  - http
  - https

consumes:
  - application/json

produces:
  - application/json

basePath: /v1.0

securityDefinitions:
  jwt:
    type: apiKey
    name: Authorization
    in: header
    x-authentication-scheme: Bearer

security:
  - jwt: [] # use the same name as above

paths:
  /phish_finder:
    get:
      operationId: app.search
      parameters:
        - name: alpha2
          in: query
          description: Valid two character Alpha-2 country code
          type: string
          minimum: 2
          maximum: 2
          format: query
        - name: link
          in: query
          description: Valid URL for checking against Phishing Database
          type: string
          minimum: 5
          format: query
      responses:
        '200':
          description: 'Test URL Link'
          schema:
            $ref: '#/definitions/PhishMatch'

definitions:
  PhishMatch:
    properties:
      match:
        type: boolean

Notice that I added operationId: app.search
and added the following in app.py:

#!/usr/bin/env python3

import connexion


def search():
    return {'match': True}


if __name__ == '__main__':
    app = connexion.FlaskApp(__name__, port=9090, specification_dir='')
    app.add_api('swagger.yaml')
    app.run()

by running python app.py you can get a server running with responses.

Hi, Mohabusama,

I got around the problem keepng the restyresolve but simply adding the operationid. So it seems like this is a bug.

I also had this issue. Looking at the code it seems its expected behaviour, although the error is super opaque.

In the file connexion/connexion/resolver.py:

class RestyResolver(Resolver):
    """
    Resolves endpoint functions using REST semantics (unless overridden by specifying operationId)
    """

    def __init__(self, default_module_name, collection_endpoint_name='search'):
        """
        :param default_module_name: Default module name for operations
        :type default_module_name: str
        """
        Resolver.__init__(self)
        self.default_module_name = default_module_name
        self.collection_endpoint_name = collection_endpoint_name

Basically if you do not pass the collection_endpoint_name kwarg to the RestyResolver call, it will assume that a general get on the collection endpoint maps to the search method. So basically you either add a search method, or pass the method you are using as a collection_endpoint_name.

This behavior described in documentation here

Was this page helpful?
0 / 5 - 0 ratings