Search code examples
pythondjangouwsgiwsgisaleor

AttributeError: 'WSGIRequest' object has no attribute 'app'


As suggested here, I'm building Saleor dashboard with these commands on a local machine and then copy it to the remote server:

cd ~/repos/fork/saleor-dashboard
git checkout 2.11.1
npm14 --verbose install
export API_URI="https://api.mydomain.com/graphql/"
export GTM_ID="GTM-K9ZZ3R8"
npm14 run build
ls build/dashboard/
scp -i ~/Documents/webserver-key-pair.pem -r build/dashboard [email protected]:/srv/www/mydomain/

Problem

The dashboard is available online. But when I log in with a valid user/pass, the Saleor API throws these errors, visible by sudo journalctl -xe:

[20/Jul/2021 02:43:43] "OPTIONS /graphql// HTTP/1.0" 200 0
[20/Jul/2021 02:43:44] "POST /graphql// HTTP/1.0" 200 23728
[20/Jul/2021 02:43:58] "OPTIONS /graphql// HTTP/1.0" 200 0
[20/Jul/2021 02:43:58] "POST /graphql// HTTP/1.0" 200 1971
Traceback (most recent call last):
   File "/home/ec2-user/repos/fork/saleor/saleor-venv/lib64/python3.9/site-packages/promise/promise.py", line 489, in _resolve_from_executor
     executor(resolve, reject)
   File "/home/ec2-user/repos/fork/saleor/saleor-venv/lib64/python3.9/site-packages/promise/promise.py", line 756, in executor
     return resolve(f(*args, **kwargs))
   File "/home/ec2-user/repos/fork/saleor/saleor-venv/lib64/python3.9/site-packages/graphql/execution/middleware.py", line 75, in make_it_promise
     return next(*args, **kwargs)
   File "/home/ec2-user/repos/fork/saleor/saleor/graphql/core/fields.py", line 164, in connection_resolver
     iterable = resolver(root, info, **args)
   File "/home/ec2-user/repos/fork/saleor/saleor/graphql/product/schema.py", line 297, in resolve_products
     return resolve_products(info, **kwargs)
   File "/home/ec2-user/repos/fork/saleor/saleor/graphql/product/resolvers.py", line 49, in resolve_products
     user = get_user_or_app_from_context(info.context)
   File "/home/ec2-user/repos/fork/saleor/saleor/graphql/utils/__init__.py", line 128, in get_user_or_app_from_context
     return context.app or context.user
 graphql.error.located_error.GraphQLLocatedError: 'WSGIRequest' object has no attribute 'app'
 ERROR saleor.graphql.errors.unhandled A query failed unexpectedly [PID:8388:Thread-117]
 Traceback (most recent call last):
   File "/home/ec2-user/repos/fork/saleor/saleor-venv/lib64/python3.9/site-packages/promise/promise.py", line 489, in _resolve_from_executor
     executor(resolve, reject)
   File "/home/ec2-user/repos/fork/saleor/saleor-venv/lib64/python3.9/site-packages/promise/promise.py", line 756, in executor
     return resolve(f(*args, **kwargs))
   File "/home/ec2-user/repos/fork/saleor/saleor-venv/lib64/python3.9/site-packages/graphql/execution/middleware.py", line 75, in make_it_promise
     return next(*args, **kwargs)
   File "/home/ec2-user/repos/fork/saleor/saleor/graphql/core/fields.py", line 164, in connection_resolver
     iterable = resolver(root, info, **args)
   File "/home/ec2-user/repos/fork/saleor/saleor/graphql/product/schema.py", line 297, in resolve_products
     return resolve_products(info, **kwargs)
   File "/home/ec2-user/repos/fork/saleor/saleor/graphql/product/resolvers.py", line 49, in resolve_products
     user = get_user_or_app_from_context(info.context)
   File "/home/ec2-user/repos/fork/saleor/saleor/graphql/utils/__init__.py", line 128, in get_user_or_app_from_context
     return context.app or context.user
 AttributeError: 'WSGIRequest' object has no attribute 'app'
 [20/Jul/2021 02:43:59] "POST /graphql// HTTP/1.0" 200 3192

What could be the cause? How can I debug it? Thanks! =)

Note

When I run both API and dashboard locally, on localhost or 127.0.0.1, everything is fine. The above error is thrown when I run API by a server and deploy dashboard on it.

Update

I'm using NGINX to serve Saleor API, dashboard and storefront:


    server {
        server_name mydomain.com;
        root /srv/www/mydomain.com/storefront;

        location / {
            index  index.html index.htm;
        }
    }

    server {
        server_name dashboard.mydomain.com;
        root /srv/www/mydomain.com/dashboard;

        location / {
            index  index.html index.htm;
        }
    }

    server {
        server_name api.mydomain.com;

        location / {
            proxy_pass http://127.0.0.1:8000/;
        }
        location /graphql {
            proxy_pass http://127.0.0.1:8000/graphql/;
        }
    }

I feel like since the web app is a WSGI one, NGINX config should be different. Any idea?

Update: test NGINX

Previously, when I ran Saleor API and Saleor storefront locally, there was no error.

But I did a test. I used NGINX even for localhost communications:

    server {
        listen 8001;
        server_name localhost;

        location / {
            proxy_pass http://127.0.0.1:8000/;
        }
        location /graphql {
            proxy_pass http://127.0.0.1:8000/graphql/;
        }
    }

Then I start storefront by:

cd ~/repos/fork/saleor-storefront
export GTM_ID="GTM-K7ZZ3R1"                    
export API_URI="http://localhost:8001/graphql/"
npm14 start

ℹ 「wds」: Project is running at http://localhost:3000/

Then the browser DevTools console throws the same error:

Console error

Conclusion

Looks like NGINX is the problem cause.


Solution

  • Problem solved by bringing up the backend Django server by uwsgi module:

      cd /home/ec2-user/repos/fork/saleor
      source saleor-venv/bin/activate
      export DEBUG="False"
      export ALLOWED_CLIENT_HOSTS="localhost,127.0.0.1,dashboard.mydomain.com,mydomain.com,api.mydomain.com"
      export ALLOWED_GRAPHQL_ORIGINS="*"
      export ALLOWED_HOSTS=".localhost,127.0.0.1,.mydomain.com"
      export CREATE_IMAGES_ON_DEMAND="False"
      export DEFAULT_COUNTRY="US"
      export DEFAULT_CURRENCY="USD"
      export SECRET_KEY="Some random text!"
      ### In production, UWSGI is required rather than this:
      #python3.9 manage.py runserver
      python3.9 -m pip install uwsgi
      ### HTTP is just for testing:
      #uwsgi --http :8000 --ini saleor/wsgi/uwsgi.ini
      ### SOCKET is required for NGINX:
      uwsgi --socket :8000 --ini saleor/wsgi/uwsgi.ini
    

    And using NGINX to pass requests by uwsgi protocol (rather than http one) to the backend Django server:

    http {
    
        # the upstream component nginx needs to connect to
        # for Saleor API
        upstream django_api {
            server 127.0.0.1:8000;
        }
    
        server {
            server_name api.mydomain.com;
    
            charset     utf-8;
            
            # max upload size
            client_max_body_size 75M;
    
            # Django media
            location /media  {
                alias /home/ec2-user/repos/fork/saleor/media;
            }
    
            location /static {
                alias /home/ec2-user/repos/fork/saleor/static;
            }   
    
            # Finally, send all non-media requests to the Django server.
            location / {
                uwsgi_pass django_api;
                include /home/ec2-user/repos/fork/saleor/uwsgi_params;
            }
    
            location /graphql {
                uwsgi_pass django_api;
                include /home/ec2-user/repos/fork/saleor/uwsgi_params;
            }
    
            #location / {
            #    proxy_pass http://127.0.0.1:8000/;
            #}
            #location /graphql {
            #    proxy_pass http://127.0.0.1:8000/graphql/;
            #}
    
    
        }
    
    }
    

    The documentation is here:

    https://uwsgi.readthedocs.io/en/latest/tutorials/Django_and_nginx.html