From 02f6880aa4ceae0da9a7e80b6e13e3e5257fa619 Mon Sep 17 00:00:00 2001 From: DJ Gillespie Date: Thu, 30 Dec 2021 17:25:37 -0700 Subject: [PATCH 01/12] updated reqs --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 6cbfb77..90ad82b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,6 +6,7 @@ cryptography==3.4.7 defusedxml==0.7.1 dj-database-url==0.5.0 dj-rest-auth==2.1.5 +django-rest-swagger==2.2.0 Django==3.2.3 django-allauth==0.44.0 django-cors-headers==3.7.0 From 6d82e3885324ace628783aa1d07020f1760d28fb Mon Sep 17 00:00:00 2001 From: David Date: Tue, 1 Feb 2022 03:49:21 +0000 Subject: [PATCH 02/12] bitbucket-pipelines.yml created online with Bitbucket --- bitbucket-pipelines.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 bitbucket-pipelines.yml diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml new file mode 100644 index 0000000..60eb69f --- /dev/null +++ b/bitbucket-pipelines.yml @@ -0,0 +1 @@ +# dummy pipelines file \ No newline at end of file From 28e39c1bdf09448ca9daa377f3bb0f0c011b941e Mon Sep 17 00:00:00 2001 From: David Date: Tue, 1 Feb 2022 03:57:05 +0000 Subject: [PATCH 03/12] Added pipelines configuration for auto-deploy --- bitbucket-pipelines.yml | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 60eb69f..4989f0e 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -1 +1,25 @@ -# dummy pipelines file \ No newline at end of file +# Template python-build + +# This template allows you to validate your python code. +# The workflow allows running tests and code linting on the default branch. + +image: python:3.8 + +pipelines: + branches: + master: + - step: + name: Deploy to Heroku Prod + caches: + - pip + script: + - pip install -r requirements.txt + - git push https://heroku:$HEROKU_API_KEY@git.heroku.com/$HEROKU_APP_NAME_PROD.git HEAD + dev-master: + - step: + name: Deploy to Heroku Staging + caches: + - pip + script: + - pip install -r requirements.txt + - git push https://heroku:$HEROKU_API_KEY@git.heroku.com/$HEROKU_APP_NAME_STAGING.git HEAD From 7aa0b0731bb29b194060a459d8e171704cebdb70 Mon Sep 17 00:00:00 2001 From: DJ Gillespie Date: Wed, 6 Apr 2022 19:23:56 -0600 Subject: [PATCH 04/12] added plaid req --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 90ad82b..eaca875 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,6 +15,7 @@ djangorestframework==3.12.4 djangorestframework-simplejwt==4.6.0 idna==2.10 oauthlib==3.1.0 +plaid-python==3.0.0 psycopg2==2.8.6 pycparser==2.20 PyJWT==2.1.0 From 44d4d91027a8ed0e7a7ca93fc4798ad144ed995c Mon Sep 17 00:00:00 2001 From: DJ Gillespie Date: Wed, 6 Apr 2022 19:40:04 -0600 Subject: [PATCH 05/12] added public_key back --- connection/connections/plaid_client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/connection/connections/plaid_client.py b/connection/connections/plaid_client.py index 73b3bb4..d6d0e98 100755 --- a/connection/connections/plaid_client.py +++ b/connection/connections/plaid_client.py @@ -42,6 +42,7 @@ class Connection(AbstractConnectionClient): client_id=self.PLAID_CLIENT_ID, secret=self.PLAID_SECRET, environment=self.PLAID_ENV, + public_key=self.PLAID_PUBLIC_KEY, # api_version='2019-05-29', api_version='2020-09-14', #webhook='https://qrtr-services.herokuapp.com/connection/plaid-webhook/' From 2555b438095a7cc619a2cfdccf861fd2aef405da Mon Sep 17 00:00:00 2001 From: DJ Gillespie Date: Wed, 6 Apr 2022 20:17:55 -0600 Subject: [PATCH 06/12] Fixed Plaid client immutable querydict bug. --- connection/connections/plaid_client.py | 3 ++- connection/views.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/connection/connections/plaid_client.py b/connection/connections/plaid_client.py index d6d0e98..d2aef67 100755 --- a/connection/connections/plaid_client.py +++ b/connection/connections/plaid_client.py @@ -18,7 +18,8 @@ class Connection(AbstractConnectionClient): def __init__(self, credentials): print("Plaid Connection Creation Initiated") - self.credentials = credentials + print(credentials) + self.credentials = credentials.dict() # Fill in your Plaid API keys - # https://dashboard.plaid.com/account/keys diff --git a/connection/views.py b/connection/views.py index a0970a7..ee64914 100644 --- a/connection/views.py +++ b/connection/views.py @@ -63,10 +63,11 @@ class ConnectionViewSet(viewsets.ModelViewSet): print(f"Account Found: {accounts[0]}") account = accounts[0] print(request) - plaid = importlib.import_module(f"connection.connections.plaid_client") + plaid_conn = importlib.import_module(f"connection.connections.plaid_client") conn_type = ConnectionType.objects.get(name="Plaid") try: - plaid_client = plaid.Connection(request.data) + print(f"CREATING CONNECTION, {request.data}") + plaid_client = plaid_conn.Connection(request.data) except ValueError: return Response(status=status.HTTP_503, data="ERROR: Invalid public_token") From 7172ec9f0dcde676607787cb104f96b9ccfaeab1 Mon Sep 17 00:00:00 2001 From: DJ Gillespie Date: Thu, 7 Apr 2022 16:53:12 -0600 Subject: [PATCH 07/12] Updated plaid libraries to 9.2.0 --- connection/connections/plaid_client.py | 52 +++++++++++++++++++------- core/settings/production.py | 7 +++- requirements.txt | 2 +- 3 files changed, 45 insertions(+), 16 deletions(-) diff --git a/connection/connections/plaid_client.py b/connection/connections/plaid_client.py index d2aef67..8beb62a 100755 --- a/connection/connections/plaid_client.py +++ b/connection/connections/plaid_client.py @@ -1,6 +1,10 @@ from .abstract import AbstractConnectionClient from django.conf import settings import plaid +from plaid.api import plaid_api +from plaid.model.item_public_token_exchange_request import ItemPublicTokenExchangeRequest +from plaid.model.transactions_get_request import TransactionsGetRequest +from plaid.model.accounts_get_request import AccountsGetRequest import os import datetime @@ -39,15 +43,26 @@ class Connection(AbstractConnectionClient): # PLAID_COUNTRY_CODES is a comma-separated list of countries for which users # will be able to select institutions from. self.PLAID_COUNTRY_CODES = settings.PLAID_COUNTRY_CODES - self.client = plaid.Client( - client_id=self.PLAID_CLIENT_ID, - secret=self.PLAID_SECRET, - environment=self.PLAID_ENV, - public_key=self.PLAID_PUBLIC_KEY, - # api_version='2019-05-29', - api_version='2020-09-14', - #webhook='https://qrtr-services.herokuapp.com/connection/plaid-webhook/' - ) + + configuration = plaid.Configuration( + host=self.PLAID_ENV, + api_key={ + 'clientId': self.PLAID_CLIENT_ID, + 'secret': self.PLAID_SECRET, + } + ) + api_client = plaid.ApiClient(configuration) + self.client = plaid_api.PlaidApi(api_client) + + # self.client = plaid.Client( + # client_id=self.PLAID_CLIENT_ID, + # secret=self.PLAID_SECRET, + # environment=self.PLAID_ENV, + # public_key=self.PLAID_PUBLIC_KEY, + # # api_version='2019-05-29', + # api_version='2020-09-14', + # #webhook='https://qrtr-services.herokuapp.com/connection/plaid-webhook/' + # ) public_key = self.credentials.get('public_token') auth_token = self.credentials.get('auth_token') if not auth_token and public_key: @@ -66,8 +81,11 @@ class Connection(AbstractConnectionClient): def get_auth_token(self, public_token): try: - exchange_response = self.client.Item.public_token.exchange( - public_token) + exchange_request = ItemPublicTokenExchangeRequest( + public_token=public_token + ) + exchange_response = self.client.item_public_token_exchange( + exchange_request) except Exception as e: print("Error Occurred") print(e) @@ -82,7 +100,8 @@ class Connection(AbstractConnectionClient): if not auth_token: raise Exception("Missing Auth Token") try: - accounts = self.client.Accounts.get(auth_token) + acc_request = AccountsGetRequest(access_token=auth_token) + accounts = self.client.accounts_get(acc_request).to_dict() except Exception as e: print(e) accounts = None @@ -103,8 +122,13 @@ class Connection(AbstractConnectionClient): if not end_date: end_date = '{:%Y-%m-%d}'.format(datetime.datetime.now()) try: - transactions_resp = self.client.Transactions.get( - auth_token, start_date, end_date) + transactions_req = TransactionsGetRequest( + access_token=auth_token, + start_date=start_date, + end_date=end_date + ) + transactions_resp = self.client.transactions_get( + transactions_req) except plaid.errors.PlaidError as e: return format_error(e) return transactions_resp.get("transactions") diff --git a/core/settings/production.py b/core/settings/production.py index d2dd201..7b21658 100644 --- a/core/settings/production.py +++ b/core/settings/production.py @@ -1,5 +1,6 @@ import os import dj_database_url +import plaid DEFAULT_CONNECTION = dj_database_url.parse(os.environ.get("DATABASE_URL")) @@ -17,8 +18,12 @@ DEBUG = True ALLOWED_HOSTS = ['*'] +plaid_envs = {'sandbox': plaid.Environment.Sandbox, + 'development': plaid.Environment.Development, + 'production': plaid.Environment.Production + } +PLAID_ENV = plaid_envs.get(os.environ.get("PLAID_ENV"), plaid_envs['sandbox']) PLAID_PRODUCTS = os.environ.get("PLAID_PRODUCTS") -PLAID_ENV = os.environ.get("PLAID_ENV") PLAID_PUBLIC_KEY = os.environ.get("PLAID_PUBLIC_KEY") PLAID_SECRET = os.environ.get("PLAID_SECRET") PLAID_CLIENT_ID = os.environ.get("PLAID_CLIENT_ID") diff --git a/requirements.txt b/requirements.txt index eaca875..f8fe6a2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,7 @@ djangorestframework==3.12.4 djangorestframework-simplejwt==4.6.0 idna==2.10 oauthlib==3.1.0 -plaid-python==3.0.0 +plaid-python==9.2.0 psycopg2==2.8.6 pycparser==2.20 PyJWT==2.1.0 From b96c0db8961a21cb3ef315e83cf1d3fe3c3fac60 Mon Sep 17 00:00:00 2001 From: DJ Gillespie Date: Mon, 31 Jan 2022 19:05:37 -0700 Subject: [PATCH 08/12] Added drf_yasg in place of django rest swagger --- core/settings/__init__.py | 8 +++++++- core/urls.py | 24 +++++++++++++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/core/settings/__init__.py b/core/settings/__init__.py index fc0422a..76b92a8 100644 --- a/core/settings/__init__.py +++ b/core/settings/__init__.py @@ -48,8 +48,14 @@ INSTALLED_APPS = [ 'corsheaders', 'rest_framework_simplejwt.token_blacklist', 'django_filters', - 'rest_framework_swagger', + 'drf_yasg', ] +SPECTACULAR_SETTINGS = { + 'SWAGGER_UI_DIST': 'SIDECAR', # shorthand to use the sidecar instead + 'SWAGGER_UI_FAVICON_HREF': 'SIDECAR', + 'REDOC_DIST': 'SIDECAR', + # OTHER SETTINGS +} EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' diff --git a/core/urls.py b/core/urls.py index e9b653b..5552b86 100644 --- a/core/urls.py +++ b/core/urls.py @@ -35,9 +35,25 @@ from qrtr_account.views import (AccountViewSet, TwitterLogin) from connection.views import ConnectionViewSet, ConnectionTypeViewSet -from rest_framework_swagger.views import get_swagger_view -schema_view = get_swagger_view(title="Qrtr API") + +from rest_framework import permissions +from drf_yasg.views import get_schema_view +from drf_yasg import openapi + + +schema_view = get_schema_view( + openapi.Info( + title="Qrtr API", + default_version='v1', + description="", + terms_of_service="https://www.google.com/policies/terms/", + contact=openapi.Contact(email="contact@snippets.local"), + license=openapi.License(name="BSD License"), + ), + public=True, + permission_classes=[permissions.AllowAny], +) router = routers.DefaultRouter() router.register(r'users', UserViewSet) @@ -67,7 +83,9 @@ apipatterns = [ urlpatterns = [ path('admin/', admin.site.urls), path('api/v1/', include(apipatterns), name='api'), - path('api/v1/docs', schema_view), + # path('api/v1/schema/', SpectacularAPIView.as_view(), name='schema'), + path('api/v1/docs', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), + path('api/v1/schema/redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), path('accounts/', include('allauth.urls')), path('accounts/profile/', ConfirmEmailSuccessView.as_view()), ] From 1ba84f8e24c32303bf02bb29bda712ee68c79e57 Mon Sep 17 00:00:00 2001 From: DJ Gillespie Date: Thu, 7 Apr 2022 16:57:37 -0600 Subject: [PATCH 09/12] General code cleanup --- connection/connections/plaid_client.py | 12 ------------ connection/views.py | 1 - 2 files changed, 13 deletions(-) diff --git a/connection/connections/plaid_client.py b/connection/connections/plaid_client.py index 8beb62a..95d81fc 100755 --- a/connection/connections/plaid_client.py +++ b/connection/connections/plaid_client.py @@ -21,8 +21,6 @@ def format_error(e): class Connection(AbstractConnectionClient): def __init__(self, credentials): - print("Plaid Connection Creation Initiated") - print(credentials) self.credentials = credentials.dict() # Fill in your Plaid API keys - @@ -53,16 +51,6 @@ class Connection(AbstractConnectionClient): ) api_client = plaid.ApiClient(configuration) self.client = plaid_api.PlaidApi(api_client) - - # self.client = plaid.Client( - # client_id=self.PLAID_CLIENT_ID, - # secret=self.PLAID_SECRET, - # environment=self.PLAID_ENV, - # public_key=self.PLAID_PUBLIC_KEY, - # # api_version='2019-05-29', - # api_version='2020-09-14', - # #webhook='https://qrtr-services.herokuapp.com/connection/plaid-webhook/' - # ) public_key = self.credentials.get('public_token') auth_token = self.credentials.get('auth_token') if not auth_token and public_key: diff --git a/connection/views.py b/connection/views.py index ee64914..147e067 100644 --- a/connection/views.py +++ b/connection/views.py @@ -66,7 +66,6 @@ class ConnectionViewSet(viewsets.ModelViewSet): plaid_conn = importlib.import_module(f"connection.connections.plaid_client") conn_type = ConnectionType.objects.get(name="Plaid") try: - print(f"CREATING CONNECTION, {request.data}") plaid_client = plaid_conn.Connection(request.data) except ValueError: return Response(status=status.HTTP_503, From 908d3ce6b6a277a29f1db3f6067597de2479c4e8 Mon Sep 17 00:00:00 2001 From: DJ Gillespie Date: Mon, 31 Jan 2022 19:20:44 -0700 Subject: [PATCH 10/12] added drf-yasg to reqs --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index f8fe6a2..a438490 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,6 +13,7 @@ django-cors-headers==3.7.0 django-filter==2.4.0 djangorestframework==3.12.4 djangorestframework-simplejwt==4.6.0 +drf-yasg==1.20.0 idna==2.10 oauthlib==3.1.0 plaid-python==9.2.0 From 7315d8212cb6534e2d5722053b86f74a1e745339 Mon Sep 17 00:00:00 2001 From: DJ Gillespie Date: Thu, 7 Apr 2022 17:08:41 -0600 Subject: [PATCH 11/12] removed django_rest_swagger --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a438490..3ee75e6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,6 @@ cryptography==3.4.7 defusedxml==0.7.1 dj-database-url==0.5.0 dj-rest-auth==2.1.5 -django-rest-swagger==2.2.0 Django==3.2.3 django-allauth==0.44.0 django-cors-headers==3.7.0 From f915ddc04226c36ed65e70698defdeed23e3bc88 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 7 Apr 2022 23:11:13 +0000 Subject: [PATCH 12/12] fixed auto-deploy for staging --- bitbucket-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index 4989f0e..f12feaa 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -22,4 +22,4 @@ pipelines: - pip script: - pip install -r requirements.txt - - git push https://heroku:$HEROKU_API_KEY@git.heroku.com/$HEROKU_APP_NAME_STAGING.git HEAD + - git push https://heroku:$HEROKU_API_KEY@git.heroku.com/$HEROKU_APP_NAME_STAGING.git HEAD:master