From e423c355b5d9d06b056da26c62fa27073233fd75 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 30 Apr 2018 21:38:09 +0200 Subject: [PATCH] Admin: add and delete a sport (WIP) --- mpwo_api/mpwo_api/activities/activities.py | 77 +++++++++++++++++++ mpwo_api/mpwo_api/tests/test_activities.py | 58 ++++++++++++++ mpwo_client/src/actions/index.js | 37 +++++++++ .../Admin/{ => Sports}/AdminMenu.jsx | 24 +++--- .../Admin/{ => Sports}/AdminSport.jsx | 4 +- .../Admin/{ => Sports}/AdminSports.jsx | 4 +- .../components/Admin/generic/AdminDetail.jsx | 9 ++- .../components/Admin/generic/AdminPage.jsx | 57 +++++++------- mpwo_client/src/components/Admin/index.jsx | 6 +- mpwo_client/src/mwpoApi/index.js | 27 +++++++ 10 files changed, 256 insertions(+), 47 deletions(-) rename mpwo_client/src/components/Admin/{ => Sports}/AdminMenu.jsx (65%) rename mpwo_client/src/components/Admin/{ => Sports}/AdminSport.jsx (86%) rename mpwo_client/src/components/Admin/{ => Sports}/AdminSports.jsx (85%) diff --git a/mpwo_api/mpwo_api/activities/activities.py b/mpwo_api/mpwo_api/activities/activities.py index 23750a06..5281d6a7 100644 --- a/mpwo_api/mpwo_api/activities/activities.py +++ b/mpwo_api/mpwo_api/activities/activities.py @@ -58,6 +58,45 @@ def get_sport(auth_user_id, sport_id): return jsonify(response_object), code +@activities_blueprint.route('/sports', methods=['POST']) +@authenticate +def post_sport(auth_user_id): + """Post a sport""" + sport_data = request.get_json() + if not sport_data or sport_data.get('label') is None: + response_object = { + 'status': 'error', + 'message': 'Invalid payload.' + } + return jsonify(response_object), 400 + + sports_list = [] + try: + new_sport = Sport(label=sport_data.get('label')) + db.session.add(new_sport) + db.session.commit() + sports_list.append({ + 'id': new_sport.id, + 'label': new_sport.label + }) + response_object = { + 'status': 'created', + 'data': { + 'sports': sports_list + } + } + code = 201 + except (exc.IntegrityError, exc.OperationalError, ValueError) as e: + db.session.rollback() + appLog.error(e) + response_object = { + 'status': 'error', + 'message': 'Error. Please try again or contact the administrator.' + } + code = 500 + return jsonify(response_object), code + + @activities_blueprint.route('/sports/', methods=['PATCH']) @authenticate def update_sport(auth_user_id, sport_id): @@ -106,6 +145,44 @@ def update_sport(auth_user_id, sport_id): return jsonify(response_object), code +@activities_blueprint.route('/sports/', methods=['DELETE']) +@authenticate +def delete_sport(auth_user_id, sport_id): + """Delete a sport""" + sports_list = [] + try: + sport = Sport.query.filter_by(id=sport_id).first() + if sport: + db.session.query(Sport).filter_by(id=sport_id).delete() + db.session.commit() + response_object = { + 'status': 'no content', + 'data': { + 'sports': sports_list + } + } + code = 204 + print('OK') + print(response_object) + else: + response_object = { + 'status': 'not found', + 'data': { + 'sports': sports_list + } + } + code = 404 + except (exc.IntegrityError, exc.OperationalError, ValueError) as e: + db.session.rollback() + appLog.error(e) + response_object = { + 'status': 'error', + 'message': 'Error. Please try again or contact the administrator.' + } + code = 500 + return jsonify(response_object), code + + @activities_blueprint.route('/activities', methods=['GET']) @authenticate def get_activities(auth_user_id): diff --git a/mpwo_api/mpwo_api/tests/test_activities.py b/mpwo_api/mpwo_api/tests/test_activities.py index 8f2225cd..bc40bc09 100644 --- a/mpwo_api/mpwo_api/tests/test_activities.py +++ b/mpwo_api/mpwo_api/tests/test_activities.py @@ -117,6 +117,39 @@ def test_get_a_sport(app): assert 'cycling' in data['data']['sports'][0]['label'] +def test_add_a_sport(app): + add_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.post( + '/api/sports', + content_type='application/json', + data=json.dumps(dict( + label='surfing' + )), + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + + assert response.status_code == 201 + assert 'created' in data['status'] + + assert len(data['data']['sports']) == 1 + assert 'surfing' in data['data']['sports'][0]['label'] + + def test_update_a_sport(app): add_admin() add_sport('cycling') @@ -149,3 +182,28 @@ def test_update_a_sport(app): assert len(data['data']['sports']) == 1 assert 'cycling updated' in data['data']['sports'][0]['label'] + + +def test_delete_a_sport(app): + add_admin() + add_sport('cycling') + + 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.delete( + '/api/sports/1', + content_type='application/json', + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + assert response.status_code == 204 diff --git a/mpwo_client/src/actions/index.js b/mpwo_client/src/actions/index.js index 595fe968..7b6e1fc2 100644 --- a/mpwo_client/src/actions/index.js +++ b/mpwo_client/src/actions/index.js @@ -1,4 +1,5 @@ import mpwoApi from '../mwpoApi/index' +import { history } from '../index' export const setData = (target, data) => ({ @@ -30,6 +31,24 @@ export function getData(target, id = null) { } } +export function addData(target, data) { + return function(dispatch) { + if (isNaN(data.id)) { + return dispatch(setError(target, `${target}: Incorrect id`)) + } + return mpwoApi + .addData(target, data) + .then(ret => { + if (ret.status === 'created') { + dispatch(setData(target, ret.data)) + } else { + dispatch(setError(`${target}: ${ret.status}`)) + } + }) + .catch(error => dispatch(setError(`${target}: ${error}`))) + } +} + export function updateData(target, data) { return function(dispatch) { if (isNaN(data.id)) { @@ -47,3 +66,21 @@ export function updateData(target, data) { .catch(error => dispatch(setError(`${target}: ${error}`))) } } + +export function deleteData(target, id) { + return function(dispatch) { + if (isNaN(id)) { + return dispatch(setError(target, `${target}: Incorrect id`)) + } + return mpwoApi + .deleteData(target, id) + .then(ret => { + if (ret.status === 204) { + history.push(`/admin/${target}`) + } else { + dispatch(setError(`${target}: ${ret.status}`)) + } + }) + .catch(error => dispatch(setError(`${target}: ${error}`))) + } +} diff --git a/mpwo_client/src/components/Admin/AdminMenu.jsx b/mpwo_client/src/components/Admin/Sports/AdminMenu.jsx similarity index 65% rename from mpwo_client/src/components/Admin/AdminMenu.jsx rename to mpwo_client/src/components/Admin/Sports/AdminMenu.jsx index 03f4fc00..df7480a6 100644 --- a/mpwo_client/src/components/Admin/AdminMenu.jsx +++ b/mpwo_client/src/components/Admin/Sports/AdminMenu.jsx @@ -16,17 +16,19 @@ class AdminMenu extends React.Component {
-
    -
  • - - Sports - -
  • -
+
+
    +
  • + + Sports + +
  • +
+
diff --git a/mpwo_client/src/components/Admin/AdminSport.jsx b/mpwo_client/src/components/Admin/Sports/AdminSport.jsx similarity index 86% rename from mpwo_client/src/components/Admin/AdminSport.jsx rename to mpwo_client/src/components/Admin/Sports/AdminSport.jsx index c50b8b04..8ff1aae8 100644 --- a/mpwo_client/src/components/Admin/AdminSport.jsx +++ b/mpwo_client/src/components/Admin/Sports/AdminSport.jsx @@ -1,8 +1,8 @@ import React from 'react' import { connect } from 'react-redux' -import { getData } from '../../actions/index' -import AdminDetail from './generic/AdminDetail' +import { getData } from '../../../actions/index' +import AdminDetail from '../generic/AdminDetail' class AdminSports extends React.Component { componentDidMount() { diff --git a/mpwo_client/src/components/Admin/AdminSports.jsx b/mpwo_client/src/components/Admin/Sports/AdminSports.jsx similarity index 85% rename from mpwo_client/src/components/Admin/AdminSports.jsx rename to mpwo_client/src/components/Admin/Sports/AdminSports.jsx index b0ff6bf9..1f1bd410 100644 --- a/mpwo_client/src/components/Admin/AdminSports.jsx +++ b/mpwo_client/src/components/Admin/Sports/AdminSports.jsx @@ -1,8 +1,8 @@ import React from 'react' import { connect } from 'react-redux' -import { getData } from '../../actions/index' -import AdminPage from './generic/AdminPage' +import { getData } from '../../../actions/index' +import AdminPage from '../generic/AdminPage' class AdminSports extends React.Component { componentDidMount() { diff --git a/mpwo_client/src/components/Admin/generic/AdminDetail.jsx b/mpwo_client/src/components/Admin/generic/AdminDetail.jsx index 92e03353..068b2bfb 100644 --- a/mpwo_client/src/components/Admin/generic/AdminDetail.jsx +++ b/mpwo_client/src/components/Admin/generic/AdminDetail.jsx @@ -3,7 +3,7 @@ import { Helmet } from 'react-helmet' import { connect } from 'react-redux' import { Link } from 'react-router-dom' -import { updateData } from '../../../actions/index' +import { deleteData, updateData } from '../../../actions/index' class AdminDetail extends React.Component { @@ -18,6 +18,7 @@ class AdminDetail extends React.Component { const { message, onDataUpdate, + onDataDelete, results, target, } = this.props @@ -92,12 +93,12 @@ class AdminDetail extends React.Component { onDataDelete(event, target)} value="Delete" />
)} - Back to the list
@@ -115,6 +116,10 @@ export default connect( message: state.message, }), dispatch => ({ + onDataDelete: (e, target) => { + const id = e.target.form.id.value + dispatch(deleteData(target, id)) + }, onDataUpdate: (e, target) => { const data = [].slice .call(e.target.form.elements) diff --git a/mpwo_client/src/components/Admin/generic/AdminPage.jsx b/mpwo_client/src/components/Admin/generic/AdminPage.jsx index 7fa0c359..a0966b7d 100644 --- a/mpwo_client/src/components/Admin/generic/AdminPage.jsx +++ b/mpwo_client/src/components/Admin/generic/AdminPage.jsx @@ -28,34 +28,37 @@ export default function AdminPage(props) {
- - - - {tbKeys.map( - tbKey => - )} - - - - { results.map((result, idx) => ( - - { Object.keys(result).map(key => { - if (key === 'id') { - return ( - - ) - } - return - }) - } +
+
{tbKey}
- - {result[key]} - - {result[key]}
+ + + {tbKeys.map( + tbKey => + )} - ))} - -
{tbKey}
+ + + { results.map((result, idx) => ( + + { Object.keys(result).map(key => { + if (key === 'id') { + return ( + + + {result[key]} + + + ) + } + return {result[key]} + }) + } + + ))} + + + Add new item +
diff --git a/mpwo_client/src/components/Admin/index.jsx b/mpwo_client/src/components/Admin/index.jsx index 97316769..e25adb86 100644 --- a/mpwo_client/src/components/Admin/index.jsx +++ b/mpwo_client/src/components/Admin/index.jsx @@ -3,9 +3,9 @@ import { Helmet } from 'react-helmet' import { connect } from 'react-redux' import { Redirect, Route, Switch } from 'react-router-dom' -import AdminMenu from './AdminMenu' -import AdminSport from './AdminSport' -import AdminSports from './AdminSports' +import AdminMenu from './Sports/AdminMenu' +import AdminSport from './Sports/AdminSport' +import AdminSports from './Sports/AdminSports' import AccessDenied from './../Others/AccessDenied' import NotFound from './../Others/NotFound' import { isLoggedIn } from '../../utils' diff --git a/mpwo_client/src/mwpoApi/index.js b/mpwo_client/src/mwpoApi/index.js index 7c0748e4..ee69654a 100644 --- a/mpwo_client/src/mwpoApi/index.js +++ b/mpwo_client/src/mwpoApi/index.js @@ -15,6 +15,20 @@ export default class MpwoApi { .catch(error => error) } + static addData(target, data) { + const request = new Request(`${apiUrl}${target}`, { + method: 'POST', + headers: new Headers({ + 'Content-Type': 'application/json', + Authorization: `Bearer ${window.localStorage.getItem('authToken')}`, + }), + body: JSON.stringify(data) + }) + return fetch(request) + .then(response => response.json()) + .catch(error => error) + } + static updateData(target, data) { const request = new Request(`${apiUrl}${target}/${data.id}`, { method: 'PATCH', @@ -28,4 +42,17 @@ export default class MpwoApi { .then(response => response.json()) .catch(error => error) } + + static deleteData(target, id) { + const request = new Request(`${apiUrl}${target}/${id}`, { + method: 'DELETE', + headers: new Headers({ + 'Content-Type': 'application/json', + Authorization: `Bearer ${window.localStorage.getItem('authToken')}`, + }), + }) + return fetch(request) + .then(response => response) + .catch(error => error) + } }