Changeset 487 for Dev/trunk/src/server/app.js
- Timestamp:
- 03/05/14 22:44:48 (11 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
Dev/trunk/src/server/app.js
r481 r487 9 9 , _ = require("underscore") 10 10 , tv4 = require("tv4") 11 , HTTPResult = require("./util/http-result") 12 , etags = require("./util/etags") 13 , cryptoken = require('./util/crypto-token') 11 14 ; 12 15 … … 22 25 exports.App = function(settings) { 23 26 24 assertSetting("couchDbURL", settings, _.isString); 25 var couch = new CouchDB(settings.couchDbURL); 27 assertSetting("couchServerURL", settings, _.isString); 28 assertSetting("dbName", settings, _.isString); 29 var couch = new CouchDB(settings.couchServerURL,settings.dbName); 26 30 27 31 var schema = require("./config/couchdb-schema.json"); … … 69 73 app.use(passport.initialize()); 70 74 app.use(passport.session()); 75 76 // various middlewares 71 77 function ensureAuthenticated(req,res,next){ 72 78 if (!req.user) { … … 78 84 function returnUser(req,res) { 79 85 res.send(200, req.user); 86 } 87 function notImplemented(req,res) { 88 res.send(501,{error:"API not implemented yet."}); 80 89 } 81 90 … … 112 121 }); 113 122 123 var JSON_MIME = 'application/json'; 124 var CSV_MIME = 'application/json'; 125 function ensureMIME(mimeType) { 126 return function(req,res,next) { 127 if (!req.accepts(mimeType)) { 128 res.send(406); 129 } else { 130 res.set({ 131 'Content-Type': mimeType 132 }); 133 next(); 134 } 135 }; 136 } 137 138 function stripAndReturnPrivates(obj) { 139 var priv = {}; 140 _.each(obj||{},function(val,key){ 141 if (key.substring(0,1) === '_') { 142 priv[key] = val; 143 delete obj[key]; 144 } 145 }); 146 return priv; 147 } 148 149 function identity(obj) { return obj; } 150 function handleUnknownResponse(status,error){ 151 return new HTTPResult(500,{error: "Unexpected database response", 152 innerStatus: status, innerResponse: error}); 153 } 154 function handleUnknownError(error){ 155 return new HTTPResult(500, {error: "Unknown error", innerError: error}); 156 } 157 function handleRowValues(result) { 158 return _.map(result.rows, function(item) { return item.value; }); 159 } 160 function handleRowDocs(result) { 161 return _.map(result.rows, function(item) { return item.doc; }); 162 } 163 164 function getDocumentsOfType (type) { 165 var url = '_design/default/_view/by_type?key='+JSON.stringify(type); 166 return HTTPResult.fromResponsePromise(couch.get(url).response, 167 handleUnknownError) 168 .handle({ 169 200: handleRowValues, 170 404: function() { return {error: "Cannot find collection of type "+type}; }, 171 default: handleUnknownResponse 172 }); 173 } 174 function getDocument(id,rev,type) { 175 var opts = {headers:{}}; 176 if (rev) { 177 opts.headers['If-Non-Match'] = '"'+rev+'"'; 178 } 179 return HTTPResult.fromResponsePromise(couch.get(id,opts).response, 180 handleUnknownError) 181 .handle({ 182 200: function(doc){ 183 if ( doc.type !== type ) { 184 return new HTTPResult(404,{error:"Document not found."}); 185 } else { 186 var priv = stripAndReturnPrivates(doc); 187 if ( priv._rev !== rev ) { 188 doc._id = priv._id; 189 doc._rev = priv._rev; 190 return doc; 191 } else { 192 return new HTTPResult(304); 193 } 194 } 195 }, 196 304: identity, 197 default: handleUnknownResponse 198 }); 199 } 200 function putDocument(id,rev,type,doc) { 201 var priv = stripAndReturnPrivates(doc); 202 if ( doc.type === type && tv4.validateResult(doc, schema) ) { 203 var opts = rev ? {headers:{'If-Match':'"'+rev+'"'}} : {}; 204 return HTTPResult.fromResponsePromise(couch.put(id,doc,opts).response, 205 handleUnknownError) 206 .handle({ 207 201: function(res){ 208 doc._id = res.id; 209 doc._rev = res.rev; 210 return doc; 211 }, 212 409: function(error) { 213 return {error: error.reason}; 214 }, 215 default: handleUnknownResponse 216 }); 217 } else { 218 return new HTTPResult(400,{error: "Document failed schema verification."}); 219 } 220 } 221 function deleteDocument(id,rev) { 222 if ( rev ) { 223 var opts = {headers:{'If-Match':'"'+rev+'"'}}; 224 return HTTPResult.fromResponsePromise(couch.delete(id,opts).response, 225 handleUnknownError) 226 .handle({ 227 200: identity, 228 409: function(error) { 229 return {error: error.reason}; 230 }, 231 default: handleUnknownResponse 232 }); 233 } else { 234 return new HTTPResult(409, {error: "Cannot identify document revision to delete."}); 235 } 236 } 237 function postDocument(type,doc) { 238 var priv = stripAndReturnPrivates(doc); 239 if ( doc.type === type && tv4.validateResult(doc, schema) ) { 240 return HTTPResult.fromResponsePromise(couch.post(doc).response, 241 handleUnknownError) 242 .handle({ 243 201: function(response) { 244 doc._id = response.id; 245 doc._rev = response.rev; 246 return doc; 247 }, 248 default: handleUnknownResponse 249 }); 250 } else { 251 return new HTTPResult(400,{error: "Document failed schema verification."}); 252 } 253 } 254 114 255 function makeDocsGet(type) { 115 256 return function(req,res) { 116 var url = '_design/default/_view/by_type?key='+JSON.stringify(type); 117 couch.get(url).then(function(result){ 118 var items = _.map(result.rows, function(item) { return item.value; }); 119 res.send(200, items); 120 }, function(error){ 121 res.send(404, {error: "Cannot find collection"}); 257 getDocumentsOfType(type) 258 .handle(res.send.bind(res)); 259 }; 260 } 261 function makeDocGet_id(type) { 262 return function(req,res) { 263 var id = req.params.id; 264 var rev = etags.parse(req.header('If-Non-Match'))[0]; 265 getDocument(id,rev,type) 266 .handle({ 267 200: function(doc){ 268 res.set({ 269 'ETag': etags.format([doc._rev]) 270 }).send(200, doc); 271 }, 272 default: res.send.bind(res) 122 273 }); 123 274 }; 124 275 } 125 function makeDoc Get_id(type) {276 function makeDocPut_id(type) { 126 277 return function(req,res) { 127 278 var id = req.params.id; 128 couch.get(id).then(function(result){ 129 if ( result.type === type ) { 130 res.send(200, result); 131 } else { 132 res.send(404, {error: "Document "+id+" is not a "+type}); 133 } 134 }, function(error){ 135 res.send(404, {error: "Cannot find survey run "+id}); 279 var doc = req.body; 280 var rev = etags.parse(req.header('If-Match'))[0] || (doc && doc._rev); 281 putDocument(id,rev,type,doc) 282 .handle({ 283 201: function(doc) { 284 res.set({ 285 'ETag': etags.format([doc._rev]) 286 }).send(201, doc); 287 }, 288 default: res.send.bind(res) 136 289 }); 137 290 }; 138 291 } 139 function makeDoc Put_id(type) {292 function makeDocDel_id(type) { 140 293 return function(req,res) { 141 294 var id = req.params.id; 142 295 var doc = req.body; 143 if ( doc.type === type && tv4.validateResult(doc, schema) ) { 144 couch.put(id,doc).then(function(result){ 145 res.send(200, result); 146 }, function(error){ 147 res.send(500, error); 296 var rev = etags.parse(req.header('If-Match'))[0] || (doc && doc._rev); 297 deleteDocument(id,rev) 298 .handle(res.send.bind(res)); 299 }; 300 } 301 function makeDocPost(type) { 302 return function(req,res) { 303 var doc = req.body; 304 postDocument(type,doc) 305 .handle({ 306 201: function(doc) { 307 res.set({ 308 'ETag': etags.format([doc._rev]) 309 }).send(201, doc); 310 }, 311 default: res.send.bind(res) 312 }); 313 }; 314 } 315 316 // Questions 317 function getQuestionsWithCode(code) { 318 var url = '_design/questions/_view/by_code'; 319 var query = {include_docs:true,key:code}; 320 return HTTPResult.fromResponsePromise(couch.get(url,{query:query}).response, 321 handleUnknownError) 322 .handle({ 323 200: handleRowDocs, 324 default: handleUnknownResponse 325 }); 326 } 327 function getQuestionsWithTopic(topic) { 328 var url = '_design/questions/_view/all_topics'; 329 var query = {reduce:false,include_docs:true,key:topic}; 330 return HTTPResult.fromResponsePromise(couch.get(url,{query:query}).response, 331 handleUnknownError) 332 .handle({ 333 200: handleRowDocs, 334 default: handleUnknownResponse 335 }); 336 } 337 function getQuestionsWithCategoryAndTopic(category,topic) { 338 var hasTopic = typeof topic !== 'undefined'; 339 var url = '_design/questions/_view/all'; 340 var query = {reduce:false,include_docs:true, 341 startkey:hasTopic?[category,topic]:[category], 342 endkey:hasTopic?[category,topic]:[category,{}]}; 343 return HTTPResult.fromResponsePromise(couch.get(url,{query:query}).response, 344 handleUnknownError) 345 .handle({ 346 200: handleRowDocs, 347 default: handleUnknownResponse 348 }); 349 } 350 app.get('/api/questions', 351 ensureAuthenticated, 352 ensureMIME(JSON_MIME), 353 function(req,res) { 354 var hr; 355 if ( 'category' in req.query ) { 356 hr = getQuestionsWithCategoryAndTopic(req.query.category,req.query.topic); 357 } else if ( 'topic' in req.query ) { 358 hr = getQuestionsWithTopic(req.query.topic); 359 } else if ( 'code' in req.query ) { 360 hr = getQuestionsWithCode(req.query.code); 361 } 362 hr.handle(res.send.bind(res)); 363 }); 364 app.post('/api/questions', 365 ensureAuthenticated, 366 ensureMIME(JSON_MIME), 367 function (req,res) { 368 var doc = req.body; 369 getQuestionsWithCode(doc.code) 370 .handle({ 371 200: function(others) { 372 if ( others.length > 0 ) { 373 return new HTTPResult(403,{error:"Other question with this code already exists."}); 374 } else { 375 return postDocument('Question',doc); 376 } 377 } 378 }) 379 .handle(res.send.bind(res)); 380 }); 381 app.get('/api/questions/:id', 382 ensureAuthenticated, 383 ensureMIME(JSON_MIME), 384 makeDocGet_id('Question')); 385 app.put('/api/questions/:id', 386 ensureAuthenticated, 387 ensureMIME(JSON_MIME), 388 function (req,res) { 389 var id = req.params.id; 390 var doc = req.body; 391 var rev = etags.parse(req.header('If-Match'))[0] || (doc && doc._rev); 392 getQuestionsWithCode(doc.code) 393 .handle({ 394 200: function(others) { 395 if ( others.length > 0 ) { 396 return new HTTPResult(403,{error:"Other question with this code already exists."}); 397 } else { 398 return putDocument(id,rev,'Question',doc); 399 } 400 } 401 }) 402 .handle(res.send.bind(res)); 403 }); 404 app.delete('/api/questions/:id', 405 ensureAuthenticated, 406 ensureMIME(JSON_MIME), 407 makeDocDel_id('Question')); 408 409 410 // Categories and topics 411 app.get('/api/categories', 412 ensureAuthenticated, 413 ensureMIME(JSON_MIME), 414 function(req,res) { 415 var url = '_design/questions/_view/all'; 416 HTTPResult.fromResponsePromise(couch.get(url,{query:{reduce:true,group:true,group_level:1}}).response, 417 handleUnknownError) 418 .handle({ 419 200: function(result) { 420 return _.map(result.rows, function(item) { 421 return { name:item.key[0], count:item.value }; 422 }); 423 }, 424 default: handleUnknownResponse 425 }) 426 .handle(res.send.bind(res)); 427 }); 428 app.get('/api/categories/:category/topics', 429 ensureAuthenticated, 430 ensureMIME(JSON_MIME), 431 function(req,res) { 432 var category = req.params.category; 433 var url = '_design/questions/_view/all'; 434 HTTPResult.fromResponsePromise(couch.get(url,{query:{reduce:true,group:true,group_level:2,startkey:[category],endkey:[category,{}]}}).response, 435 handleUnknownError) 436 .handle({ 437 200: function(result) { 438 return _.map(result.rows, function(item) { return { name:item.key[1], count:item.value }; }); 439 }, 440 default: handleUnknownResponse 441 }) 442 .handle(res.send.bind(res)); 443 }); 444 app.get('/api/topics', 445 ensureAuthenticated, 446 ensureMIME(JSON_MIME), 447 function(req,res) { 448 var url = '_design/questions/_view/all_topics'; 449 HTTPResult.fromResponsePromise(couch.get(url,{query:{reduce:true,group:true}}).response, 450 handleUnknownError) 451 .handle({ 452 200: function(result) { 453 return _.map(result.rows, function(item) { return {name:item.key, count:item.value}; }); 454 }, 455 default: handleUnknownResponse 456 }) 457 .handle(res.send.bind(res)); 458 }); 459 460 // Surveys 461 app.get('/api/surveys', 462 ensureAuthenticated, 463 ensureMIME(JSON_MIME), 464 function(req,res) { 465 var url; 466 if ( 'drafts' in req.query ) { 467 url = '_design/surveys/_view/drafts'; 468 } else if ( 'published' in req.query ) { 469 url = '_design/surveys/_view/published'; 470 } else { 471 url = '_design/default/_view/by_type?key='+JSON.stringify('Survey'); 472 } 473 HTTPResult.fromResponsePromise(couch.get(url).response, 474 handleUnknownError) 475 .handle({ 476 200: function(result) { 477 return _.map(result.rows, function(item) { return item.value; }); 478 }, 479 default: handleUnknownResponse 480 }) 481 .handle(res.send.bind(res)); 482 }); 483 app.post('/api/surveys', 484 ensureAuthenticated, 485 ensureMIME(JSON_MIME), 486 makeDocPost('Survey')); 487 app.get('/api/surveys/:id', 488 ensureAuthenticated, 489 ensureMIME(JSON_MIME), 490 makeDocGet_id('Survey')); 491 app.put('/api/surveys/:id', 492 ensureAuthenticated, 493 ensureMIME(JSON_MIME), 494 makeDocPut_id('Survey')); 495 app.delete('/api/surveys/:id', 496 ensureAuthenticated, 497 ensureMIME(JSON_MIME), 498 makeDocDel_id('Survey')); 499 500 // SurveyRuns 501 app.get('/api/surveyRuns', 502 ensureAuthenticated, 503 ensureMIME(JSON_MIME), 504 makeDocsGet('SurveyRun')); 505 app.post('/api/surveyRuns', 506 ensureAuthenticated, 507 ensureMIME(JSON_MIME), 508 function(req,res) { 509 var doc = req.body; 510 randomToken() 511 .handle({ 512 201: function(token) { 513 doc.secret = token; 514 return postDocument('SurveyRun',doc); 515 } 516 }) 517 .handle(res.send.bind(res)); 518 }); 519 app.get('/api/surveyRuns/:id', 520 ensureAuthenticated, 521 ensureMIME(JSON_MIME), 522 makeDocGet_id('SurveyRun')); 523 app.put('/api/surveyRuns/:id', 524 ensureAuthenticated, 525 ensureMIME(JSON_MIME), 526 function (req,res) { 527 var id = req.params.id; 528 var doc = req.body; 529 var rev = etags.parse(req.header('If-Match'))[0] || (doc && doc._rev); 530 var hr; 531 if ( typeof doc.secret === 'undefined' ) { 532 hr = randomToken() 533 .handle({ 534 201: function(token) { 535 doc.secret = token; 536 return putDocument(id,rev,'SurveyRun',doc); 537 } 148 538 }); 149 539 } else { 150 res.send(400,{error: "Document failed schema verification."});540 hr = putDocument(id,rev,'SurveyRun',doc); 151 541 } 152 }; 153 } 154 function makeDocDel_id(type) { 155 return function(req,res) { 156 var id = req.params.id; 157 var rev = req.query.rev; 158 couch.delete(id,rev).then(function(result){ 159 res.send(200, result); 160 }, function(error){ 161 res.send(500, error); 542 hr.handle(res.send.bind(res)); 543 }); 544 app.delete('/api/surveyRuns/:id', 545 ensureAuthenticated, 546 ensureMIME(JSON_MIME), 547 makeDocDel_id('SurveyRun')); 548 app.get('/api/surveyRuns/:id/responses', 549 ensureAuthenticated, 550 ensureMIME(JSON_MIME), 551 function(req,res) { 552 var id = req.params.id; 553 getResponsesBySurveyRunId(id) 554 .handle(res.send.bind(res)); 555 }); 556 app.get('/api/surveyRuns/:id/responses.csv', 557 ensureAuthenticated, 558 ensureMIME(CSV_MIME), 559 function(req, res) { 560 var id = req.params.id; 561 getResponsesBySurveyRunId(id) 562 .handle({ 563 200: function(responses) { 564 var flatResponses = responsesToVariables(responses); 565 res.set({ 566 'Content-Disposition': 'attachment; filename=surveyRun-'+id+'-responses.csv' 567 }); 568 res.status(200); 569 writeObjectsToCSVStream(flatResponses, res); 570 res.end(); 571 }, 572 default: res.send.bind(res) 162 573 }); 163 }; 164 } 165 function makeDocPost(type) { 166 return function(req,res) { 167 var doc = req.body; 168 if ( doc.type === type && tv4.validateResult(doc, schema) ) { 169 couch.post(req.body).then(function(result){ 170 res.send(200, result); 171 }, function(error){ 172 res.send(500, error); 574 }); 575 576 // Responses 577 function getResponsesBySurveyRunId(surveyRunId) { 578 var url = '_design/responses/_view/by_surveyrun?key='+JSON.stringify(surveyRunId); 579 return HTTPResult.fromResponsePromise(couch.get(url).response, 580 handleUnknownError) 581 .handle({ 582 200: handleRowValues, 583 default: handleUnknownResponse 584 }); 585 } 586 app.get('/api/responses', 587 ensureAuthenticated, 588 ensureMIME(JSON_MIME), 589 function(req,res){ 590 var hr; 591 if ( 'surveyRunId' in req.query ) { 592 hr = getResponsesBySurveyRunId(req.query.surveyRunId); 593 } else { 594 hr = getDocumentsOfType('Response'); 595 } 596 hr.handle(res.send.bind(res)); 597 }); 598 app.get('/api/responses/:id', 599 ensureAuthenticated, 600 ensureMIME(JSON_MIME), 601 makeDocGet_id('Response')); 602 app.post('/api/responses', 603 ensureAuthenticated, 604 ensureMIME(JSON_MIME), 605 function (req,res) { 606 var doc = req.body; 607 randomToken() 608 .handle({ 609 201: function(token) { 610 doc.secret = token; 611 return postDocument('Response',doc); 612 } 613 }) 614 .handle(res.send.bind(res)); 615 }); 616 app.put('/api/responses/:id', 617 ensureAuthenticated, 618 ensureMIME(JSON_MIME), 619 function (req,res) { 620 var id = req.params.id; 621 var doc = req.body; 622 var rev = etags.parse(req.header('If-Match'))[0] || (doc && doc._rev); 623 var hr; 624 if ( typeof doc.secret === 'undefined' ) { 625 hr = randomToken() 626 .handle({ 627 201: function(token) { 628 doc.secret = token; 629 return putDocument(id,rev,'Response',doc); 630 } 173 631 }); 174 632 } else { 175 res.send(400,{error: "Document failed schema verification."});633 hr = putDocument(id,rev,'Response',doc); 176 634 } 177 }; 178 } 179 function notImplemented(req,res) { 180 res.send(501,{error:"API not implemented yet."}); 181 } 182 183 // Questions 184 app.get('/api/questions', 185 ensureAuthenticated, 186 makeDocsGet('Question')); 187 app.get('/api/questions/:id', 188 ensureAuthenticated, 189 makeDocGet_id('Question')); 190 app.post('/api/questions', 191 ensureAuthenticated, 192 makeDocPost('Question')); 193 app.put('/api/questions/:id', 194 ensureAuthenticated, 195 makeDocPut_id('Question')); 196 app.delete('/api/questions/:id', 197 ensureAuthenticated, 198 makeDocDel_id('Question')); 199 app.get('/api/questions/categories', 200 ensureAuthenticated, 201 notImplemented); 202 203 // Surveys 204 app.get('/api/surveys', 205 ensureAuthenticated, 206 makeDocsGet('Survey')); 207 app.get('/api/surveys/:id', 208 ensureAuthenticated, 209 makeDocGet_id('Survey')); 210 app.post('/api/surveys', 211 ensureAuthenticated, 212 makeDocPost('Survey')); 213 app.put('/api/surveys/:id', 214 ensureAuthenticated, 215 makeDocPut_id('Survey')); 216 app.delete('/api/surveys/:id', 217 ensureAuthenticated, 218 makeDocDel_id('Survey')); 219 220 // SurveyRuns 221 app.get('/api/surveyRuns', 222 ensureAuthenticated, 223 makeDocsGet('SurveyRun')); 224 app.get('/api/surveyRuns/:id', 225 ensureAuthenticated, 226 makeDocGet_id('SurveyRun')); 227 app.get('/api/surveyRuns/:id/responses.csv', 228 ensureAuthenticated, 229 function(req, res) { 230 var id = req.params.id; 231 var url = '_design/responses/_view/by_surveyrun?key='+JSON.stringify(id); 232 couch.get(url).then(function(result){ 233 var responses = _.map(result.rows, function(item) { return item.value; }); 234 var flatResponses = responsesToVariables(responses); 235 res.set({ 236 'Content-Type': 'text/csv', 237 'Content-Disposition': 'attachment; filename=surveyRun-'+id+'-responses.csv' 238 }); 239 res.status(200); 240 writeObjectsToCSVStream(flatResponses, res); 241 res.end(); 242 }, function(error){ 243 res.send(404, {error: "Cannot find responses for survey run "+id}); 244 }); 245 }); 246 app.get('/api/surveyRuns/:id/responses', 247 ensureAuthenticated, 635 hr.handle(res.send.bind(res)); 636 }); 637 app.delete('/api/responses/:id', 638 ensureAuthenticated, 639 ensureMIME(JSON_MIME), 640 makeDocDel_id('Response')); 641 642 //respondents api 643 function isSurveyRunRunning(surveyRun) { 644 var now = new Date(); 645 var afterStart = !surveyRun.startDate || now >= new Date(surveyRun.startDate); 646 var beforeEnd = !surveyRun.endDate || now <= new Date(surveyRun.endDate); 647 return afterStart && beforeEnd; 648 } 649 app.get('/api/open/responses/:id', 650 ensureMIME(JSON_MIME), 248 651 function(req,res) { 249 652 var id = req.params.id; 250 var url = '_design/responses/_view/by_surveyrun?key='+JSON.stringify(id); 251 couch.get(url).then(function(result){ 252 var responses = _.map(result.rows, function(item) { return item.value; }); 253 res.send(200, responses); 254 }, function(error){ 255 res.send(404, {error: "Cannot find responses for survey run "+id}); 256 }); 257 }); 258 259 // Responses 260 app.get('/api/responses', 261 ensureAuthenticated, 262 makeDocsGet('Response')); 263 app.get('/api/responses/:id', 264 ensureAuthenticated, 265 makeDocGet_id('Response')); 266 app.post('/api/responses', 267 ensureAuthenticated, 268 makeDocPost('Response')); 269 app.put('/api/responses/:id', 270 ensureAuthenticated, 271 makeDocPut_id('Response')); 272 app.delete('/api/responses/:id', 273 ensureAuthenticated, 274 makeDocDel_id('Response')); 653 var rev = etags.parse(req.header('If-Non-Match'))[0]; 654 var secret = req.query.secret; 655 getDocument(id,rev,'Response') 656 .handle({ 657 200: function(response) { 658 if ( response.secret === secret ) { 659 return getDocument(response.surveyRunId,[],'SurveyRun') 660 .handle({ 661 200: function(surveyRun) { 662 if ( !isSurveyRunRunning(surveyRun) ) { 663 return new HTTPResult(404,{error:"Survey is not running anymore."}); 664 } else { 665 response._surveyRun = surveyRun; 666 return response; 667 } 668 } 669 }); 670 } else { 671 return new HTTPResult(403,{error: "Wrong secret for response"}); 672 } 673 } 674 }) 675 .handle(res.send.bind(res)); 676 }); 677 app.post('/api/open/responses', 678 ensureMIME(JSON_MIME), 679 function(req,res) { 680 var secret = req.query.secret; 681 var response = req.body; 682 delete response._surveyRun; 683 getDocument(response.surveyRunId,[],'SurveyRun') 684 .handle({ 685 200: function(surveyRun) { 686 if ( surveyRun.secret !== secret ) { 687 return new HTTPResult(403,{error:"Wrong secret for surveyRun."}); 688 } else if ( !isSurveyRunRunning(surveyRun) ) { 689 return new HTTPResult(404,{error:"Survey is not running anymore."}); 690 } else if ( surveyRun.mode === 'closed' ) { 691 return new HTTPResult(403,{error:"Survey is closed and doesn't allow new responses."}); 692 } else { 693 return randomToken() 694 .handle({ 695 201: function(token) { 696 response.secret = token; 697 return postDocument('Response',response) 698 .handle({ 699 201: function(doc){ 700 doc._surveyRun = surveyRun; 701 return doc; 702 } 703 }); 704 } 705 }); 706 } 707 } 708 }) 709 .handle(res.send.bind(res)); 710 }); 711 app.put('/api/open/responses/:id', 712 ensureMIME(JSON_MIME), 713 function(req,res){ 714 var id = req.params.id; 715 var doc = req.body; 716 var rev = etags.parse(req.header('If-Match'))[0] || (doc && doc._rev); 717 var secret = req.query.secret || doc.secret; 718 delete doc._surveyRun; 719 getDocument(id,[],'Response') 720 .handle({ 721 200: function(prev) { 722 if ( prev.secret !== secret ) { 723 return new HTTPResult(403,{error: "Secrets are not the same."}); 724 } else if ( prev.surveyRunId !== doc.surveyRunId ) { 725 return new HTTPResult(400,{error:"Response cannot change it's surveyRunId."}); 726 } else { 727 doc.secret = secret; // restore in case it got lost or was changed 728 return getDocument(doc.surveyRunId,[],'SurveyRun') 729 .handle({ 730 200: function(surveyRun) { 731 if ( !isSurveyRunRunning(surveyRun) ) { 732 return new HTTPResult(404,{error:"Survey is not running anymore."}); 733 } else { 734 return putDocument(id,rev,'Response',doc) 735 .handle({ 736 200: function(doc) { 737 doc._surveyRun = surveyRun; 738 return new HTTPResult(201,doc); 739 } 740 }); 741 } 742 } 743 }); 744 } 745 } 746 }) 747 .handle(res.send.bind(res)); 748 }); 749 app.delete('/api/open/responses/:id', 750 ensureMIME(JSON_MIME), 751 function(req,res){ 752 var id = req.params.id; 753 var doc = req.body; 754 var rev = etags.parse(req.header('If-Match'))[0] || (doc && doc._rev); 755 var secret = req.query.secret || doc.secret; 756 delete doc._surveyRun; 757 getDocument(id,[],'Response') 758 .handle({ 759 200: function(prev) { 760 if ( prev.secret !== secret ) { 761 return new HTTPResult(403,{error: "Secrets are not the same."}); 762 } else { 763 return deleteDocument(id,rev,doc); 764 } 765 } 766 }) 767 .handle(res.send.bind(res)); 768 }); 769 770 // uuids 771 app.get('/api/uuids', 772 ensureAuthenticated, 773 ensureMIME(JSON_MIME), 774 function(req,res){ 775 var count = (req.query.count && parseInt(req.query.count,10)) || 1; 776 HTTPResult.fromResponsePromise(couch.uuids({query:{count:count}}).response, 777 handleUnknownError) 778 .handle({ 779 200: function(res) { 780 return res.uuids; 781 }, 782 default: handleUnknownResponse 783 }) 784 .handle(res.send.bind(res)); 785 }); 275 786 276 787 return app; 277 278 788 } 279 789 … … 341 851 }); 342 852 } 853 854 function randomToken() { 855 var result = new HTTPResult(); 856 cryptoken(8) 857 .then(function(token){ 858 result.set(201,token); 859 }, function(ex){ 860 result.set(500,{error:"Cannot generate secrets.", innerError: ex}); 861 }); 862 return result; 863 }
Note: See TracChangeset
for help on using the changeset viewer.