Admin: add and delete a sport (WIP)
This commit is contained in:
parent
b69a55362d
commit
e423c355b5
@ -58,6 +58,45 @@ def get_sport(auth_user_id, sport_id):
|
|||||||
return jsonify(response_object), code
|
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/<int:sport_id>', methods=['PATCH'])
|
@activities_blueprint.route('/sports/<int:sport_id>', methods=['PATCH'])
|
||||||
@authenticate
|
@authenticate
|
||||||
def update_sport(auth_user_id, sport_id):
|
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
|
return jsonify(response_object), code
|
||||||
|
|
||||||
|
|
||||||
|
@activities_blueprint.route('/sports/<int:sport_id>', 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'])
|
@activities_blueprint.route('/activities', methods=['GET'])
|
||||||
@authenticate
|
@authenticate
|
||||||
def get_activities(auth_user_id):
|
def get_activities(auth_user_id):
|
||||||
|
@ -117,6 +117,39 @@ def test_get_a_sport(app):
|
|||||||
assert 'cycling' in data['data']['sports'][0]['label']
|
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):
|
def test_update_a_sport(app):
|
||||||
add_admin()
|
add_admin()
|
||||||
add_sport('cycling')
|
add_sport('cycling')
|
||||||
@ -149,3 +182,28 @@ def test_update_a_sport(app):
|
|||||||
|
|
||||||
assert len(data['data']['sports']) == 1
|
assert len(data['data']['sports']) == 1
|
||||||
assert 'cycling updated' in data['data']['sports'][0]['label']
|
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
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import mpwoApi from '../mwpoApi/index'
|
import mpwoApi from '../mwpoApi/index'
|
||||||
|
import { history } from '../index'
|
||||||
|
|
||||||
|
|
||||||
export const setData = (target, data) => ({
|
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) {
|
export function updateData(target, data) {
|
||||||
return function(dispatch) {
|
return function(dispatch) {
|
||||||
if (isNaN(data.id)) {
|
if (isNaN(data.id)) {
|
||||||
@ -47,3 +66,21 @@ export function updateData(target, data) {
|
|||||||
.catch(error => dispatch(setError(`${target}: ${error}`)))
|
.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}`)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -16,17 +16,19 @@ class AdminMenu extends React.Component {
|
|||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-md-2" />
|
<div className="col-md-2" />
|
||||||
<div className="col-md-8 card">
|
<div className="col-md-8 card">
|
||||||
<ul className="admin-items">
|
<div className="card-body">
|
||||||
<li>
|
<ul className="admin-items">
|
||||||
<Link
|
<li>
|
||||||
to={{
|
<Link
|
||||||
pathname: '/admin/sports',
|
to={{
|
||||||
}}
|
pathname: '/admin/sports',
|
||||||
>
|
}}
|
||||||
Sports
|
>
|
||||||
</Link>
|
Sports
|
||||||
</li>
|
</Link>
|
||||||
</ul>
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-2" />
|
<div className="col-md-2" />
|
||||||
</div>
|
</div>
|
@ -1,8 +1,8 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
|
|
||||||
import { getData } from '../../actions/index'
|
import { getData } from '../../../actions/index'
|
||||||
import AdminDetail from './generic/AdminDetail'
|
import AdminDetail from '../generic/AdminDetail'
|
||||||
|
|
||||||
class AdminSports extends React.Component {
|
class AdminSports extends React.Component {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
@ -1,8 +1,8 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
|
|
||||||
import { getData } from '../../actions/index'
|
import { getData } from '../../../actions/index'
|
||||||
import AdminPage from './generic/AdminPage'
|
import AdminPage from '../generic/AdminPage'
|
||||||
|
|
||||||
class AdminSports extends React.Component {
|
class AdminSports extends React.Component {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
@ -3,7 +3,7 @@ import { Helmet } from 'react-helmet'
|
|||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
import { updateData } from '../../../actions/index'
|
import { deleteData, updateData } from '../../../actions/index'
|
||||||
|
|
||||||
class AdminDetail extends React.Component {
|
class AdminDetail extends React.Component {
|
||||||
|
|
||||||
@ -18,6 +18,7 @@ class AdminDetail extends React.Component {
|
|||||||
const {
|
const {
|
||||||
message,
|
message,
|
||||||
onDataUpdate,
|
onDataUpdate,
|
||||||
|
onDataDelete,
|
||||||
results,
|
results,
|
||||||
target,
|
target,
|
||||||
} = this.props
|
} = this.props
|
||||||
@ -92,12 +93,12 @@ class AdminDetail extends React.Component {
|
|||||||
<input
|
<input
|
||||||
type="submit"
|
type="submit"
|
||||||
className="btn btn-danger btn-lg btn-block"
|
className="btn btn-danger btn-lg btn-block"
|
||||||
|
onClick={event => onDataDelete(event, target)}
|
||||||
value="Delete"
|
value="Delete"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</form>
|
</form>
|
||||||
<Link to={`/admin/${target}`}>Back to the list</Link>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-2" />
|
<div className="col-md-2" />
|
||||||
@ -115,6 +116,10 @@ export default connect(
|
|||||||
message: state.message,
|
message: state.message,
|
||||||
}),
|
}),
|
||||||
dispatch => ({
|
dispatch => ({
|
||||||
|
onDataDelete: (e, target) => {
|
||||||
|
const id = e.target.form.id.value
|
||||||
|
dispatch(deleteData(target, id))
|
||||||
|
},
|
||||||
onDataUpdate: (e, target) => {
|
onDataUpdate: (e, target) => {
|
||||||
const data = [].slice
|
const data = [].slice
|
||||||
.call(e.target.form.elements)
|
.call(e.target.form.elements)
|
||||||
|
@ -28,34 +28,37 @@ export default function AdminPage(props) {
|
|||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-md-2" />
|
<div className="col-md-2" />
|
||||||
<div className="col-md-8 card">
|
<div className="col-md-8 card">
|
||||||
<table className="table">
|
<div className="card-body">
|
||||||
<thead>
|
<table className="table">
|
||||||
<tr>
|
<thead>
|
||||||
{tbKeys.map(
|
<tr>
|
||||||
tbKey => <th key={tbKey} scope="col">{tbKey}</th>
|
{tbKeys.map(
|
||||||
)}
|
tbKey => <th key={tbKey} scope="col">{tbKey}</th>
|
||||||
</tr>
|
)}
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{ results.map((result, idx) => (
|
|
||||||
<tr key={idx}>
|
|
||||||
{ Object.keys(result).map(key => {
|
|
||||||
if (key === 'id') {
|
|
||||||
return (
|
|
||||||
<th key={key} scope="row">
|
|
||||||
<Link to={`/admin/${detailLink}/${result[key]}`}>
|
|
||||||
{result[key]}
|
|
||||||
</Link>
|
|
||||||
</th>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return <td key={key}>{result[key]}</td>
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
</thead>
|
||||||
</tbody>
|
<tbody>
|
||||||
</table>
|
{ results.map((result, idx) => (
|
||||||
|
<tr key={idx}>
|
||||||
|
{ Object.keys(result).map(key => {
|
||||||
|
if (key === 'id') {
|
||||||
|
return (
|
||||||
|
<th key={key} scope="row">
|
||||||
|
<Link to={`/admin/${detailLink}/${result[key]}`}>
|
||||||
|
{result[key]}
|
||||||
|
</Link>
|
||||||
|
</th>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return <td key={key}>{result[key]}</td>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<Link to={`/admin/${target}/add`}>Add new item</Link>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-2" />
|
<div className="col-md-2" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,9 +3,9 @@ import { Helmet } from 'react-helmet'
|
|||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { Redirect, Route, Switch } from 'react-router-dom'
|
import { Redirect, Route, Switch } from 'react-router-dom'
|
||||||
|
|
||||||
import AdminMenu from './AdminMenu'
|
import AdminMenu from './Sports/AdminMenu'
|
||||||
import AdminSport from './AdminSport'
|
import AdminSport from './Sports/AdminSport'
|
||||||
import AdminSports from './AdminSports'
|
import AdminSports from './Sports/AdminSports'
|
||||||
import AccessDenied from './../Others/AccessDenied'
|
import AccessDenied from './../Others/AccessDenied'
|
||||||
import NotFound from './../Others/NotFound'
|
import NotFound from './../Others/NotFound'
|
||||||
import { isLoggedIn } from '../../utils'
|
import { isLoggedIn } from '../../utils'
|
||||||
|
@ -15,6 +15,20 @@ export default class MpwoApi {
|
|||||||
.catch(error => error)
|
.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) {
|
static updateData(target, data) {
|
||||||
const request = new Request(`${apiUrl}${target}/${data.id}`, {
|
const request = new Request(`${apiUrl}${target}/${data.id}`, {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
@ -28,4 +42,17 @@ export default class MpwoApi {
|
|||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.catch(error => error)
|
.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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user