API - move some app configuration parameters in database
This commit is contained in:
parent
6447d22d97
commit
649b773ba9
3
Makefile
3
Makefile
@ -29,6 +29,9 @@ install-db:
|
||||
$(FLASK) db upgrade --directory $(MIGRATIONS)
|
||||
$(FLASK) initdata
|
||||
|
||||
init-app-config:
|
||||
$(FLASK) init-app-config
|
||||
|
||||
init-db:
|
||||
$(FLASK) drop-db
|
||||
$(FLASK) db upgrade --directory $(MIGRATIONS)
|
||||
|
@ -26,12 +26,26 @@ def create_app():
|
||||
bcrypt.init_app(app)
|
||||
migrate.init_app(app, db)
|
||||
|
||||
# get configuration from database
|
||||
from .application.models import AppConfig
|
||||
from .application.utils import init_config, update_app_config_from_database
|
||||
|
||||
with app.app_context():
|
||||
# Note: check if "app_config" table exist to avoid errors when
|
||||
# dropping tables on dev environments
|
||||
if db.engine.dialect.has_table(db.engine, 'app_config'):
|
||||
db_app_config = AppConfig.query.one_or_none()
|
||||
if not db_app_config:
|
||||
_, db_app_config = init_config()
|
||||
update_app_config_from_database(app, db_app_config)
|
||||
|
||||
from .users.auth import auth_blueprint # noqa
|
||||
from .users.users import users_blueprint # noqa
|
||||
from .activities.activities import activities_blueprint # noqa
|
||||
from .activities.records import records_blueprint # noqa
|
||||
from .activities.sports import sports_blueprint # noqa
|
||||
from .activities.stats import stats_blueprint # noqa
|
||||
from .application.config import config_blueprint # noqa
|
||||
|
||||
app.register_blueprint(users_blueprint, url_prefix='/api')
|
||||
app.register_blueprint(auth_blueprint, url_prefix='/api')
|
||||
@ -39,6 +53,7 @@ def create_app():
|
||||
app.register_blueprint(records_blueprint, url_prefix='/api')
|
||||
app.register_blueprint(sports_blueprint, url_prefix='/api')
|
||||
app.register_blueprint(stats_blueprint, url_prefix='/api')
|
||||
app.register_blueprint(config_blueprint, url_prefix='/api')
|
||||
|
||||
if app.debug:
|
||||
logging.getLogger('sqlalchemy').setLevel(logging.WARNING)
|
||||
|
146
fittrackee_api/fittrackee_api/application/config.py
Normal file
146
fittrackee_api/fittrackee_api/application/config.py
Normal file
@ -0,0 +1,146 @@
|
||||
from fittrackee_api import appLog, db
|
||||
from flask import Blueprint, current_app, jsonify, request
|
||||
from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound
|
||||
|
||||
from ..users.utils import authenticate_as_admin
|
||||
from .models import AppConfig
|
||||
from .utils import update_app_config_from_database
|
||||
|
||||
config_blueprint = Blueprint('config', __name__)
|
||||
|
||||
|
||||
@config_blueprint.route('/config', methods=['GET'])
|
||||
def get_application_config():
|
||||
"""
|
||||
Get Application config
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/config HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"data": {
|
||||
"gpx_limit_import": 10,
|
||||
"is_registration_enabled": false,
|
||||
"max_single_file_size": 1048576,
|
||||
"max_zip_file_size": 10485760,
|
||||
"max_users": 0,
|
||||
"registration": false
|
||||
},
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
:statuscode 200: success
|
||||
:statuscode 500: Error on getting configuration.
|
||||
"""
|
||||
|
||||
try:
|
||||
config = AppConfig.query.one()
|
||||
response_object = {'status': 'success', 'data': config.serialize()}
|
||||
return jsonify(response_object), 200
|
||||
except (MultipleResultsFound, NoResultFound) as e:
|
||||
appLog.error(e)
|
||||
response_object = {
|
||||
'status': 'error',
|
||||
'message': 'Error on getting configuration.',
|
||||
}
|
||||
return jsonify(response_object), 500
|
||||
|
||||
|
||||
@config_blueprint.route('/config', methods=['PATCH'])
|
||||
@authenticate_as_admin
|
||||
def update_application_config(auth_user_id):
|
||||
"""
|
||||
Update Application config
|
||||
|
||||
Authenticated user must be an admin
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/config HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"data": {
|
||||
"gpx_limit_import": 10,
|
||||
"is_registration_enabled": true,
|
||||
"max_single_file_size": 1048576,
|
||||
"max_zip_file_size": 10485760,
|
||||
"max_users": 10,
|
||||
"registration": true
|
||||
},
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
:param integer auth_user_id: authenticate user id (from JSON Web Token)
|
||||
|
||||
:<json integrer gpx_limit_import: max number of files in zip archive
|
||||
:<json integrer max_single_file_size: max size of a single file
|
||||
:<json integrer max_zip_file_size: max size of a zip archive
|
||||
:<json integrer max_users: max users allowed to register on instance
|
||||
:<json boolean registration: is registration enabled ?
|
||||
|
||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||
|
||||
:statuscode 200: success
|
||||
:statuscode 400: invalid payload
|
||||
:statuscode 401:
|
||||
- Provide a valid auth token.
|
||||
- Signature expired. Please log in again.
|
||||
- Invalid token. Please log in again.
|
||||
:statuscode 403: You do not have permissions.
|
||||
:statuscode 500: Error on updating configuration.
|
||||
"""
|
||||
config_data = request.get_json()
|
||||
if not config_data:
|
||||
response_object = {'status': 'error', 'message': 'Invalid payload.'}
|
||||
return jsonify(response_object), 400
|
||||
|
||||
try:
|
||||
config = AppConfig.query.one()
|
||||
if 'gpx_limit_import' in config_data:
|
||||
config.gpx_limit_import = config_data.get('gpx_limit_import')
|
||||
if 'max_single_file_size' in config_data:
|
||||
config.max_single_file_size = config_data.get(
|
||||
'max_single_file_size'
|
||||
)
|
||||
if 'max_zip_file_size' in config_data:
|
||||
config.max_zip_file_size = config_data.get('max_zip_file_size')
|
||||
if 'max_users' in config_data:
|
||||
config.max_users = config_data.get('max_users')
|
||||
if 'registration' in config_data:
|
||||
config.registration = config_data.get('registration')
|
||||
|
||||
db.session.commit()
|
||||
update_app_config_from_database(current_app, config)
|
||||
|
||||
response_object = {'status': 'success', 'data': config.serialize()}
|
||||
code = 200
|
||||
|
||||
except Exception as e:
|
||||
appLog.error(e)
|
||||
response_object = {
|
||||
'status': 'error',
|
||||
'message': 'Error on updating configuration.',
|
||||
}
|
||||
code = 500
|
||||
return jsonify(response_object), code
|
30
fittrackee_api/fittrackee_api/application/models.py
Normal file
30
fittrackee_api/fittrackee_api/application/models.py
Normal file
@ -0,0 +1,30 @@
|
||||
from fittrackee_api import db
|
||||
|
||||
from ..users.models import User
|
||||
|
||||
|
||||
class AppConfig(db.Model):
|
||||
__tablename__ = 'app_config'
|
||||
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
||||
registration = db.Column(db.Boolean, default=False, nullable=False)
|
||||
max_users = db.Column(db.Integer, default=0, nullable=False)
|
||||
gpx_limit_import = db.Column(db.Integer, default=10, nullable=False)
|
||||
max_single_file_size = db.Column(
|
||||
db.Integer, default=1048576, nullable=False
|
||||
)
|
||||
max_zip_file_size = db.Column(db.Integer, default=10485760, nullable=False)
|
||||
|
||||
@property
|
||||
def is_registration_enabled(self):
|
||||
nb_users = User.query.count()
|
||||
return self.registration and nb_users < self.max_users
|
||||
|
||||
def serialize(self):
|
||||
return {
|
||||
"gpx_limit_import": self.gpx_limit_import,
|
||||
"is_registration_enabled": self.is_registration_enabled,
|
||||
"max_single_file_size": self.max_single_file_size,
|
||||
"max_zip_file_size": self.max_zip_file_size,
|
||||
"max_users": self.max_users,
|
||||
"registration": self.registration,
|
||||
}
|
46
fittrackee_api/fittrackee_api/application/utils.py
Normal file
46
fittrackee_api/fittrackee_api/application/utils.py
Normal file
@ -0,0 +1,46 @@
|
||||
import os
|
||||
|
||||
from fittrackee_api import db
|
||||
|
||||
from .models import AppConfig
|
||||
|
||||
MAX_FILE_SIZE = 1 * 1024 * 1024 # 1MB
|
||||
|
||||
|
||||
def init_config():
|
||||
"""
|
||||
init application configuration if not existing in database
|
||||
|
||||
Note: get some configuration values from env variables (for FitTrackee versions
|
||||
prior to v0.3.0)
|
||||
"""
|
||||
existing_config = AppConfig.query.one_or_none()
|
||||
if not existing_config:
|
||||
config = AppConfig()
|
||||
config.registration = (
|
||||
False
|
||||
if os.getenv('REACT_APP_ALLOW_REGISTRATION') == "false"
|
||||
else True
|
||||
)
|
||||
config.max_users = 0
|
||||
config.max_single_file_size = os.environ.get(
|
||||
'REACT_APP_MAX_SINGLE_FILE_SIZE', MAX_FILE_SIZE
|
||||
)
|
||||
config.max_zip_file_size = os.environ.get(
|
||||
'REACT_APP_MAX_ZIP_FILE_SIZE', MAX_FILE_SIZE * 10
|
||||
)
|
||||
db.session.add(config)
|
||||
db.session.commit()
|
||||
return True, config
|
||||
return False, existing_config
|
||||
|
||||
|
||||
def update_app_config_from_database(current_app, db_config):
|
||||
current_app.config['gpx_limit_import'] = db_config.gpx_limit_import
|
||||
current_app.config['max_single_file_size'] = db_config.max_single_file_size
|
||||
current_app.config['MAX_CONTENT_LENGTH'] = db_config.max_zip_file_size
|
||||
current_app.config['max_users'] = db_config.max_users
|
||||
current_app.config['registration'] = db_config.registration
|
||||
current_app.config[
|
||||
'is_registration_enabled'
|
||||
] = db_config.is_registration_enabled
|
@ -2,8 +2,6 @@ import os
|
||||
|
||||
from flask import current_app
|
||||
|
||||
MAX_FILE_SIZE = 1 * 1024 * 1024 # 1MB
|
||||
|
||||
|
||||
class BaseConfig:
|
||||
"""Base configuration"""
|
||||
@ -15,19 +13,8 @@ class BaseConfig:
|
||||
TOKEN_EXPIRATION_DAYS = 30
|
||||
TOKEN_EXPIRATION_SECONDS = 0
|
||||
UPLOAD_FOLDER = os.path.join(current_app.root_path, 'uploads')
|
||||
# for gpx zip
|
||||
MAX_CONTENT_LENGTH = int(
|
||||
os.environ.get('REACT_APP_MAX_ZIP_FILE_SIZE', MAX_FILE_SIZE * 10)
|
||||
)
|
||||
# for single file (gpx or picture)
|
||||
MAX_SINGLE_FILE = int(
|
||||
os.environ.get('REACT_APP_MAX_SINGLE_FILE_SIZE', MAX_FILE_SIZE)
|
||||
)
|
||||
PICTURE_ALLOWED_EXTENSIONS = {'jpg', 'png', 'gif'}
|
||||
ACTIVITY_ALLOWED_EXTENSIONS = {'gpx', 'zip'}
|
||||
REGISTRATION_ALLOWED = (
|
||||
False if os.getenv('REACT_APP_ALLOW_REGISTRATION') == "false" else True
|
||||
)
|
||||
|
||||
|
||||
class DevelopmentConfig(BaseConfig):
|
||||
|
@ -4,16 +4,58 @@ import os
|
||||
import pytest
|
||||
from fittrackee_api import create_app, db
|
||||
from fittrackee_api.activities.models import Activity, ActivitySegment, Sport
|
||||
from fittrackee_api.application.models import AppConfig
|
||||
from fittrackee_api.application.utils import update_app_config_from_database
|
||||
from fittrackee_api.users.models import User
|
||||
|
||||
os.environ["FLASK_ENV"] = 'testing'
|
||||
os.environ["APP_SETTINGS"] = 'fittrackee_api.config.TestingConfig'
|
||||
|
||||
|
||||
def app_config_with_registration():
|
||||
config = AppConfig()
|
||||
config.gpx_limit_import = 10
|
||||
config.max_single_file_size = 1 * 1024 * 1024
|
||||
config.max_zip_file_size = 1 * 1024 * 1024 * 10
|
||||
config.max_users = 10
|
||||
config.registration = True
|
||||
db.session.add(config)
|
||||
db.session.commit()
|
||||
return config
|
||||
|
||||
|
||||
def app_config_registration_disabled():
|
||||
config = AppConfig()
|
||||
config.gpx_limit_import = 20
|
||||
config.max_single_file_size = 10485
|
||||
config.max_zip_file_size = 104850
|
||||
config.max_users = 0
|
||||
config.registration = False
|
||||
db.session.add(config)
|
||||
db.session.commit()
|
||||
return config
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app():
|
||||
app = create_app()
|
||||
app.config['REGISTRATION_ALLOWED'] = True
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
app_db_config = app_config_with_registration()
|
||||
update_app_config_from_database(app, app_db_config)
|
||||
yield app
|
||||
db.session.remove()
|
||||
db.drop_all()
|
||||
# close unused idle connections => avoid the following error:
|
||||
# FATAL: remaining connection slots are reserved for non-replication
|
||||
# superuser connections
|
||||
db.engine.dispose()
|
||||
return app
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app_no_config():
|
||||
app = create_app()
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
yield app
|
||||
@ -29,9 +71,10 @@ def app():
|
||||
@pytest.fixture
|
||||
def app_no_registration():
|
||||
app = create_app()
|
||||
app.config['REGISTRATION_ALLOWED'] = False
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
app_db_config = app_config_registration_disabled()
|
||||
update_app_config_from_database(app, app_db_config)
|
||||
yield app
|
||||
db.session.remove()
|
||||
db.drop_all()
|
||||
@ -39,6 +82,19 @@ def app_no_registration():
|
||||
return app
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def app_config():
|
||||
config = AppConfig()
|
||||
config.gpx_limit_import = 10
|
||||
config.max_single_file_size = 1048576
|
||||
config.max_zip_file_size = 10485760
|
||||
config.max_users = 0
|
||||
config.registration = False
|
||||
db.session.add(config)
|
||||
db.session.commit()
|
||||
return config
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def user_1():
|
||||
user = User(username='test', email='test@test.com', password='12345678')
|
||||
|
214
fittrackee_api/fittrackee_api/tests/test_app_config_api.py
Normal file
214
fittrackee_api/fittrackee_api/tests/test_app_config_api.py
Normal file
@ -0,0 +1,214 @@
|
||||
import json
|
||||
|
||||
|
||||
def test_get_config(app, user_1):
|
||||
client = app.test_client()
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(email='test@test.com', password='12345678')),
|
||||
content_type='application/json',
|
||||
)
|
||||
response = client.get(
|
||||
'/api/config',
|
||||
headers=dict(
|
||||
Authorization='Bearer '
|
||||
+ json.loads(resp_login.data.decode())['auth_token']
|
||||
),
|
||||
)
|
||||
data = json.loads(response.data.decode())
|
||||
|
||||
assert response.status_code == 200
|
||||
assert 'success' in data['status']
|
||||
|
||||
assert data['data']['gpx_limit_import'] == 10
|
||||
assert data['data']['is_registration_enabled'] is True
|
||||
assert data['data']['max_single_file_size'] == 1048576
|
||||
assert data['data']['max_zip_file_size'] == 10485760
|
||||
assert data['data']['max_users'] == 10
|
||||
assert data['data']['registration'] is True
|
||||
|
||||
|
||||
def test_get_config_no_config(app_no_config, user_1_admin):
|
||||
client = app_no_config.test_client()
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(email='admin@example.com', password='12345678')),
|
||||
content_type='application/json',
|
||||
)
|
||||
response = client.get(
|
||||
'/api/config',
|
||||
content_type='application/json',
|
||||
headers=dict(
|
||||
Authorization='Bearer '
|
||||
+ json.loads(resp_login.data.decode())['auth_token']
|
||||
),
|
||||
)
|
||||
data = json.loads(response.data.decode())
|
||||
|
||||
assert response.status_code == 500
|
||||
assert 'error' in data['status']
|
||||
assert 'Error on getting configuration.' in data['message']
|
||||
|
||||
|
||||
def test_get_config_several_config(
|
||||
app, app_config, user_1_admin
|
||||
):
|
||||
client = app.test_client()
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(email='admin@example.com', password='12345678')),
|
||||
content_type='application/json',
|
||||
)
|
||||
response = client.get(
|
||||
'/api/config',
|
||||
content_type='application/json',
|
||||
headers=dict(
|
||||
Authorization='Bearer '
|
||||
+ json.loads(resp_login.data.decode())['auth_token']
|
||||
),
|
||||
)
|
||||
data = json.loads(response.data.decode())
|
||||
|
||||
assert response.status_code == 500
|
||||
assert 'error' in data['status']
|
||||
assert 'Error on getting configuration.' in data['message']
|
||||
|
||||
|
||||
def test_update_config_as_admin(app, user_1_admin):
|
||||
client = app.test_client()
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(email='admin@example.com', password='12345678')),
|
||||
content_type='application/json',
|
||||
)
|
||||
response = client.patch(
|
||||
'/api/config',
|
||||
content_type='application/json',
|
||||
data=json.dumps(
|
||||
dict(registration=True, max_users=10)
|
||||
),
|
||||
headers=dict(
|
||||
Authorization='Bearer '
|
||||
+ json.loads(resp_login.data.decode())['auth_token']
|
||||
),
|
||||
)
|
||||
data = json.loads(response.data.decode())
|
||||
|
||||
assert response.status_code == 200
|
||||
assert 'success' in data['status']
|
||||
assert data['data']['gpx_limit_import'] == 10
|
||||
assert data['data']['is_registration_enabled'] is True
|
||||
assert data['data']['max_single_file_size'] == 1048576
|
||||
assert data['data']['max_zip_file_size'] == 10485760
|
||||
assert data['data']['max_users'] == 10
|
||||
assert data['data']['registration'] is True
|
||||
|
||||
|
||||
def test_update_full_config_as_admin(app, user_1_admin):
|
||||
client = app.test_client()
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(email='admin@example.com', password='12345678')),
|
||||
content_type='application/json',
|
||||
)
|
||||
response = client.patch(
|
||||
'/api/config',
|
||||
content_type='application/json',
|
||||
data=json.dumps(
|
||||
dict(
|
||||
gpx_limit_import=20,
|
||||
max_single_file_size=10000,
|
||||
max_zip_file_size=25000,
|
||||
max_users=50,
|
||||
registration=True,
|
||||
)
|
||||
),
|
||||
headers=dict(
|
||||
Authorization='Bearer '
|
||||
+ json.loads(resp_login.data.decode())['auth_token']
|
||||
),
|
||||
)
|
||||
data = json.loads(response.data.decode())
|
||||
|
||||
assert response.status_code == 200
|
||||
assert 'success' in data['status']
|
||||
assert data['data']['gpx_limit_import'] == 20
|
||||
assert data['data']['is_registration_enabled'] is True
|
||||
assert data['data']['max_single_file_size'] == 10000
|
||||
assert data['data']['max_zip_file_size'] == 25000
|
||||
assert data['data']['max_users'] == 50
|
||||
assert data['data']['registration'] is True
|
||||
|
||||
|
||||
def test_update_config_not_admin(app, user_1):
|
||||
client = app.test_client()
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(email='test@test.com', password='12345678')),
|
||||
content_type='application/json',
|
||||
)
|
||||
response = client.patch(
|
||||
'/api/config',
|
||||
content_type='application/json',
|
||||
data=json.dumps(
|
||||
dict(registration=True, max_users=10)
|
||||
),
|
||||
headers=dict(
|
||||
Authorization='Bearer '
|
||||
+ json.loads(resp_login.data.decode())['auth_token']
|
||||
),
|
||||
)
|
||||
data = json.loads(response.data.decode())
|
||||
|
||||
assert response.status_code == 403
|
||||
assert 'success' not in data['status']
|
||||
assert 'error' in data['status']
|
||||
assert 'You do not have permissions.' in data['message']
|
||||
|
||||
|
||||
def test_update_config_invalid_payload(app, user_1_admin):
|
||||
client = app.test_client()
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(email='admin@example.com', password='12345678')),
|
||||
content_type='application/json',
|
||||
)
|
||||
response = client.patch(
|
||||
'/api/config',
|
||||
content_type='application/json',
|
||||
data=json.dumps(dict()),
|
||||
headers=dict(
|
||||
Authorization='Bearer '
|
||||
+ json.loads(resp_login.data.decode())['auth_token']
|
||||
),
|
||||
)
|
||||
data = json.loads(response.data.decode())
|
||||
|
||||
assert response.status_code == 400
|
||||
assert 'error' in data['status']
|
||||
assert 'Invalid payload.' in data['message']
|
||||
|
||||
|
||||
def test_update_config_no_config(app_no_config, user_1_admin):
|
||||
client = app_no_config.test_client()
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(email='admin@example.com', password='12345678')),
|
||||
content_type='application/json',
|
||||
)
|
||||
response = client.patch(
|
||||
'/api/config',
|
||||
content_type='application/json',
|
||||
data=json.dumps(
|
||||
dict(registration=True, max_users=10)
|
||||
),
|
||||
headers=dict(
|
||||
Authorization='Bearer '
|
||||
+ json.loads(resp_login.data.decode())['auth_token']
|
||||
),
|
||||
)
|
||||
data = json.loads(response.data.decode())
|
||||
|
||||
assert response.status_code == 500
|
||||
assert 'error' in data['status']
|
||||
assert 'Error on updating configuration.' in data['message']
|
14
fittrackee_api/fittrackee_api/tests/test_app_config_model.py
Normal file
14
fittrackee_api/fittrackee_api/tests/test_app_config_model.py
Normal file
@ -0,0 +1,14 @@
|
||||
from fittrackee_api.application.models import AppConfig
|
||||
|
||||
|
||||
def test_application_config(app):
|
||||
app_config = AppConfig.query.first()
|
||||
assert 1 == app_config.id
|
||||
|
||||
serialized_app_config = app_config.serialize()
|
||||
assert serialized_app_config['gpx_limit_import'] == 10
|
||||
assert serialized_app_config['is_registration_enabled'] is True
|
||||
assert serialized_app_config['max_single_file_size'] == 1048576
|
||||
assert serialized_app_config['max_zip_file_size'] == 10485760
|
||||
assert serialized_app_config['max_users'] == 10
|
||||
assert serialized_app_config['registration'] is True
|
@ -281,6 +281,46 @@ def test_user_registration_not_allowed(app_no_registration):
|
||||
assert data['message'] == 'Error. Registration is disabled.'
|
||||
|
||||
|
||||
def test_user_registration_max_users_exceeded(
|
||||
app, user_1_admin, user_2, user_3
|
||||
):
|
||||
client = app.test_client()
|
||||
|
||||
resp_login = client.post(
|
||||
'/api/auth/login',
|
||||
data=json.dumps(dict(email='admin@example.com', password='12345678')),
|
||||
content_type='application/json',
|
||||
)
|
||||
client.patch(
|
||||
'/api/config',
|
||||
content_type='application/json',
|
||||
data=json.dumps(dict(max_users=3, registration=True)),
|
||||
headers=dict(
|
||||
Authorization='Bearer '
|
||||
+ json.loads(resp_login.data.decode())['auth_token']
|
||||
),
|
||||
)
|
||||
|
||||
response = client.post(
|
||||
'/api/auth/register',
|
||||
data=json.dumps(
|
||||
dict(
|
||||
username='user4',
|
||||
email='user4@test.com',
|
||||
password='12345678',
|
||||
password_conf='12345678',
|
||||
)
|
||||
),
|
||||
content_type='application/json',
|
||||
)
|
||||
|
||||
assert response.content_type == 'application/json'
|
||||
assert response.status_code == 403
|
||||
data = json.loads(response.data.decode())
|
||||
assert data['status'] == 'error'
|
||||
assert data['message'] == 'Error. Registration is disabled.'
|
||||
|
||||
|
||||
def test_login_registered_user(app, user_1):
|
||||
client = app.test_client()
|
||||
response = client.post(
|
||||
|
@ -78,7 +78,7 @@ def register_user():
|
||||
Error. Please try again or contact the administrator.
|
||||
|
||||
"""
|
||||
if not current_app.config.get('REGISTRATION_ALLOWED'):
|
||||
if not current_app.config.get('is_registration_enabled'):
|
||||
response_object = {
|
||||
'status': 'error',
|
||||
'message': 'Error. Registration is disabled.',
|
||||
|
@ -53,7 +53,7 @@ def verify_extension_and_size(file_type, req):
|
||||
if '.' in file.filename
|
||||
else None
|
||||
)
|
||||
max_file_size = current_app.config['MAX_SINGLE_FILE']
|
||||
max_file_size = current_app.config['max_single_file_size']
|
||||
|
||||
if not (
|
||||
file_extension
|
||||
|
@ -0,0 +1,36 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 8a0aad4c838c
|
||||
Revises: 1345afe3b11d
|
||||
Create Date: 2019-11-13 13:14:20.147296
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '8a0aad4c838c'
|
||||
down_revision = '1345afe3b11d'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('app_config',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('registration', sa.Boolean(), nullable=False),
|
||||
sa.Column('max_users', sa.Integer(), nullable=False),
|
||||
sa.Column('gpx_limit_import', sa.Integer(), nullable=False),
|
||||
sa.Column('max_single_file_size', sa.Integer(), nullable=False),
|
||||
sa.Column('max_zip_file_size', sa.Integer(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('app_config')
|
||||
# ### end Alembic commands ###
|
@ -3,6 +3,7 @@ import shutil
|
||||
from fittrackee_api import create_app, db
|
||||
from fittrackee_api.activities.models import Activity, Sport
|
||||
from fittrackee_api.activities.utils import update_activity
|
||||
from fittrackee_api.application.utils import init_config
|
||||
from fittrackee_api.users.models import User
|
||||
from tqdm import tqdm
|
||||
|
||||
@ -75,5 +76,19 @@ def recalculate():
|
||||
db.session.commit()
|
||||
|
||||
|
||||
@app.cli.command('init-app-config')
|
||||
def init_app_config():
|
||||
"""Init application configuration."""
|
||||
print("Init application configuration")
|
||||
config_created, _ = init_config()
|
||||
if config_created:
|
||||
print("Creation done!")
|
||||
else:
|
||||
print(
|
||||
"Application configuration already existing in database. "
|
||||
"Please use web application to update it."
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run()
|
||||
|
Loading…
Reference in New Issue
Block a user