API - set language on account creation and use it in confirmation email

This commit is contained in:
Sam 2022-07-03 13:29:50 +02:00
parent c50c3c1f92
commit 5549eff08b
3 changed files with 57 additions and 18 deletions

View File

@ -1,6 +1,7 @@
import json
from datetime import datetime, timedelta
from io import BytesIO
from typing import Optional
from unittest.mock import MagicMock, Mock, patch
import pytest
@ -233,7 +234,16 @@ class TestUserRegistration(ApiTestCaseMixin):
assert data['status'] == 'success'
assert 'auth_token' not in data
def test_it_creates_user_with_inactive_account(self, app: Flask) -> None:
@pytest.mark.parametrize(
'input_language,expected_language',
[('en', 'en'), ('fr', 'fr'), ('invalid', 'en'), (None, 'en')],
)
def test_it_creates_user_with_inactive_account(
self,
app: Flask,
input_language: Optional[str],
expected_language: str,
) -> None:
client = app.test_client()
username = self.random_string()
email = self.random_email()
@ -245,6 +255,7 @@ class TestUserRegistration(ApiTestCaseMixin):
username=username,
email=email,
password=self.random_string(),
language=input_language,
)
),
content_type='application/json',
@ -254,9 +265,18 @@ class TestUserRegistration(ApiTestCaseMixin):
assert new_user.email == email
assert new_user.password is not None
assert new_user.is_active is False
assert new_user.language == expected_language
def test_it_calls_account_confirmation_email_if_payload_is_valid(
self, app: Flask, account_confirmation_email_mock: Mock
@pytest.mark.parametrize(
'input_language,expected_language',
[('en', 'en'), ('fr', 'fr'), ('invalid', 'en'), (None, 'en')],
)
def test_it_calls_account_confirmation_email_when_payload_is_valid(
self,
app: Flask,
account_confirmation_email_mock: Mock,
input_language: Optional[str],
expected_language: str,
) -> None:
client = app.test_client()
email = self.random_email()
@ -271,6 +291,7 @@ class TestUserRegistration(ApiTestCaseMixin):
username=username,
email=email,
password='12345678',
language=input_language,
)
),
content_type='application/json',
@ -279,7 +300,7 @@ class TestUserRegistration(ApiTestCaseMixin):
account_confirmation_email_mock.send.assert_called_once_with(
{
'language': 'en',
'language': expected_language,
'email': email,
},
{
@ -1227,8 +1248,16 @@ class TestUserPreferencesUpdate(ApiTestCaseMixin):
self.assert_400(response)
@pytest.mark.parametrize(
'input_language,expected_language',
[('en', 'en'), ('fr', 'fr'), ('invalid', 'en'), (None, 'en')],
)
def test_it_updates_user_preferences(
self, app: Flask, user_1: User
self,
app: Flask,
user_1: User,
input_language: Optional[str],
expected_language: str,
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1.email
@ -1241,7 +1270,7 @@ class TestUserPreferencesUpdate(ApiTestCaseMixin):
dict(
timezone='America/New_York',
weekm=True,
language='fr',
language=input_language,
imperial_units=True,
)
),
@ -1252,6 +1281,7 @@ class TestUserPreferencesUpdate(ApiTestCaseMixin):
data = json.loads(response.data.decode())
assert data['status'] == 'success'
assert data['message'] == 'user preferences updated'
assert data['data']['language'] == expected_language
assert data['data'] == jsonify_dict(user_1.serialize(user_1))
@ -2237,6 +2267,7 @@ class TestResendAccountConfirmationEmail(ApiTestCaseMixin):
) -> None:
client = app.test_client()
expected_token = self.random_string()
inactive_user.language = 'fr'
with patch('secrets.token_urlsafe', return_value=expected_token):
client.post(
@ -2248,7 +2279,7 @@ class TestResendAccountConfirmationEmail(ApiTestCaseMixin):
account_confirmation_email_mock.send.assert_called_once_with(
{
'language': 'en',
'language': inactive_user.language,
'email': inactive_user.email,
},
{

View File

@ -2,7 +2,7 @@ import datetime
import os
import re
import secrets
from typing import Dict, Tuple, Union
from typing import Dict, Optional, Tuple, Union
import jwt
from flask import Blueprint, current_app, request
@ -43,6 +43,13 @@ HEX_COLOR_REGEX = regex = "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"
NOT_FOUND_MESSAGE = 'the requested URL was not found on the server'
def get_language(language: Optional[str]) -> str:
# Note: some users may not have language preferences set
if not language or language not in current_app.config['LANGUAGES']:
language = 'en'
return language
def send_account_confirmation_email(user: User) -> None:
if current_app.config['CAN_SEND_EMAILS']:
ui_url = current_app.config['UI_URL']
@ -57,7 +64,7 @@ def send_account_confirmation_email(user: User) -> None:
),
}
user_data = {
'language': 'en',
'language': get_language(user.language),
'email': user.email,
}
account_confirmation_email.send(user_data, email_data)
@ -106,6 +113,8 @@ def register_user() -> Union[Tuple[Dict, int], HttpResponse]:
:<json string username: username (3 to 30 characters required)
:<json string email: user email
:<json string password: password (8 characters required)
:<json string lang: user language preferences (if not provided or invalid,
fallback to 'en' (english))
:statuscode 200: success
:statuscode 400:
@ -138,6 +147,7 @@ def register_user() -> Union[Tuple[Dict, int], HttpResponse]:
username = post_data.get('username')
email = post_data.get('email')
password = post_data.get('password')
language = get_language(post_data.get('language'))
try:
ret = register_controls(username, email, password)
@ -165,6 +175,7 @@ def register_user() -> Union[Tuple[Dict, int], HttpResponse]:
new_user = User(username=username, email=email, password=password)
new_user.timezone = 'Europe/Paris'
new_user.confirmation_token = secrets.token_urlsafe(30)
new_user.language = language
db.session.add(new_user)
db.session.commit()
@ -661,9 +672,7 @@ def update_user_account(auth_user: User) -> Union[Dict, HttpResponse]:
if current_app.config['CAN_SEND_EMAILS']:
ui_url = current_app.config['UI_URL']
user_data = {
'language': (
'en' if auth_user.language is None else auth_user.language
),
'language': get_language(auth_user.language),
'email': auth_user.email,
}
data = {
@ -830,7 +839,7 @@ def edit_user_preferences(auth_user: User) -> Union[Dict, HttpResponse]:
return InvalidPayloadErrorResponse()
imperial_units = post_data.get('imperial_units')
language = post_data.get('language')
language = get_language(post_data.get('language'))
timezone = post_data.get('timezone')
weekm = post_data.get('weekm')
@ -1189,7 +1198,7 @@ def request_password_reset() -> Union[Dict, HttpResponse]:
if user:
password_reset_token = user.encode_password_reset_token(user.id)
ui_url = current_app.config['UI_URL']
user_language = 'en' if user.language is None else user.language
user_language = get_language(user.language)
email_data = {
'expiration_delay': get_readable_duration(
current_app.config['PASSWORD_TOKEN_EXPIRATION_SECONDS'],
@ -1280,9 +1289,7 @@ def update_password() -> Union[Dict, HttpResponse]:
if current_app.config['CAN_SEND_EMAILS']:
password_change_email.send(
{
'language': (
'en' if user.language is None else user.language
),
'language': get_language(user.language),
'email': user.email,
},
{

View File

@ -23,6 +23,7 @@ from fittrackee.responses import (
from fittrackee.utils import get_readable_duration
from fittrackee.workouts.models import Record, Workout, WorkoutSegment
from .auth import get_language
from .decorators import authenticate, authenticate_as_admin
from .exceptions import InvalidEmailException, UserNotFoundException
from .models import User, UserSportPreference
@ -530,7 +531,7 @@ def update_user(auth_user: User, user_name: str) -> Union[Dict, HttpResponse]:
)
if current_app.config['CAN_SEND_EMAILS']:
user_language = 'en' if user.language is None else user.language
user_language = get_language(user.language)
ui_url = current_app.config['UI_URL']
if reset_password:
user_data = {