API & Client - max sizes and max number of files must be greater than 0 - #71
This commit is contained in:
parent
c4e579dd25
commit
fbc40385a0
@ -196,11 +196,11 @@
|
|||||||
</dd>
|
</dd>
|
||||||
<dt class="field-even">Request JSON Object</dt>
|
<dt class="field-even">Request JSON Object</dt>
|
||||||
<dd class="field-even"><ul class="simple">
|
<dd class="field-even"><ul class="simple">
|
||||||
<li><p><strong>gpx_limit_import</strong> (<em>integrer</em>) – max number of files in zip archive</p></li>
|
<li><p><strong>gpx_limit_import</strong> (<em>integer</em>) – max number of files in zip archive</p></li>
|
||||||
<li><p><strong>is_registration_enabled</strong> (<em>boolean</em>) – is registration enabled ?</p></li>
|
<li><p><strong>is_registration_enabled</strong> (<em>boolean</em>) – is registration enabled ?</p></li>
|
||||||
<li><p><strong>max_single_file_size</strong> (<em>integrer</em>) – max size of a single file</p></li>
|
<li><p><strong>max_single_file_size</strong> (<em>integer</em>) – max size of a single file</p></li>
|
||||||
<li><p><strong>max_zip_file_size</strong> (<em>integrer</em>) – max size of a zip archive</p></li>
|
<li><p><strong>max_zip_file_size</strong> (<em>integer</em>) – max size of a zip archive</p></li>
|
||||||
<li><p><strong>max_users</strong> (<em>integrer</em>) – max users allowed to register on instance</p></li>
|
<li><p><strong>max_users</strong> (<em>integer</em>) – max users allowed to register on instance</p></li>
|
||||||
</ul>
|
</ul>
|
||||||
</dd>
|
</dd>
|
||||||
<dt class="field-odd">Request Headers</dt>
|
<dt class="field-odd">Request Headers</dt>
|
||||||
|
File diff suppressed because one or more lines are too long
@ -12,7 +12,7 @@ from fittrackee.responses import (
|
|||||||
from fittrackee.users.decorators import authenticate_as_admin
|
from fittrackee.users.decorators import authenticate_as_admin
|
||||||
|
|
||||||
from .models import AppConfig
|
from .models import AppConfig
|
||||||
from .utils import update_app_config_from_database
|
from .utils import update_app_config_from_database, verify_app_config
|
||||||
|
|
||||||
config_blueprint = Blueprint('config', __name__)
|
config_blueprint = Blueprint('config', __name__)
|
||||||
|
|
||||||
@ -96,11 +96,11 @@ def update_application_config(auth_user_id: int) -> Union[Dict, HttpResponse]:
|
|||||||
|
|
||||||
:param integer auth_user_id: authenticate user id (from JSON Web Token)
|
: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 integer gpx_limit_import: max number of files in zip archive
|
||||||
:<json boolean is_registration_enabled: is registration enabled ?
|
:<json boolean is_registration_enabled: is registration enabled ?
|
||||||
:<json integrer max_single_file_size: max size of a single file
|
:<json integer max_single_file_size: max size of a single file
|
||||||
:<json integrer max_zip_file_size: max size of a zip archive
|
:<json integer max_zip_file_size: max size of a zip archive
|
||||||
:<json integrer max_users: max users allowed to register on instance
|
:<json integer max_users: max users allowed to register on instance
|
||||||
|
|
||||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||||
|
|
||||||
@ -117,6 +117,10 @@ def update_application_config(auth_user_id: int) -> Union[Dict, HttpResponse]:
|
|||||||
if not config_data:
|
if not config_data:
|
||||||
return InvalidPayloadErrorResponse()
|
return InvalidPayloadErrorResponse()
|
||||||
|
|
||||||
|
ret = verify_app_config(config_data)
|
||||||
|
if ret:
|
||||||
|
return InvalidPayloadErrorResponse(message=ret)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
config = AppConfig.query.one()
|
config = AppConfig.query.one()
|
||||||
if 'gpx_limit_import' in config_data:
|
if 'gpx_limit_import' in config_data:
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
from typing import Tuple
|
from typing import Dict, List, Tuple
|
||||||
|
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
|
|
||||||
@ -49,3 +49,30 @@ def update_app_config_from_database(
|
|||||||
current_app.config[
|
current_app.config[
|
||||||
'is_registration_enabled'
|
'is_registration_enabled'
|
||||||
] = db_config.is_registration_enabled
|
] = db_config.is_registration_enabled
|
||||||
|
|
||||||
|
|
||||||
|
def verify_app_config(config_data: Dict) -> List:
|
||||||
|
"""
|
||||||
|
Verify if application config is valid.
|
||||||
|
|
||||||
|
If not, it returns not empty string
|
||||||
|
"""
|
||||||
|
ret = []
|
||||||
|
if (
|
||||||
|
'gpx_limit_import' in config_data
|
||||||
|
and config_data['gpx_limit_import'] <= 0
|
||||||
|
):
|
||||||
|
ret.append('Max. files in a zip archive must be greater than 0')
|
||||||
|
|
||||||
|
if (
|
||||||
|
'max_single_file_size' in config_data
|
||||||
|
and config_data['max_single_file_size'] <= 0
|
||||||
|
):
|
||||||
|
ret.append('Max. size of uploaded files must be greater than 0')
|
||||||
|
|
||||||
|
if (
|
||||||
|
'max_zip_file_size' in config_data
|
||||||
|
and config_data['max_zip_file_size'] <= 0
|
||||||
|
):
|
||||||
|
ret.append('Max. size of zip archive must be greater than 0')
|
||||||
|
return ret
|
||||||
|
6
fittrackee/dist/asset-manifest.json
vendored
6
fittrackee/dist/asset-manifest.json
vendored
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"files": {
|
"files": {
|
||||||
"main.css": "/static/css/main.376b8924.chunk.css",
|
"main.css": "/static/css/main.376b8924.chunk.css",
|
||||||
"main.js": "/static/js/main.7c5c861a.chunk.js",
|
"main.js": "/static/js/main.4387b246.chunk.js",
|
||||||
"main.js.map": "/static/js/main.7c5c861a.chunk.js.map",
|
"main.js.map": "/static/js/main.4387b246.chunk.js.map",
|
||||||
"runtime-main.js": "/static/js/runtime-main.1240af94.js",
|
"runtime-main.js": "/static/js/runtime-main.1240af94.js",
|
||||||
"runtime-main.js.map": "/static/js/runtime-main.1240af94.js.map",
|
"runtime-main.js.map": "/static/js/runtime-main.1240af94.js.map",
|
||||||
"static/js/2.301144a0.chunk.js": "/static/js/2.301144a0.chunk.js",
|
"static/js/2.301144a0.chunk.js": "/static/js/2.301144a0.chunk.js",
|
||||||
@ -19,6 +19,6 @@
|
|||||||
"static/js/runtime-main.1240af94.js",
|
"static/js/runtime-main.1240af94.js",
|
||||||
"static/js/2.301144a0.chunk.js",
|
"static/js/2.301144a0.chunk.js",
|
||||||
"static/css/main.376b8924.chunk.css",
|
"static/css/main.376b8924.chunk.css",
|
||||||
"static/js/main.7c5c861a.chunk.js"
|
"static/js/main.4387b246.chunk.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
2
fittrackee/dist/index.html
vendored
2
fittrackee/dist/index.html
vendored
@ -1 +1 @@
|
|||||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000"><link rel="manifest" href="/manifest.json"><link rel="shortcut icon" href="/favicon.ico"><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fork-awesome@1.1.7/css/fork-awesome.min.css" integrity="sha256-gsmEoJAws/Kd3CjuOQzLie5Q3yshhvmo7YNtBG7aaEY=" crossorigin="anonymous"><link rel="stylesheet" href="https://cdn.jsdelivr.net/foundation-icons/3.0/foundation-icons.min.css"><link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin=""><title>FitTrackee</title><link href="/static/css/main.376b8924.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script><script type="text/javascript">$(document).ready((function(){$("li.nav-item").click((function(){$("button.navbar-toggler").toggleClass("collapsed"),$("#navbarSupportedContent").toggleClass("show")}))}))</script><script>!function(e){function t(t){for(var n,i,l=t[0],f=t[1],a=t[2],p=0,s=[];p<l.length;p++)i=l[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=0;for(n in f)Object.prototype.hasOwnProperty.call(f,n)&&(e[n]=f[n]);for(c&&c(t);s.length;)s.shift()();return u.push.apply(u,a||[]),r()}function r(){for(var e,t=0;t<u.length;t++){for(var r=u[t],n=!0,l=1;l<r.length;l++){var f=r[l];0!==o[f]&&(n=!1)}n&&(u.splice(t--,1),e=i(i.s=r[0]))}return e}var n={},o={1:0},u=[];function i(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,i),r.l=!0,r.exports}i.m=e,i.c=n,i.d=function(e,t,r){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(i.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)i.d(r,n,function(t){return e[t]}.bind(null,n));return r},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="/";var l=this.webpackJsonpfittrackee_client=this.webpackJsonpfittrackee_client||[],f=l.push.bind(l);l.push=t,l=l.slice();for(var a=0;a<l.length;a++)t(l[a]);var c=f;r()}([])</script><script src="/static/js/2.301144a0.chunk.js"></script><script src="/static/js/main.7c5c861a.chunk.js"></script></body></html>
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000"><link rel="manifest" href="/manifest.json"><link rel="shortcut icon" href="/favicon.ico"><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fork-awesome@1.1.7/css/fork-awesome.min.css" integrity="sha256-gsmEoJAws/Kd3CjuOQzLie5Q3yshhvmo7YNtBG7aaEY=" crossorigin="anonymous"><link rel="stylesheet" href="https://cdn.jsdelivr.net/foundation-icons/3.0/foundation-icons.min.css"><link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin=""><title>FitTrackee</title><link href="/static/css/main.376b8924.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script><script type="text/javascript">$(document).ready((function(){$("li.nav-item").click((function(){$("button.navbar-toggler").toggleClass("collapsed"),$("#navbarSupportedContent").toggleClass("show")}))}))</script><script>!function(e){function t(t){for(var n,i,l=t[0],f=t[1],a=t[2],p=0,s=[];p<l.length;p++)i=l[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=0;for(n in f)Object.prototype.hasOwnProperty.call(f,n)&&(e[n]=f[n]);for(c&&c(t);s.length;)s.shift()();return u.push.apply(u,a||[]),r()}function r(){for(var e,t=0;t<u.length;t++){for(var r=u[t],n=!0,l=1;l<r.length;l++){var f=r[l];0!==o[f]&&(n=!1)}n&&(u.splice(t--,1),e=i(i.s=r[0]))}return e}var n={},o={1:0},u=[];function i(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,i),r.l=!0,r.exports}i.m=e,i.c=n,i.d=function(e,t,r){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(i.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)i.d(r,n,function(t){return e[t]}.bind(null,n));return r},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="/";var l=this.webpackJsonpfittrackee_client=this.webpackJsonpfittrackee_client||[],f=l.push.bind(l);l.push=t,l=l.slice();for(var a=0;a<l.length;a++)t(l[a]);var c=f;r()}([])</script><script src="/static/js/2.301144a0.chunk.js"></script><script src="/static/js/main.4387b246.chunk.js"></script></body></html>
|
File diff suppressed because one or more lines are too long
1
fittrackee/dist/static/js/main.4387b246.chunk.js.map
vendored
Normal file
1
fittrackee/dist/static/js/main.4387b246.chunk.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -32,7 +32,10 @@ class HttpResponse(Response):
|
|||||||
|
|
||||||
class GenericErrorResponse(HttpResponse):
|
class GenericErrorResponse(HttpResponse):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, status_code: int, message: str, status: Optional[str] = None
|
self,
|
||||||
|
status_code: int,
|
||||||
|
message: Union[str, List],
|
||||||
|
status: Optional[str] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
response = {
|
response = {
|
||||||
'status': 'error' if status is None else status,
|
'status': 'error' if status is None else status,
|
||||||
@ -46,7 +49,9 @@ class GenericErrorResponse(HttpResponse):
|
|||||||
|
|
||||||
class InvalidPayloadErrorResponse(GenericErrorResponse):
|
class InvalidPayloadErrorResponse(GenericErrorResponse):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, message: Optional[str] = None, status: Optional[str] = None
|
self,
|
||||||
|
message: Optional[Union[str, List]] = None,
|
||||||
|
status: Optional[str] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
message = 'Invalid payload.' if message is None else message
|
message = 'Invalid payload.' if message is None else message
|
||||||
super().__init__(status_code=400, message=message, status=status)
|
super().__init__(status_code=400, message=message, status=status)
|
||||||
|
@ -276,3 +276,105 @@ class TestUpdateConfig:
|
|||||||
'Max. size of zip archive must be equal or greater than max. size '
|
'Max. size of zip archive must be equal or greater than max. size '
|
||||||
'of uploaded files'
|
'of uploaded files'
|
||||||
) in data['message']
|
) in data['message']
|
||||||
|
|
||||||
|
def test_it_raises_error_if_archive_max_size_equals_0(
|
||||||
|
self, app_with_max_single_file_size: Flask, user_1_admin: User
|
||||||
|
) -> None:
|
||||||
|
client = app_with_max_single_file_size.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(
|
||||||
|
max_zip_file_size=0,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
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 (
|
||||||
|
'Max. size of zip archive must be greater than 0'
|
||||||
|
in data['message']
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_it_raises_error_if_files_max_size_equals_0(
|
||||||
|
self, app: Flask, user_1_admin: User
|
||||||
|
) -> None:
|
||||||
|
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(
|
||||||
|
max_single_file_size=0,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
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 (
|
||||||
|
'Max. size of uploaded files must be greater than 0'
|
||||||
|
in data['message']
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_it_raises_error_if_gpx_limit_import_equals_0(
|
||||||
|
self, app: Flask, user_1_admin: User
|
||||||
|
) -> None:
|
||||||
|
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=0,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
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 (
|
||||||
|
'Max. files in a zip archive must be greater than 0'
|
||||||
|
in data['message']
|
||||||
|
)
|
||||||
|
16
fittrackee/tests/fixtures/fixtures_app.py
vendored
16
fittrackee/tests/fixtures/fixtures_app.py
vendored
@ -11,11 +11,16 @@ from fittrackee.application.utils import update_app_config_from_database
|
|||||||
def get_app_config(
|
def get_app_config(
|
||||||
with_config: Optional[bool] = False,
|
with_config: Optional[bool] = False,
|
||||||
max_workouts: Optional[int] = None,
|
max_workouts: Optional[int] = None,
|
||||||
|
max_single_file_size: Optional[int] = None,
|
||||||
) -> Optional[AppConfig]:
|
) -> Optional[AppConfig]:
|
||||||
if with_config:
|
if with_config:
|
||||||
config = AppConfig()
|
config = AppConfig()
|
||||||
config.gpx_limit_import = 10 if max_workouts is None else max_workouts
|
config.gpx_limit_import = 10 if max_workouts is None else max_workouts
|
||||||
config.max_single_file_size = 1 * 1024 * 1024
|
config.max_single_file_size = (
|
||||||
|
1 * 1024 * 1024
|
||||||
|
if max_single_file_size is None
|
||||||
|
else max_single_file_size
|
||||||
|
)
|
||||||
config.max_zip_file_size = 1 * 1024 * 1024 * 10
|
config.max_zip_file_size = 1 * 1024 * 1024 * 10
|
||||||
config.max_users = 100
|
config.max_users = 100
|
||||||
db.session.add(config)
|
db.session.add(config)
|
||||||
@ -27,6 +32,7 @@ def get_app_config(
|
|||||||
def get_app(
|
def get_app(
|
||||||
with_config: Optional[bool] = False,
|
with_config: Optional[bool] = False,
|
||||||
max_workouts: Optional[int] = None,
|
max_workouts: Optional[int] = None,
|
||||||
|
max_single_file_size: Optional[int] = None,
|
||||||
) -> Generator:
|
) -> Generator:
|
||||||
app = create_app()
|
app = create_app()
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
@ -64,6 +70,14 @@ def app_with_max_workouts(monkeypatch: pytest.MonkeyPatch) -> Generator:
|
|||||||
yield from get_app(with_config=True, max_workouts=2)
|
yield from get_app(with_config=True, max_workouts=2)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def app_with_max_single_file_size(
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> Generator:
|
||||||
|
monkeypatch.setenv('EMAIL_URL', 'smtp://none:none@0.0.0.0:1025')
|
||||||
|
yield from get_app(with_config=True, max_single_file_size=0)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def app_no_config() -> Generator:
|
def app_no_config() -> Generator:
|
||||||
yield from get_app(with_config=False)
|
yield from get_app(with_config=False)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import FitTrackeeGenericApi from '../fitTrackeeApi'
|
import FitTrackeeGenericApi from '../fitTrackeeApi'
|
||||||
import { history } from '../index'
|
import { history } from '../index'
|
||||||
import { setError } from './index'
|
import { generateIds } from '../utils'
|
||||||
|
import { emptyMessages, setError } from './index'
|
||||||
|
|
||||||
export const setAppConfig = data => ({
|
export const setAppConfig = data => ({
|
||||||
type: 'SET_APP_CONFIG',
|
type: 'SET_APP_CONFIG',
|
||||||
@ -12,6 +13,8 @@ export const setAppStats = data => ({
|
|||||||
data,
|
data,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const SetAppErrors = messages => ({ type: 'APP_ERRORS', messages })
|
||||||
|
|
||||||
export const getAppData = target => dispatch =>
|
export const getAppData = target => dispatch =>
|
||||||
FitTrackeeGenericApi.getData(target)
|
FitTrackeeGenericApi.getData(target)
|
||||||
.then(ret => {
|
.then(ret => {
|
||||||
@ -27,14 +30,18 @@ export const getAppData = target => dispatch =>
|
|||||||
})
|
})
|
||||||
.catch(error => dispatch(setError(`application|${error}`)))
|
.catch(error => dispatch(setError(`application|${error}`)))
|
||||||
|
|
||||||
export const updateAppConfig = formData => dispatch =>
|
export const updateAppConfig = formData => dispatch => {
|
||||||
|
dispatch(emptyMessages())
|
||||||
FitTrackeeGenericApi.updateData('config', formData)
|
FitTrackeeGenericApi.updateData('config', formData)
|
||||||
.then(ret => {
|
.then(ret => {
|
||||||
if (ret.status === 'success') {
|
if (ret.status === 'success') {
|
||||||
dispatch(setAppConfig(ret.data))
|
dispatch(setAppConfig(ret.data))
|
||||||
history.push('/admin/application')
|
history.push('/admin/application')
|
||||||
|
} else if (Array.isArray(ret.message)) {
|
||||||
|
dispatch(SetAppErrors(generateIds(ret.message)))
|
||||||
} else {
|
} else {
|
||||||
dispatch(setError(`application|${ret.message}`))
|
dispatch(setError(ret.message))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => dispatch(setError(`application|${error}`)))
|
.catch(error => dispatch(setError(`application|${error}`)))
|
||||||
|
}
|
||||||
|
@ -3,11 +3,16 @@ import i18next from 'i18next'
|
|||||||
import FitTrackeeApi from '../fitTrackeeApi/index'
|
import FitTrackeeApi from '../fitTrackeeApi/index'
|
||||||
import { history } from '../index'
|
import { history } from '../index'
|
||||||
|
|
||||||
|
export const emptyMessages = () => ({
|
||||||
|
type: 'CLEAN_ALL_MESSAGES',
|
||||||
|
})
|
||||||
|
|
||||||
export const setData = (target, data) => ({
|
export const setData = (target, data) => ({
|
||||||
type: 'SET_DATA',
|
type: 'SET_DATA',
|
||||||
data,
|
data,
|
||||||
target,
|
target,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const setPaginatedData = (target, data, pagination) => ({
|
export const setPaginatedData = (target, data, pagination) => ({
|
||||||
type: 'SET_PAGINATED_DATA',
|
type: 'SET_PAGINATED_DATA',
|
||||||
data,
|
data,
|
||||||
@ -51,7 +56,7 @@ export const getOrUpdateData = (
|
|||||||
dispatch(setLoading(false))
|
dispatch(setLoading(false))
|
||||||
return dispatch(setError(`${target}|Incorrect id`))
|
return dispatch(setError(`${target}|Incorrect id`))
|
||||||
}
|
}
|
||||||
dispatch(setError(''))
|
dispatch(emptyMessages())
|
||||||
return FitTrackeeApi[action](target, data)
|
return FitTrackeeApi[action](target, data)
|
||||||
.then(ret => {
|
.then(ret => {
|
||||||
if (ret.status === 'success') {
|
if (ret.status === 'success') {
|
||||||
|
@ -48,13 +48,16 @@ class AdminApplication extends React.Component {
|
|||||||
isInEdition,
|
isInEdition,
|
||||||
loadAppConfig,
|
loadAppConfig,
|
||||||
message,
|
message,
|
||||||
|
messages,
|
||||||
onHandleConfigFormSubmit,
|
onHandleConfigFormSubmit,
|
||||||
t,
|
t,
|
||||||
} = this.props
|
} = this.props
|
||||||
const { formData } = this.state
|
const { formData } = this.state
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{message && <Message message={message} t={t} />}
|
{(message || messages) && (
|
||||||
|
<Message message={message} messages={messages} t={t} />
|
||||||
|
)}
|
||||||
{Object.keys(formData).length > 0 && (
|
{Object.keys(formData).length > 0 && (
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-md-12">
|
<div className="col-md-12">
|
||||||
@ -209,6 +212,7 @@ class AdminApplication extends React.Component {
|
|||||||
export default connect(
|
export default connect(
|
||||||
state => ({
|
state => ({
|
||||||
message: state.message,
|
message: state.message,
|
||||||
|
messages: state.messages,
|
||||||
}),
|
}),
|
||||||
dispatch => ({
|
dispatch => ({
|
||||||
loadAppConfig: () => {
|
loadAppConfig: () => {
|
||||||
|
@ -14,7 +14,10 @@
|
|||||||
"Invalid credentials.": "Invalid credentials.",
|
"Invalid credentials.": "Invalid credentials.",
|
||||||
"Invalid payload.": "Invalid payload.",
|
"Invalid payload.": "Invalid payload.",
|
||||||
"Invalid token. Please log in again.": "Invalid token. Please log in again.",
|
"Invalid token. Please log in again.": "Invalid token. Please log in again.",
|
||||||
|
"Max. files in a zip archive must be greater than 0": "Max. files in a zip archive must be greater than 0",
|
||||||
|
"Max. size of uploaded files must be greater than 0": "Max. size of uploaded files must be greater than 0",
|
||||||
"Max. size of zip archive must be equal or greater than max. size of uploaded files": "Max. size of zip archive must be equal or greater than max. size of uploaded files",
|
"Max. size of zip archive must be equal or greater than max. size of uploaded files": "Max. size of zip archive must be equal or greater than max. size of uploaded files",
|
||||||
|
"Max. size of zip archive must be greater than 0": "Max. size of zip archive must be greater than 0",
|
||||||
"No file part.": "No file part.",
|
"No file part.": "No file part.",
|
||||||
"No picture.": "No picture.",
|
"No picture.": "No picture.",
|
||||||
"No selected file.": "No selected file.",
|
"No selected file.": "No selected file.",
|
||||||
|
@ -14,7 +14,10 @@
|
|||||||
"Invalid credentials.": "Identifiants invalides.",
|
"Invalid credentials.": "Identifiants invalides.",
|
||||||
"Invalid payload.": "Données incorrectes.",
|
"Invalid payload.": "Données incorrectes.",
|
||||||
"Invalid token. Please log in again.": "Jeton invalide. Merci de vous reconnecter.",
|
"Invalid token. Please log in again.": "Jeton invalide. Merci de vous reconnecter.",
|
||||||
|
"Max. files in a zip archive must be greater than 0": "Le nombre max. de fichiers dans une archive doit être supérieur à 0",
|
||||||
|
"Max. size of uploaded files must be greater than 0": "La taille max. des fichiers doit être supérieure à 0",
|
||||||
"Max. size of zip archive must be equal or greater than max. size of uploaded files": "La taille max. d'une archive doit être supérieure ou égale à la taille max. d'un fichier",
|
"Max. size of zip archive must be equal or greater than max. size of uploaded files": "La taille max. d'une archive doit être supérieure ou égale à la taille max. d'un fichier",
|
||||||
|
"Max. size of zip archive must be greater than 0": "La taille max. d'une archive doit être supérieure à 0",
|
||||||
"No file part.": "Pas de fichier fourni.",
|
"No file part.": "Pas de fichier fourni.",
|
||||||
"No picture.": "Pas d'image.",
|
"No picture.": "Pas d'image.",
|
||||||
"No selected file.": "Pas de fichier sélectionné.",
|
"No selected file.": "Pas de fichier sélectionné.",
|
||||||
|
@ -108,6 +108,7 @@ const message = (state = initial.message, action) => {
|
|||||||
case 'PICTURE_ERROR':
|
case 'PICTURE_ERROR':
|
||||||
case 'SET_ERROR':
|
case 'SET_ERROR':
|
||||||
return action.message
|
return action.message
|
||||||
|
case 'CLEAN_ALL_MESSAGES':
|
||||||
case 'LOGOUT':
|
case 'LOGOUT':
|
||||||
case 'PROFILE_SUCCESS':
|
case 'PROFILE_SUCCESS':
|
||||||
case 'SET_APP_CONFIG':
|
case 'SET_APP_CONFIG':
|
||||||
@ -122,7 +123,9 @@ const message = (state = initial.message, action) => {
|
|||||||
const messages = (state = initial.messages, action) => {
|
const messages = (state = initial.messages, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'AUTH_ERRORS':
|
case 'AUTH_ERRORS':
|
||||||
|
case 'APP_ERRORS':
|
||||||
return action.messages
|
return action.messages
|
||||||
|
case 'CLEAN_ALL_MESSAGES':
|
||||||
case 'LOGOUT':
|
case 'LOGOUT':
|
||||||
case 'PROFILE_SUCCESS':
|
case 'PROFILE_SUCCESS':
|
||||||
case '@@router/LOCATION_CHANGE':
|
case '@@router/LOCATION_CHANGE':
|
||||||
|
Loading…
Reference in New Issue
Block a user