/**
* Controller handles the logic for any citizen related routes.
* @module Controllers/CitizenController
*/
const CitizenModel = require('../models/citizenModel.js')
const StepModel = require('../models/stepModel.js')
// the N3 file service wrapper
const FileService = require('../services/n3FileService.js')
const SolidService = require('../services/solidService.js')
const CitizenService = require('../services/citizenService.js')
// error handler
const Boom = require('@hapi/boom')
/**
* GET
*
* placeholder route that would get all of the citizens and their info
*
* /citizens
*/
exports.index = (req, res, next) => {
const citizen = new CitizenModel(req, null)
citizen.getAll(req)
.then(result => {
res.send(result)
// todo add error handling
})
.catch(e => {
next(e)
})
}
/**
* placeholder route that would handle deletion of citizen
* currently this just removes all info on our 1 user
*
* DELETE
*
* /citizens
*/
exports.delete = (req, res, next) => {
try {
// create instance of citizen model
const citizen = new CitizenModel(req, null)
citizen.deleteAllInfo()
.then(() => res.send('Deleted all user info'))
} catch (e) {
next(e)
}
}
/**
* GET and POST, depends on which type of data user (solid or stored here)
*
* Show info for a specific step (based on route, eg /citizens/step/providePhoneNumberManually)
*
* /citizens/step/:step
*
* @param req.params.step {String} - Name of the step used
* @async
*/
exports.showStep = async (req, res, next) => {
try {
// create instance of model
const step = new StepModel(req)
// get our stepname from the parameters
const stepName = req.params.step
// get the input pattern for all steps
const inputPatterns = await step.getAllQueryPatternsAsString()
// check if an input pattern exists for the provided step, handle error if it doesn't
if (!inputPatterns.includes(stepName + ' ')) { return next(Boom.notFound('Route does not exist.')) }
// create models after validation to save resources
let solidService = null
if (req.method === 'POST') {
solidService = new SolidService()
}
const citizen = new CitizenModel(req, solidService)
const fileService = new FileService()
if (solidService !== null && solidService.validated !== null) {
return next(Boom.notFound('Incorrect data structure, problem occured on line ' + solidService.validated.context.line))
}
// add the info for the example user
const results = await citizen.getInfoByStep(stepName)
// convert turtle result to json-ld
if (Object.prototype.hasOwnProperty.call(req.headers, 'accept') && req.headers.accept === 'text/plain') {
res.send(results)
} else {
res.send(fileService.stringToJSONLD(results))
}
} catch (e) {
next(e)
}
}
/**
* DELETE
*
* delete info for a specific step (based on route, eg /citizens/step/providePhoneNumberManually)
*
* /citizens/step/:step
* @param req.params.step {String} - Name of the step used
* @async
*/
exports.undoStep = async (req, res, next) => {
try {
const citizen = new CitizenModel(req, null)
const step = new StepModel(req)
// get our stepname from the parameters
const stepName = req.params.step
// get the input pattern for all steps
const inputPatterns = await step.getAllInputPatternsAsString()
// check if an input pattern exists for the provided step, handle error if it doesn't
if (!inputPatterns.includes(stepName + ' ')) { return next(Boom.notFound('Route does not exist.')) }
// get the input pattern required to edit if it turns out the user already has data stored for this step
const inputPattern = await step.getInputPatternByStep(stepName)
// delete the info
res.send(await citizen.deleteQuadsByInputPatterns(inputPattern))
} catch (e) {
next(e)
}
}
/**
* DELETE
* Delete info for one or more steps based on JSON request.
* /citizens/deleteInfo
* @param req.body.input {...String} - Names of the steps to delete
* @async
*/
exports.deleteInfo = async (req, res, next) => {
try {
const citizen = new CitizenModel(req, null)
const step = new StepModel(req)
// get the input pattern for all steps
const inputPatterns = step.getAllInputPatternsAsString()
const inputPatternPromises = []
// loop the provided steps and check if they exist, delete the ones that exist
for (const stepName in req.body.input) {
// check if an input pattern exists for the provided step, handle error if it doesn't
if (!inputPatterns.includes(stepName)) { return next(Boom.badRequest('The provided step, ' + stepName + ', does not exist.')) }
// get the input pattern required to edit if it turns out the user already has data stored for this step
inputPatternPromises.push(step.getInputPatternByStep(stepName))
}
const patternsToDelete = await Promise.all(inputPatternPromises)
// delete the info
res.send(citizen.deleteQuadsByInputPatterns(patternsToDelete))
} catch (e) {
next(e)
}
}
/**
*
* Puts the provided step.
*
* Overwrites if existing step has multiple values,
* otherwise edits already presesent value with provided value.
*
* !! It's up to the sender of the request to verify that multiple values make sense for a specific step,
*
* Route: /citizens/step/:step
* @param req.params.step {String} - Name of the step used
* @param req.body.input.value {String|String[]} - Value(s) of the corresponding step
* @async
*/
exports.putStep = async (req, res, next) => {
try {
// handle error if data was not supplied in correct format
if (!Object.prototype.hasOwnProperty.call(req.body, 'input') || !Object.prototype.hasOwnProperty.call(req.body.input, 'value')) { return next(Boom.badRequest('Invalid format')) }
// Reformat input data so we can use the same function for both this route as well as putMultipleSteps
const stepName = req.params.step
const stepValue = req.body.input.value
const input = { [stepName]: stepValue }
const result = await CitizenService.addSteps(req, input, CitizenService.preparePut)
res.send(result)
} catch (error) {
next(error)
}
}
/**
* Puts the provided steps.
*
* Overwrites existing steps if a list of values is supplied or if multiple values are already present,
* otherwise edits steps that are already presesent with a new value
*
* !! It's up to the sender of the request to verify that multiple values make sense for a specific step,
*
* Route: /citizens/addInfo
* @param req.body.input {Object} - Key value pairs with stepnames as keys and their values
* @async
*/
exports.putMultipleSteps = async (req, res, next) => {
try {
// handle error if data was not supplied in correct format
if (!Object.prototype.hasOwnProperty.call(req.body, 'input')) { return next(Boom.badRequest('Invalid format')) }
const response = await CitizenService.addSteps(req, req.body.input, CitizenService.preparePut)
res.send(response)
} catch (e) {
next(e)
}
}
/**
* Posts the provided step.
*
* Appends new values if step already exists and if a list of values is supplied,
* otherwise edits the step with a new value
*
* !! It's up to the sender of the request to verify that multiple values make sense for a specific step
*
* Route: /citizens/step/:step
* @param req.params.step {String} - Name of the step used
* @param req.body.input.value {String|String[]} - Value(s) of the corresponding step
* @async
*/
exports.postStep = async (req, res, next) => {
try {
// handle error if data was not supplied in correct format
if (!Object.prototype.hasOwnProperty.call(req.body, 'input') || !Object.prototype.hasOwnProperty.call(req.body.input, 'value')) { return next(Boom.badRequest('Invalid format')) }
// Reformat input data so we can use the same function for both this route as well as postMultipleSteps
const stepName = req.params.step
const stepValue = req.body.input.value
const input = { [stepName]: stepValue }
const result = await CitizenService.addSteps(req, input, CitizenService.preparePost)
res.send(result)
} catch (error) {
next(error)
}
}
/**
* Posts the provided steps.
*
* Appends new values to existing steps if a list of values is supplied,
* otherwise edits steps that are already presesent with a new value
* !! It's up to the sender of the request to verify that multiple values make sense for a specific step
*
* Route: /citizens/addInfo
* @param req.body.input {Object} - Key value pairs with stepnames as keys and their values
* @async
*/
exports.postMultipleSteps = async (req, res, next) => {
try {
// handle error if data was not supplied in correct format
if (!Object.prototype.hasOwnProperty.call(req.body, 'input')) { return next(Boom.badRequest('Invalid format')) }
// return next(Boom.badRequest('Reworking implementation'))
// create instance of model
const response = await CitizenService.addSteps(req, req.body.input, CitizenService.preparePost)
res.send(response)
} catch (e) {
next(e)
}
}