Modify routes, refactor API structure

This commit is contained in:
Maciej Ziarkowski 2019-08-14 14:05:49 +01:00
parent 021578a2d8
commit 2c9b5ea3d8
13 changed files with 211 additions and 171 deletions

View File

@ -1,161 +1,18 @@
import express from 'express'; import express from 'express';
import bodyParser from 'body-parser';
import { authUser, createUser, getUserById, getNewUserAPIKey } from './services/user';
import { authUser, createUser, getUserById, authAPIUser, getNewUserAPIKey } from './services/user';
import {
queryBuildingsAtPoint,
queryBuildingsByReference,
getBuildingById,
getBuildingLikeById,
getBuildingUPRNsById,
saveBuilding,
likeBuilding,
unlikeBuilding
} from './services/building';
import { queryLocation } from './services/search'; import { queryLocation } from './services/search';
const server = express.Router(); import buildingsRouter from './routes/buildingsRouter';
// GET buildings
// not implemented - may be useful to GET all buildings, paginated
// GET buildings at point const server = express();
server.get('/buildings/locate', function (req, res) {
const { lng, lat } = req.query;
queryBuildingsAtPoint(lng, lat).then(function (result) {
res.send(result);
}).catch(function (error) {
console.error(error);
res.send({ error: 'Database error' })
})
});
// GET buildings by reference (UPRN/TOID or other identifier) // parse POSTed json body
server.get('/buildings/reference', function (req, res) { server.use(bodyParser.json());
const { key, id } = req.query;
queryBuildingsByReference(key, id).then(function (result) {
res.send(result);
}).catch(function (error) {
console.error(error);
res.send({ error: 'Database error' })
})
});
// GET individual building, POST building updates server.use('/buildings', buildingsRouter);
server.route('/building/:building_id.json')
.get(function (req, res) {
const { building_id } = req.params;
getBuildingById(building_id).then(function (result) {
res.send(result);
}).catch(function (error) {
console.error(error);
res.send({ error: 'Database error' })
})
})
.post(function (req, res) {
if (req.session.user_id) {
updateBuilding(req, res, req.session.user_id);
} else if (req.query.api_key) {
authAPIUser(req.query.api_key)
.then(function (user) {
updateBuilding(req, res, user.user_id)
})
.catch(function (err) {
console.error(err);
res.send({ error: 'Must be logged in' });
});
} else {
res.send({ error: 'Must be logged in' });
}
})
function updateBuilding(req, res, userId) {
const { building_id } = req.params;
const building = req.body;
saveBuilding(building_id, building, userId).then(building => {
if (building.error) {
res.send(building)
return
}
if (typeof (building) === 'undefined') {
res.send({ error: 'Database error' })
return
}
res.send(building)
}).catch(
() => res.send({ error: 'Database error' })
)
}
// GET building UPRNs
server.get('/building/:building_id/uprns.json', function (req, res) {
const { building_id } = req.params;
getBuildingUPRNsById(building_id).then(function (result) {
if (typeof (result) === 'undefined') {
res.send({ error: 'Database error' })
return
}
res.send({
uprns: result
});
}).catch(function (error) {
console.error(error);
res.send({ error: 'Database error' })
})
})
// GET/POST like building
server.route('/building/:building_id/like.json')
.get(function (req, res) {
if (!req.session.user_id) {
res.send({ like: false }); // not logged in, so cannot have liked
return
}
const { building_id } = req.params;
getBuildingLikeById(building_id, req.session.user_id).then(like => {
// any value returned means like
res.send({ like: like })
}).catch(
() => res.send({ error: 'Database error' })
)
})
.post(function (req, res) {
if (!req.session.user_id) {
res.send({ error: 'Must be logged in' });
return
}
const { building_id } = req.params;
const { like } = req.body;
if (like) {
likeBuilding(building_id, req.session.user_id).then(building => {
if (building.error) {
res.send(building)
return
}
if (typeof (building) === 'undefined') {
res.send({ error: 'Database error' })
return
}
res.send(building)
}).catch(
() => res.send({ error: 'Database error' })
)
} else {
unlikeBuilding(building_id, req.session.user_id).then(building => {
if (building.error) {
res.send(building)
return
}
if (typeof (building) === 'undefined') {
res.send({ error: 'Database error' })
return
}
res.send(building)
}).catch(
() => res.send({ error: 'Database error' })
)
}
})
// POST new user // POST new user
server.post('/users', function (req, res) { server.post('/users', function (req, res) {

View File

@ -0,0 +1,154 @@
import * as buildingService from '../services/building';
import * as userService from '../services/user';
// GET buildings
// not implemented - may be useful to GET all buildings, paginated
// GET buildings at point
function getBuildingsByLocation(req, res) {
const { lng, lat } = req.query;
buildingService.queryBuildingsAtPoint(lng, lat).then(function (result) {
res.send(result);
}).catch(function (error) {
console.error(error);
res.send({ error: 'Database error' })
})
}
// GET buildings by reference (UPRN/TOID or other identifier)
function getBuildingsByReference(req, res) {
const { key, id } = req.query;
buildingService.queryBuildingsByReference(key, id).then(function (result) {
res.send(result);
}).catch(function (error) {
console.error(error);
res.send({ error: 'Database error' })
})
}
// GET individual building, POST building updates
function getBuildingById(req, res) {
const { building_id } = req.params;
buildingService.getBuildingById(building_id).then(function (result) {
res.send(result);
}).catch(function (error) {
console.error(error);
res.send({ error: 'Database error' })
})
}
function updateBuildingById(req, res) {
if (req.session.user_id) {
updateBuilding(req, res, req.session.user_id);
} else if (req.query.api_key) {
userService.authAPIUser(req.query.api_key)
.then(function (user) {
updateBuilding(req, res, user.user_id)
})
.catch(function (err) {
console.error(err);
res.send({ error: 'Must be logged in' });
});
} else {
res.send({ error: 'Must be logged in' });
}
}
function updateBuilding(req, res, userId) {
const { building_id } = req.params;
const building = req.body;
buildingService.saveBuilding(building_id, building, userId).then(building => {
if (building.error) {
res.send(building)
return
}
if (typeof (building) === 'undefined') {
res.send({ error: 'Database error' })
return
}
res.send(building)
}).catch(
() => res.send({ error: 'Database error' })
)
}
// GET building UPRNs
function getBuildingUPRNsById(req, res) {
const { building_id } = req.params;
buildingService.getBuildingUPRNsById(building_id).then(function (result) {
if (typeof (result) === 'undefined') {
res.send({ error: 'Database error' })
return
}
res.send({
uprns: result
});
}).catch(function (error) {
console.error(error);
res.send({ error: 'Database error' })
})
}
// GET/POST like building
function getBuildingLikeById(req, res) {
if (!req.session.user_id) {
res.send({ like: false }); // not logged in, so cannot have liked
return
}
const { building_id } = req.params;
buildingService.getBuildingLikeById(building_id, req.session.user_id).then(like => {
// any value returned means like
res.send({ like: like })
}).catch(
() => res.send({ error: 'Database error' })
)
}
function updateBuildingLikeById(req, res) {
if (!req.session.user_id) {
res.send({ error: 'Must be logged in' });
return
}
const { building_id } = req.params;
const { like } = req.body;
if (like) {
buildingService.likeBuilding(building_id, req.session.user_id).then(building => {
if (building.error) {
res.send(building)
return
}
if (typeof (building) === 'undefined') {
res.send({ error: 'Database error' })
return
}
res.send(building)
}).catch(
() => res.send({ error: 'Database error' })
)
} else {
buildingService.unlikeBuilding(building_id, req.session.user_id).then(building => {
if (building.error) {
res.send(building)
return
}
if (typeof (building) === 'undefined') {
res.send({ error: 'Database error' })
return
}
res.send(building)
}).catch(
() => res.send({ error: 'Database error' })
)
}
}
export default {
getBuildingsByLocation,
getBuildingsByReference,
getBuildingById,
updateBuildingById,
getBuildingUPRNsById,
getBuildingLikeById,
updateBuildingLikeById
};

View File

@ -0,0 +1,32 @@
import express from 'express';
import buildingController from '../controllers/buildingController';
const router = express.Router();
// GET buildings
// not implemented - may be useful to GET all buildings, paginated
// GET buildings at point
router.get('/locate', buildingController.getBuildingsByLocation);
// GET buildings by reference (UPRN/TOID or other identifier)
router.get('/reference', buildingController.getBuildingsByReference);
router.route('/:building_id.json')
// GET individual building
.get(buildingController.getBuildingById)
// POST building updates
.post(buildingController.updateBuildingById);
// GET building UPRNs
router.get('/:building_id/uprns.json', buildingController.getBuildingUPRNsById);
// GET/POST like building
router.route('/:building_id/like.json')
.get(buildingController.getBuildingLikeById)
.post(buildingController.updateBuildingLikeById);
export default router;

View File

@ -85,7 +85,7 @@ class App extends React.Component<any, any> { // TODO: add proper types
selectBuilding(building) { selectBuilding(building) {
this.increaseRevision(building.revision_id); this.increaseRevision(building.revision_id);
// get UPRNs and update // get UPRNs and update
fetch(`/building/${building.building_id}/uprns.json`, { fetch(`/api/buildings/${building.building_id}/uprns.json`, {
method: 'GET', method: 'GET',
headers:{ headers:{
'Content-Type': 'application/json' 'Content-Type': 'application/json'
@ -106,7 +106,7 @@ class App extends React.Component<any, any> { // TODO: add proper types
}); });
// get if liked and update // get if liked and update
fetch(`/building/${building.building_id}/like.json`, { fetch(`/api/buildings/${building.building_id}/like.json`, {
method: 'GET', method: 'GET',
headers:{ headers:{
'Content-Type': 'application/json' 'Content-Type': 'application/json'
@ -147,7 +147,7 @@ class App extends React.Component<any, any> { // TODO: add proper types
} }
likeBuilding(buildingId) { likeBuilding(buildingId) {
fetch(`/building/${buildingId}/like.json`, { fetch(`/api/buildings/${buildingId}/like.json`, {
method: 'POST', method: 'POST',
headers:{ headers:{
'Content-Type': 'application/json' 'Content-Type': 'application/json'
@ -168,7 +168,7 @@ class App extends React.Component<any, any> { // TODO: add proper types
} }
updateBuilding(buildingId, data){ updateBuilding(buildingId, data){
fetch(`/building/${buildingId}.json`, { fetch(`/api/buildings/${buildingId}.json`, {
method: 'POST', method: 'POST',
body: JSON.stringify(data), body: JSON.stringify(data),
headers:{ headers:{

View File

@ -176,7 +176,7 @@ class EditForm extends Component<any, any> { // TODO: add proper types
event.preventDefault(); event.preventDefault();
const like = event.target.checked; const like = event.target.checked;
fetch(`/building/${this.props.building_id}/like.json`, { fetch(`/api/buildings/${this.props.building_id}/like.json`, {
method: 'POST', method: 'POST',
headers:{ headers:{
'Content-Type': 'application/json' 'Content-Type': 'application/json'
@ -203,7 +203,7 @@ class EditForm extends Component<any, any> { // TODO: add proper types
event.preventDefault(); event.preventDefault();
this.setState({error: undefined}) this.setState({error: undefined})
fetch(`/building/${this.props.building_id}.json`, { fetch(`/api/buildings/${this.props.building_id}.json`, {
method: 'POST', method: 'POST',
body: JSON.stringify(this.state), body: JSON.stringify(this.state),
headers:{ headers:{

View File

@ -39,7 +39,7 @@ class Login extends Component<any, any> { // TODO: add proper types
event.preventDefault(); event.preventDefault();
this.setState({error: undefined}) this.setState({error: undefined})
fetch('/login', { fetch('/api/login', {
method: 'POST', method: 'POST',
body: JSON.stringify(this.state), body: JSON.stringify(this.state),
headers:{ headers:{
@ -52,7 +52,7 @@ class Login extends Component<any, any> { // TODO: add proper types
if (res.error) { if (res.error) {
this.setState({error: res.error}) this.setState({error: res.error})
} else { } else {
fetch('/users/me', { fetch('/api/users/me', {
credentials: 'same-origin' credentials: 'same-origin'
}).then( }).then(
(res) => res.json() (res) => res.json()

View File

@ -61,7 +61,7 @@ class ColouringMap extends Component<any, any> { // TODO: add proper types
const newCat = parseCategoryURL(this.props.match.url); const newCat = parseCategoryURL(this.props.match.url);
const mapCat = newCat || 'age'; const mapCat = newCat || 'age';
fetch( fetch(
'/buildings/locate?lat='+lat+'&lng='+lng '/api/buildings/locate?lat='+lat+'&lng='+lng
).then( ).then(
(res) => res.json() (res) => res.json()
).then(function(data){ ).then(function(data){

View File

@ -30,7 +30,7 @@ class MyAccountPage extends Component<any, any> { // TODO: add proper types
event.preventDefault(); event.preventDefault();
this.setState({error: undefined}); this.setState({error: undefined});
fetch('/logout', { fetch('/api/logout', {
method: 'POST', method: 'POST',
credentials: 'same-origin' credentials: 'same-origin'
}).then( }).then(
@ -50,7 +50,7 @@ class MyAccountPage extends Component<any, any> { // TODO: add proper types
event.preventDefault(); event.preventDefault();
this.setState({error: undefined}); this.setState({error: undefined});
fetch('/api/key', { fetch('/api/api/key', {
method: 'POST', method: 'POST',
credentials: 'same-origin' credentials: 'same-origin'
}).then( }).then(

View File

@ -78,7 +78,7 @@ class SearchBox extends Component<any, any> { // TODO: add proper types
}) })
fetch( fetch(
'/search?q='+this.state.q '/api/search?q='+this.state.q
).then( ).then(
(res) => res.json() (res) => res.json()
).then((data) => { ).then((data) => {

View File

@ -42,7 +42,7 @@ class SignUp extends Component<any, any> { // TODO: add proper types
event.preventDefault(); event.preventDefault();
this.setState({error: undefined}) this.setState({error: undefined})
fetch('/users', { fetch('/api/users', {
method: 'POST', method: 'POST',
body: JSON.stringify(this.state), body: JSON.stringify(this.state),
headers:{ headers:{
@ -55,7 +55,7 @@ class SignUp extends Component<any, any> { // TODO: add proper types
if (res.error) { if (res.error) {
this.setState({error: res.error}) this.setState({error: res.error})
} else { } else {
fetch('/users/me', { fetch('/api/users/me', {
credentials: 'same-origin' credentials: 'same-origin'
}).then( }).then(
(res) => res.json() (res) => res.json()

View File

@ -10,7 +10,6 @@ import express from 'express';
import { renderToString } from 'react-dom/server'; import { renderToString } from 'react-dom/server';
import serialize from 'serialize-javascript'; import serialize from 'serialize-javascript';
import bodyParser from 'body-parser';
import session from 'express-session'; import session from 'express-session';
import pgConnect from 'connect-pg-simple'; import pgConnect from 'connect-pg-simple';
@ -23,7 +22,7 @@ import {
getBuildingUPRNsById getBuildingUPRNsById
} from './api/services/building'; } from './api/services/building';
import tileserver from './tiles/tileserver'; import tileserver from './tiles/tileserver';
import apiRouter from './api/api'; import apiServer from './api/api';
import { parseBuildingURL } from './parse'; import { parseBuildingURL } from './parse';
// create server // create server
@ -38,8 +37,6 @@ server.disable('x-powered-by');
// serve static files // serve static files
server.use(express.static(process.env.RAZZLE_PUBLIC_DIR)); server.use(express.static(process.env.RAZZLE_PUBLIC_DIR));
// parse POSTed json body
server.use(bodyParser.json());
// handle user sessions // handle user sessions
const pgSession = pgConnect(session); const pgSession = pgConnect(session);
@ -159,6 +156,6 @@ function renderHTML(context, data, req, res) {
server.use('/tiles', tileserver); server.use('/tiles', tileserver);
server.use('/api', apiRouter); server.use('/api', apiServer);
export default server; export default server;

View File

@ -59,7 +59,7 @@ def save_data(building_id, data, api_key, base_url):
"""Save data to a building """Save data to a building
""" """
r = requests.post( r = requests.post(
"{}/building/{}.json?api_key={}".format(base_url, building_id, api_key), "{}/buildings/{}.json?api_key={}".format(base_url, building_id, api_key),
json=data json=data
) )

View File

@ -89,7 +89,7 @@ def save_data(building_id, data, api_key, base_url):
"""Save data to a building """Save data to a building
""" """
r = requests.post( r = requests.post(
"{}/building/{}.json?api_key={}".format(base_url, building_id, api_key), "{}/buildings/{}.json?api_key={}".format(base_url, building_id, api_key),
json=data json=data
) )