Asana / python-asana

Official Python client library for the Asana API v1
MIT License
299 stars 103 forks source link

fix: GCP not recognizing access token #196

Closed vantaboard closed 6 months ago

vantaboard commented 6 months ago

The issue: While running the package in a Google Cloud Function, an AttributeError was being raised about the access token. This was despite having set configuration.access_token.

Error log:

 Traceback (most recent call last):
  File "/layers/google.python.pip/pip/lib/python3.11/site-packages/flask/app.py", line 2529, in wsgi_app
    response = self.full_dispatch_request()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/layers/google.python.pip/pip/lib/python3.11/site-packages/flask/app.py", line 1825, in full_dispatch_request
    rv = self.handle_user_exception(e)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/layers/google.python.pip/pip/lib/python3.11/site-packages/flask/app.py", line 1823, in full_dispatch_request
    rv = self.dispatch_request()
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "/layers/google.python.pip/pip/lib/python3.11/site-packages/flask/app.py", line 1799, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/layers/google.python.pip/pip/lib/python3.11/site-packages/functions_framework/__init__.py", line 123, in view_func
    function(event)
  File "/layers/google.python.pip/pip/lib/python3.11/site-packages/functions_framework/__init__.py", line 67, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/main.py", line 19, in pull_workspaces
    workspaces = get_workspaces()
                 ^^^^^^^^^^^^^^^^
  File "/layers/google.python.pip/pip/lib/python3.11/site-packages/########/asana/api.py", line 46, in get_workspaces
    for workspace in workspaces_iter:
  File "/layers/google.python.pip/pip/lib/python3.11/site-packages/asana/pagination/page_iterator.py", line 43, in items
    for page in self:
  File "/layers/google.python.pip/pip/lib/python3.11/site-packages/asana/pagination/page_iterator.py", line 24, in __next__
    result = self.call_api()
             ^^^^^^^^^^^^^^^
  File "/layers/google.python.pip/pip/lib/python3.11/site-packages/asana/pagination/page_iterator.py", line 48, in call_api
    return self.__api_client.call_api(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/layers/google.python.pip/pip/lib/python3.11/site-packages/asana/api_client.py", line 340, in call_api
    return self.__call_api(resource_path, method,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/layers/google.python.pip/pip/lib/python3.11/site-packages/asana/api_client.py", line 155, in __call_api
    self.update_params_for_auth(header_params, query_params, auth_settings)
  File "/layers/google.python.pip/pip/lib/python3.11/site-packages/asana/api_client.py", line 522, in update_params_for_auth
    auth_setting = self.configuration.auth_settings().get('token')
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/layers/google.python.pip/pip/lib/python3.11/site-packages/asana/configuration.py", line 246, in auth_settings
    'value': 'Bearer ' + self.access_token
                         ^^^^^^^^^^^^^^^^^
AttributeError: 'Configuration' object has no attribute 'access_token' 

Right now I am remedying the issue with this helper function:

def rewrite_configuration_file() -> None:
    """
    Auth settings patcher for Asana API.

    In a departure from the official documentation where you would reassign the
    configuration.access_token property, we are using a lambda function to
    dynamically set the token value. This is because the configuration class
    does not define this property and leads to an AttributeError when trying to
    use it in the original auth_settings function. This fails in Google Cloud
    Functions, but works locally. This is a workaround to make it work in both
    environments.
    """
    with open(
        os.path.join(os.path.dirname(asana.__file__), "configuration.py")
    ) as configuration_file:
        configuration_file_contents = configuration_file.read()

        if "self.access_token = None" in configuration_file_contents:
            return

        init_method = configuration_file_contents.find(
            "def __init__(self):"
        ) + len("def __init__(self):")

        configuration_file_contents = (
            configuration_file_contents[:init_method]
            + "\n        self.access_token = None\n"
            + configuration_file_contents[init_method:]
        )

    with open(
        os.path.join(os.path.dirname(asana.__file__), "configuration.py"), "w"
    ) as configuration_file:
        configuration_file.write(configuration_file_contents)

Which is obviously not ideal.

This resolves a bug similar to https://github.com/OpenAPITools/openapi-generator/issues/7929 in the openapi-generator project which uses a similar fix as this one, found here https://github.com/OpenAPITools/openapi-generator/pull/7469.

Ensuring the access_token attribute is always initialized helps prevent this error from being raised. The only error from an empty access_token attribute should be one that comes back from the Asana API.

jv-asana commented 6 months ago

@vantaboard Thanks again for your contribution. We've deployed your fixes under python-asana v5.0.6.