Skip to content

Commit 0b36179

Browse files
renzonrenzon
authored andcommitted
Implemented Celery
Used RabbitMQ as broker and Redis as result store First test to run only one async task close #2038
1 parent 7074d5d commit 0b36179

20 files changed

Lines changed: 183 additions & 86 deletions

‎Pipfile‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ django-extensions = "*"
2828
activecampaign-python = {git = "https://github.com/renzon/activecampaign-python.git",ref = "last-v1-api"}
2929
codecov = "*"
3030
django-bootstrap4 = "*"
31+
celery = "*"
32+
redis = "*"
3133

3234
[dev-packages]
3335
faker = "*"

‎Pipfile.lock‎

Lines changed: 63 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Procfile‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
release: python manage.py sync_roles & python manage.py migrate --noinput
22
web: newrelic-admin run-program gunicorn pythonpro.wsgi --log-file -
3+
worker: newrelic-admin run-program celery -A pythonpro.celery worker --loglevel=info

‎contrib/env-sample‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,9 @@ ACTIVE_CAMPAIGN_TURNED_ON=true
4040

4141
# Google Tag Manager Config
4242
GOOGLE_TAG_MANAGER_ID=GTM-ABC1234
43+
44+
#CELERY_CONFIG
45+
46+
CLOUDAMQP_URL=amqp://guest:guest@localhost:5672
47+
REDIS_URL=redis://localhost:6379/1
4348

‎docker-compose.yml‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,16 @@ services:
1212
- POSTGRES_PASSWORD=pythonpro
1313
- POSTGRES_USER=pythonpro
1414
ports:
15-
- 5433:5432
15+
- 5433:5432
16+
celery_broker:
17+
container_name: pythonpro_celery_broker
18+
image: rabbitmq:3.8.2
19+
restart: always
20+
ports:
21+
- 5672:5672
22+
redis:
23+
container_name: pythonpro_redis
24+
image: redis:5.0.7
25+
restart: always
26+
ports:
27+
- 6379:6379

‎pythonpro/__init__.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .celery import app # noqa

‎pythonpro/celery.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import os
2+
3+
from celery import Celery
4+
from django.conf import settings
5+
6+
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pythonpro.settings')
7+
app = Celery('pythonpro.celery')
8+
app.config_from_object('django.conf:settings')
9+
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

‎pythonpro/core/tests/test_lead_landing_page.py‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@
88

99
from pythonpro.absolute_uri import build_absolute_uri
1010
from pythonpro.core.models import UserInteraction
11-
from pythonpro.django_assertions import (
12-
dj_assert_not_contains, dj_assert_contains, dj_assert_template_used
13-
)
11+
from pythonpro.django_assertions import (dj_assert_contains, dj_assert_not_contains, dj_assert_template_used)
1412

1513

1614
@pytest.fixture
@@ -98,12 +96,17 @@ def resp_lead_change_pasword(resp_lead_creation, client):
9896

9997
@pytest.fixture(autouse=True)
10098
def sync_user(mocker):
101-
return mocker.patch('pythonpro.domain.user_facade._discourse_facade.sync_user')
99+
return mocker.patch('pythonpro.domain.user_facade.sync_user_on_discourse')
102100

103101

104-
def test_user_discourse_sync(resp_lead_creation, django_user_model, sync_user):
102+
@pytest.fixture(autouse=True)
103+
def sync_user_delay(mocker):
104+
return mocker.patch('pythonpro.domain.user_facade.sync_user_on_discourse.delay')
105+
106+
107+
def test_user_discourse_sync(resp_lead_creation, django_user_model, sync_user_delay):
105108
user = django_user_model.objects.first()
106-
sync_user.assert_called_once_with(user)
109+
sync_user_delay.assert_called_once_with(user.id)
107110

108111

109112
def test_lead_creation(resp_lead_creation, django_user_model):

‎pythonpro/discourse/facade.py‎

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from logging import Logger
66
from urllib import parse
77

8-
import requests
98
from django.conf import settings
109

1110
_key_bytes = settings.DISCOURSE_SSO_SECRET.encode('utf-8')
@@ -43,37 +42,6 @@ def generate_discourse_login_url(user, payload, signature):
4342
return _generate_discourse_login_url(nonce, user)
4443

4544

46-
def sync_user(user):
47-
"""
48-
Synchronize user data on forum if API is set
49-
:param user: Django user
50-
:return: returns nothing
51-
"""
52-
can_make_api_call = bool(settings.DISCOURSE_API_KEY and settings.DISCOURSE_API_USER)
53-
can_work_without_sync = not (settings.DISCOURSE_BASE_URL or can_make_api_call)
54-
if can_work_without_sync:
55-
return
56-
elif not can_make_api_call:
57-
raise MissingDiscourseAPICredentials('Must define both DISCOURSE_API_KEY and DISCOURSE_API_USER configs')
58-
# https://meta.discourse.org/t/sync-sso-user-data-with-the-sync-sso-route/84398
59-
params = {
60-
'email': user.email,
61-
'external_id': user.id,
62-
'require_activation': 'false',
63-
'groups': ','.join(g.name for g in user.groups.all())
64-
}
65-
sso_payload, signature = generate_sso_payload_and_signature(params)
66-
# query_string = parse.urlencode()
67-
url = f'{settings.DISCOURSE_BASE_URL}/admin/users/sync_sso'
68-
headers = {
69-
'content-type': 'multipart/form-data',
70-
'Api-Key': settings.DISCOURSE_API_KEY,
71-
'Api-Username': settings.DISCOURSE_API_USER,
72-
}
73-
74-
return requests.post(url, data={'sso': sso_payload, 'sig': signature}, headers=headers)
75-
76-
7745
def _generate_discourse_login_url(nonce, user):
7846
params = {
7947
'nonce': nonce,

‎pythonpro/discourse/tests/test_sync.py‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import pytest
22
import responses
33

4+
import pythonpro.domain.user_facade
45
from pythonpro.discourse import facade
56

67

@@ -14,21 +15,21 @@ def test_missing_discourse_api_key(settings, no_responses):
1415
settings.DISCOURSE_API_KEY = ''
1516
settings.DISCOURSE_BASE_URL = 'https://forum.python.pro.br/'
1617
with pytest.raises(facade.MissingDiscourseAPICredentials):
17-
facade.sync_user(None)
18+
pythonpro.domain.user_facade.sync_user_on_discourse(None)
1819

1920

2021
def test_missing_discourse_api_user(settings, no_responses):
2122
settings.DISCOURSE_API_USER = ''
2223
settings.DISCOURSE_BASE_URL = 'https://forum.python.pro.br/'
2324
with pytest.raises(facade.MissingDiscourseAPICredentials):
24-
facade.sync_user(None)
25+
pythonpro.domain.user_facade.sync_user_on_discourse(None)
2526

2627

2728
def test_no_integration_on_missing_url_config(settings, no_responses):
2829
settings.DISCOURSE_API_USER = ''
2930
settings.DISCOURSE_API_KEY = ''
3031
settings.DISCOURSE_BASE_URL = ''
31-
assert facade.sync_user(None) is None
32+
assert pythonpro.domain.user_facade.sync_user_on_discourse(None) is None
3233

3334

3435
@pytest.fixture
@@ -43,7 +44,7 @@ def resps(settings):
4344

4445

4546
def test_user_sync(logged_user, resps):
46-
facade.sync_user(logged_user)
47+
pythonpro.domain.user_facade.sync_user_on_discourse(logged_user)
4748

4849

4950
success_response = {

0 commit comments

Comments
 (0)