├── .gitattributes ├── .gitignore ├── Chapter 2 ├── hello-node-http-server.txt ├── hello-node.txt ├── main.txt ├── modules │ ├── http-module.txt │ └── math.txt ├── test-http-module.txt └── test-math.txt ├── Chapter 3 └── sources │ ├── app.js │ ├── contacts.js │ ├── data │ └── contacts.json │ ├── hello-routes-with-get-params.js │ ├── hello-routes-with-params.js │ ├── hello-routes.js │ ├── modules │ ├── B5406_03_01.png │ ├── B5406_03_02.png │ ├── B5406_03_03.png │ ├── B5406_03_04.png │ ├── B5406_03_05.png │ └── contacts.js │ └── package.json ├── Chapter 4 └── sources │ ├── app.txt │ ├── levelup-1.txt │ ├── levelup.txt │ ├── modules │ └── contactdataservice.txt │ ├── mongodb-1.txt │ ├── mongodb-express.txt │ ├── package.txt │ └── test │ ├── contact-model-test.txt │ └── prepare.txt ├── Chapter 5 ├── contacts.wadl.txt ├── full-fledged-service.txt └── modules │ ├── contactdataservice_v1.txt │ └── contactdataservice_v2.txt ├── Chapter 6 ├── OLD │ └── source │ │ ├── http-auth-full-fledged-service.txt │ │ ├── http-basic-auth-full-fledged-service.txt │ │ ├── http-passport-basic-auth-full-fledged-service.txt │ │ ├── https-full-fledged-service.txt │ │ └── modules │ │ ├── admin.txt │ │ ├── contactdataservice_v1.txt │ │ └── contactdataservice_v2.txt └── source │ ├── http-auth-full-fledged-service.txt │ ├── http-basic-auth-full-fledged-service.txt │ ├── http-passport-basic-auth-full-fledged-service.txt │ ├── https-full-fledged-service.txt │ └── modules │ ├── admin.txt │ ├── contactdataservice_v1.txt │ └── contactdataservice_v2.txt ├── LICENSE └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /Chapter 2/hello-node-http-server.txt: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var port = 8180; 3 | 4 | function handle_GET_request(response) { 5 | response.writeHead(200, { 6 | 'Content-Type' : 'text/plain' 7 | }); 8 | response.end('Get action was requested'); 9 | } 10 | 11 | function handle_POST_request(response) { 12 | response.writeHead(200, { 13 | 'Content-Type' : 'text/plain' 14 | }); 15 | response.end('Post action was requested'); 16 | } 17 | 18 | function handle_PUT_request(response) { 19 | response.writeHead(200, { 20 | 'Content-Type' : 'text/plain' 21 | }); 22 | response.end('Put action was requested'); 23 | } 24 | 25 | function handle_HEAD_request(response) { 26 | response.writeHead(200, { 27 | 'Content-Type' : 'text/plain' 28 | }); 29 | response.end('Head action was requested'); 30 | } 31 | 32 | function handle_DELETE_request(response) { 33 | response.writeHead(200, { 34 | 'Content-Type' : 'text/plain' 35 | }); 36 | response.end('Delete action was requested'); 37 | } 38 | 39 | function handle_bad_request(response) { 40 | response.writeHead(400, { 41 | 'Content-Type' : 'text/plain' 42 | }); 43 | response.end('Bad request'); 44 | } 45 | 46 | function handle_request(request, response) { 47 | 48 | switch (request.method) { 49 | case 'GET': 50 | handle_GET_request(response); 51 | break; 52 | case 'POST': 53 | handle_POST_request(response); 54 | break; 55 | case 'PUT': 56 | handle_PUT_request(response); 57 | break; 58 | case 'DELETE': 59 | handle_DELETE_request(response); 60 | break; 61 | case 'HEAD': 62 | handle_HEAD_request(response); 63 | break; 64 | default: 65 | handle_bad_request(response); 66 | break; 67 | } 68 | console.log('Request processing ended'); 69 | } 70 | 71 | http.createServer(handle_request).listen(port, '127.0.0.1'); 72 | 73 | console.log('Started Node.js http server at http://127.0.0.1:' + port); 74 | -------------------------------------------------------------------------------- /Chapter 2/hello-node.txt: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var port = 8180; 3 | 4 | function handle_request(request, response) { 5 | response.writeHead(200, { 6 | 'Content-Type' : 'text/plain' 7 | }); 8 | 9 | response.end('Hello World. Are you restless to go restful?\n'); 10 | console.log('hello-node.js was requested'); 11 | } 12 | 13 | http.createServer(handle_request).listen(port, '127.0.0.1'); 14 | 15 | console.log('Started Node.js http server at http://127.0.0.1:' + port); 16 | 17 | -------------------------------------------------------------------------------- /Chapter 2/main.txt: -------------------------------------------------------------------------------- 1 | var httpModule = require('./modules/http-module'); 2 | 3 | var http = require('http'); 4 | var port = 8080; 5 | 6 | http.createServer(httpModule.handle_request).listen(port, '127.0.0.1'); -------------------------------------------------------------------------------- /Chapter 2/modules/http-module.txt: -------------------------------------------------------------------------------- 1 | function handle_GET_request(request, response) { 2 | response.writeHead(200, { 3 | 'Content-Type' : 'text/plain' 4 | }); 5 | response.end('Get action was requested'); 6 | } 7 | 8 | function handle_POST_request(request, response) { 9 | response.writeHead(200, { 10 | 'Content-Type' : 'text/plain' 11 | }); 12 | response.end('Post action was request'); 13 | } 14 | 15 | function handle_PUT_request(request, response) { 16 | response.writeHead(200, { 17 | 'Content-Type' : 'text/plain' 18 | }); 19 | response.end('Put action was request'); 20 | } 21 | 22 | function handle_HEAD_request(request, response) { 23 | response.writeHead(200, { 24 | 'Content-Type' : 'text/plain' 25 | }); 26 | response.end('Head action was request'); 27 | } 28 | 29 | function handle_DELETE_request(request, response) { 30 | response.writeHead(200, { 31 | 'Content-Type' : 'text/plain' 32 | }); 33 | response.end('Delete action was request'); 34 | } 35 | 36 | function handle_bad_request(request, response) { 37 | response.writeHead(400, { 38 | 'Content-Type' : 'text/plain' 39 | }); 40 | response.end('Bad request'); 41 | } 42 | 43 | exports.handle_request = function (request, response) { 44 | 45 | switch (request.method) { 46 | case 'GET': 47 | handle_GET_request(request, response); 48 | break; 49 | case 'POST': 50 | handle_POST_request(request, response); 51 | break; 52 | case 'PUT': 53 | handle_PUT_request(request, response); 54 | break; 55 | case 'DELETE': 56 | handle_DELETE_request(request, response); 57 | break; 58 | case 'HEAD': 59 | handle_HEAD_request(request, response); 60 | break; 61 | default: 62 | handle_bad_request(request, response); 63 | break; 64 | } 65 | console.log('Request processing by http-module ended'); 66 | }; -------------------------------------------------------------------------------- /Chapter 2/modules/math.txt: -------------------------------------------------------------------------------- 1 | exports.add = function (x, y) 2 | { 3 | return x + y; 4 | }; 5 | 6 | exports.subtract = function (x, y) 7 | { 8 | return x - y; 9 | }; -------------------------------------------------------------------------------- /Chapter 2/test-http-module.txt: -------------------------------------------------------------------------------- 1 | var sinon = require('sinon'); 2 | 3 | exports.test_handle_GET_request = function (test) { 4 | 5 | var response = {'writeHead' : function () {}, 'end': function() {}}; 6 | var responseMock = sinon.mock(response); 7 | 8 | responseMock.expects('end').once().withArgs('Get action was requested'); 9 | responseMock.expects('writeHead').once().withArgs(200, { 10 | 'Content-Type' : 'text/plain' 11 | }); 12 | 13 | var request = {}; 14 | var requestMock = sinon.mock(request); 15 | requestMock.method = 'GET'; 16 | 17 | var http_module = require('../modules/http-module'); 18 | 19 | http_module.handle_request(requestMock, response); 20 | 21 | responseMock.verify(); 22 | test.done(); 23 | }; 24 | 25 | -------------------------------------------------------------------------------- /Chapter 2/test-math.txt: -------------------------------------------------------------------------------- 1 | var math = require('./modules/math'); 2 | exports.test_add = function (test) { 3 | test.equal(math.add(1, 1), 2); 4 | test.done(); 5 | }; 6 | exports.test_subtract = function (test) { 7 | test.equals(math.subtract(4,2), 2); 8 | test.done(); 9 | }; 10 | -------------------------------------------------------------------------------- /Chapter 3/sources/app.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var express = require('express'); 7 | var path = require('path'); 8 | 9 | var logger = require('morgan'); 10 | var cookieParser = require('cookie-parser'); 11 | var bodyParser = require('body-parser'); 12 | var contacts = require('./modules/contacts'); 13 | var http = require('http'); 14 | var url = require('url'); 15 | 16 | var app = express(); 17 | 18 | app.get('/contacts', 19 | function(request, response){ 20 | var get_params = url.parse(request.url, true).query; 21 | 22 | if (Object.keys(get_params).length === 0) 23 | { 24 | response.setHeader('content-type', 'application/json'); 25 | response.end(JSON.stringify(contacts.list())); 26 | } 27 | else 28 | { 29 | response.setHeader('content-type', 'application/json'); 30 | response.end(JSON.stringify(contacts.query_by_arg(get_params.arg, get_params.value))); 31 | } 32 | } 33 | ); 34 | 35 | 36 | app.get('/contacts/:number', function(request, response) { 37 | response.setHeader('content-type', 'application/json'); 38 | response.end(JSON.stringify(contacts.query(request.params.number))); 39 | }); 40 | 41 | app.get('/groups', function(request, response) { 42 | console.log ('groups'); 43 | response.setHeader('content-type', 'application/json'); 44 | response.end(JSON.stringify(contacts.list_groups())); 45 | }); 46 | 47 | app.get('/groups/:name', function(request, response) { 48 | console.log ('groups'); 49 | response.setHeader('content-type', 'application/json'); 50 | response.end(JSON.stringify(contacts.get_members(request.params.name))); 51 | }); 52 | 53 | 54 | 55 | 56 | http.createServer(app).listen(3000, function(){ 57 | console.log('Express server listening on port 3000'); 58 | }); 59 | -------------------------------------------------------------------------------- /Chapter 3/sources/contacts.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | function read_json_file() 4 | { 5 | var file = './data/contacts.json'; 6 | return fs.readFileSync(file); 7 | } 8 | 9 | exports.list = function() 10 | { 11 | return JSON.parse(read_json_file()); 12 | }; 13 | 14 | exports.query = function(number) 15 | { 16 | var json = read_json_file(); 17 | var json_result = JSON.parse(json); 18 | var result = json_result.result; 19 | 20 | for (var i = 0; i < result.length; i++) 21 | { 22 | var contact = result[i]; 23 | if (contact.primarycontactnumber === number) 24 | { 25 | return contact; 26 | } 27 | } 28 | return null; 29 | }; 30 | 31 | exports.query_by_arg = function(arg, value) 32 | { 33 | var json = read_json_file(); 34 | var json_result = JSON.parse(json); 35 | var result = json_result.result; 36 | 37 | for (var i = 0; i < result.length; i++) 38 | { 39 | var contact = result[i]; 40 | if (contact[arg] === value) 41 | { 42 | return contact; 43 | } 44 | } 45 | return null; 46 | }; 47 | 48 | exports.list_groups = function() 49 | { 50 | var json = read_json_file(); 51 | var json_result = JSON.parse(json); 52 | var result = json_result.result; 53 | 54 | var resultArray = []; 55 | 56 | for (var i = 0; i < result.length; i++) 57 | { 58 | var groups = result[i].groups; 59 | for (var index = 0; index < groups.length; index++) 60 | { 61 | if (resultArray.indexOf(groups[index]) ===-1) 62 | { 63 | resultArray.push(groups[index]); 64 | } 65 | } 66 | } 67 | 68 | return resultArray; 69 | }; 70 | 71 | exports.get_members = function(group_name) 72 | { 73 | var json = read_json_file(); 74 | var json_result = JSON.parse(json); 75 | var result = json_result.result; 76 | 77 | var resultArray = []; 78 | 79 | for (var i = 0; i < result.length; i++) 80 | { 81 | 82 | if (result[i].groups.indexOf(group_name) > -1) 83 | { 84 | resultArray.push(result[i]); 85 | } 86 | } 87 | 88 | return resultArray; 89 | }; 90 | -------------------------------------------------------------------------------- /Chapter 3/sources/data/contacts.json: -------------------------------------------------------------------------------- 1 | { 2 | "result": [{ 3 | "firstname": "Joe", 4 | "lastname": "Smith", 5 | "title": "Mr.", 6 | "company": "Dev Inc.", 7 | "jobtitle": "Developer", 8 | "primarycontactnumber": "+359777123456", 9 | "othercontactnumbers": [ 10 | "+359777456789", 11 | "+359777112233" 12 | ], 13 | "primaryemailaddress": "joe.smith@xyz.com", 14 | "emailaddresses": [ 15 | "j.smith@xyz.com" 16 | ], 17 | "groups": [ 18 | "Dev", 19 | "Family" 20 | ] 21 | }, 22 | { 23 | "firstname": "John", 24 | "lastname": "Douglas", 25 | "title": "Mr.", 26 | "company": "Dev Inc.", 27 | "jobtitle": "Developer", 28 | "primarycontactnumber": "+359777223344", 29 | "othercontactnumbers": [], 30 | "primaryemailaddress": "john.douglas@xyz.com", 31 | "emailaddresses": [ 32 | "j.douglas@xyz.com" 33 | ], 34 | "groups": [ 35 | "Dev" 36 | ] 37 | }] 38 | } -------------------------------------------------------------------------------- /Chapter 3/sources/hello-routes-with-get-params.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var url = require('url'); 3 | var app = express(); 4 | 5 | app.get('/hello', function(request, response){ 6 | var get_params = url.parse(request.url, true).query; 7 | 8 | if (Object.keys(get_params).length == 0) 9 | { 10 | response.end('Hello all'); 11 | } 12 | else 13 | { 14 | response.end('Hello ' + get_params.name); 15 | } 16 | }); 17 | 18 | app.listen(3000); -------------------------------------------------------------------------------- /Chapter 3/sources/hello-routes-with-params.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); 3 | 4 | app.get('/hello/:name', function(request, response){ 5 | response.send('hello ' + request.params.name); 6 | }); 7 | 8 | app.listen(3000); -------------------------------------------------------------------------------- /Chapter 3/sources/hello-routes.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); 3 | 4 | app.get('/hello', function(req, res){ 5 | res.send('hello world'); 6 | }); 7 | 8 | app.listen(3000); -------------------------------------------------------------------------------- /Chapter 3/sources/modules/B5406_03_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/RESTful-Web-API-Design-with-Node.JS-Second-Edition/df1c23b4f7fc70a2c90088b5a074888369c83d92/Chapter 3/sources/modules/B5406_03_01.png -------------------------------------------------------------------------------- /Chapter 3/sources/modules/B5406_03_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/RESTful-Web-API-Design-with-Node.JS-Second-Edition/df1c23b4f7fc70a2c90088b5a074888369c83d92/Chapter 3/sources/modules/B5406_03_02.png -------------------------------------------------------------------------------- /Chapter 3/sources/modules/B5406_03_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/RESTful-Web-API-Design-with-Node.JS-Second-Edition/df1c23b4f7fc70a2c90088b5a074888369c83d92/Chapter 3/sources/modules/B5406_03_03.png -------------------------------------------------------------------------------- /Chapter 3/sources/modules/B5406_03_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/RESTful-Web-API-Design-with-Node.JS-Second-Edition/df1c23b4f7fc70a2c90088b5a074888369c83d92/Chapter 3/sources/modules/B5406_03_04.png -------------------------------------------------------------------------------- /Chapter 3/sources/modules/B5406_03_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/RESTful-Web-API-Design-with-Node.JS-Second-Edition/df1c23b4f7fc70a2c90088b5a074888369c83d92/Chapter 3/sources/modules/B5406_03_05.png -------------------------------------------------------------------------------- /Chapter 3/sources/modules/contacts.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | function read_json_file() 4 | { 5 | var file = './data/contacts.json'; 6 | return fs.readFileSync(file); 7 | } 8 | 9 | exports.list = function() 10 | { 11 | return JSON.parse(read_json_file()); 12 | }; 13 | 14 | exports.query = function(number) 15 | { 16 | var json = read_json_file(); 17 | var json_result = JSON.parse(json); 18 | var result = json_result.result; 19 | 20 | for (var i = 0; i < result.length; i++) 21 | { 22 | var contact = result[i]; 23 | if (contact.primarycontactnumber === number) 24 | { 25 | return contact; 26 | } 27 | } 28 | return null; 29 | }; 30 | 31 | exports.query_by_arg = function(arg, value) 32 | { 33 | var json = read_json_file(); 34 | var json_result = JSON.parse(json); 35 | var result = json_result.result; 36 | 37 | for (var i = 0; i < result.length; i++) 38 | { 39 | var contact = result[i]; 40 | if (contact[arg] === value) 41 | { 42 | return contact; 43 | } 44 | } 45 | return null; 46 | }; 47 | 48 | exports.list_groups = function() 49 | { 50 | var json = read_json_file(); 51 | var json_result = JSON.parse(json); 52 | var result = json_result.result; 53 | 54 | var resultArray = []; 55 | 56 | for (var i = 0; i < result.length; i++) 57 | { 58 | var groups = result[i].groups; 59 | for (var index = 0; index < groups.length; index++) 60 | { 61 | if (resultArray.indexOf(groups[index]) ===-1) 62 | { 63 | resultArray.push(groups[index]); 64 | } 65 | } 66 | } 67 | 68 | return resultArray; 69 | }; 70 | 71 | exports.get_members = function(group_name) 72 | { 73 | var json = read_json_file(); 74 | var json_result = JSON.parse(json); 75 | var result = json_result.result; 76 | 77 | var resultArray = []; 78 | 79 | for (var i = 0; i < result.length; i++) 80 | { 81 | 82 | if (result[i].groups.indexOf(group_name) > -1) 83 | { 84 | resultArray.push(result[i]); 85 | } 86 | } 87 | 88 | return resultArray; 89 | }; 90 | -------------------------------------------------------------------------------- /Chapter 3/sources/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chapter2", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "body-parser": "~1.13.2", 10 | "cookie-parser": "~1.3.5", 11 | "debug": "~2.2.0", 12 | "express": "~4.13.1", 13 | "jade": "~1.11.0", 14 | "morgan": "~1.6.1", 15 | "serve-favicon": "~2.3.0" 16 | } 17 | } -------------------------------------------------------------------------------- /Chapter 4/sources/app.txt: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 4 | dependencies. 5 | */ 6 | 7 | 8 | 9 | var express = require('express') 10 | , http = require('http') 11 | , path = require('path') 12 | , bodyParser = require('body-parser') 13 | , logger = require('morgan') 14 | , methodOverride = require('method-override') 15 | , errorHandler = require('errorhandler') 16 | , levelup = require('levelup'); 17 | var app = express(); 18 | var url = require('url'); 19 | 20 | // all environments 21 | app.set('port', process.env.PORT || 3000); 22 | app.set('views', __dirname + '/views'); 23 | app.set('view engine', 'jade'); 24 | 25 | app.use(methodOverride()); 26 | app.use(bodyParser.json()); 27 | // development only 28 | if ('development' == app.get('env')) { 29 | app.use(errorHandler()); 30 | } 31 | 32 | var db = levelup('./contacts', {valueEncoding: 'json'}); 33 | db.put('+359777123456', { 34 | "firstname": "Joe", 35 | "lastname": "Smith", 36 | "title": "Mr.", 37 | "company": "Dev Inc.", 38 | "jobtitle": "Developer", 39 | "primarycontactnumber": "+359777123456", 40 | "othercontactnumbers": [ 41 | "+359777456789", 42 | "+359777112233"], 43 | "primaryemailaddress": "joe.smith@xyz.com", 44 | "emailaddresses": [ 45 | "j.smith@xyz.com"], 46 | "groups": [ 47 | "Dev", 48 | "Family"] 49 | }); 50 | 51 | app.get('/contacts/:number', function(request, response) { 52 | 53 | console.log(request.url + ' : querying for ' + request.params.number); 54 | db.get(request.params.number, function(error, data) { 55 | 56 | if (error) { 57 | response.writeHead(404, { 58 | 'Content-Type' : 'text/plain'}); 59 | response.end('Not Found'); 60 | return; 61 | } 62 | 63 | response.setHeader('content-type', 'application/json'); 64 | response.send(data); 65 | }); 66 | }); 67 | 68 | console.log('Running at port ' + app.get('port')); 69 | http.createServer(app).listen(app.get('port')); 70 | 71 | -------------------------------------------------------------------------------- /Chapter 4/sources/levelup-1.txt: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 4 | dependencies. 5 | */ 6 | 7 | 8 | 9 | var express = require('express') 10 | , http = require('http') 11 | , path = require('path') 12 | , bodyParser = require('body-parser') 13 | , logger = require('morgan') 14 | , methodOverride = require('method-override') 15 | , errorHandler = require('errorhandler') 16 | , levelup = require('levelup'); 17 | var app = express(); 18 | var url = require('url'); 19 | 20 | // all environments 21 | app.set('port', process.env.PORT || 3000); 22 | app.set('views', __dirname + '/views'); 23 | app.set('view engine', 'jade'); 24 | 25 | app.use(methodOverride()); 26 | app.use(bodyParser.json()); 27 | 28 | 29 | // development only 30 | if ('development' == app.get('env')) { 31 | app.use(errorHandler()); 32 | } 33 | 34 | 35 | var db = levelup('./contact', {valueEncoding: 'json'}); 36 | 37 | 38 | app.get('/contacts/:number', function(request, response) { 39 | 40 | console.log(request.url + ' : querying for ' + request.params.number); 41 | 42 | db.get(request.params.number, function(error, data) { 43 | 44 | if (error) { 45 | response.writeHead(404, { 46 | 'Content-Type' : 'text/plain'}); 47 | response.end('Not Found'); 48 | return; 49 | } 50 | 51 | response.setHeader('content-type', 'application/json'); 52 | response.send(data); 53 | }); 54 | }); 55 | 56 | 57 | app.post('/contacts/:number', function(request, response) { 58 | 59 | console.log('Adding new contact with primary number' + request.params.number); 60 | db.put(request.params.number, request.body, function(error) { 61 | if (error) { 62 | response.writeHead(500, { 63 | 'Content-Type' : 'text/plain'}); 64 | response.end('Internal server error'); 65 | return; 66 | } 67 | 68 | response.send(request.params.number + ' successfully inserted'); 69 | }); 70 | }); 71 | 72 | app.post('/contacts', function(request, response) { 73 | 74 | console.log('Adding new contact with primary number' + request.params.number); 75 | db.put(request.params.number, request.body, function(error) { 76 | if (error) { 77 | response.writeHead(500, { 78 | 'Content-Type' : 'text/plain'}); 79 | response.end('Internal server error'); 80 | return; 81 | } 82 | 83 | response.send(request.params.number + ' successfully inserted'); 84 | }); 85 | }); 86 | 87 | app.delete('/contacts/:number', function(request, response) { 88 | 89 | console.log('Deleting contact with primary number' + request.params.number); 90 | db.del(request.params.number, function(error) { 91 | if (error) { 92 | response.writeHead(500, { 93 | 'Content-Type' : 'text/plain'}); 94 | response.end('Internal server error'); 95 | return; 96 | } 97 | 98 | response.send(request.params.number + ' successfully deleted'); 99 | }); 100 | }); 101 | 102 | app.get('/contacts', function(request, response) { 103 | console.log('Listing all contacts'); 104 | var is_first = true; 105 | 106 | 107 | response.setHeader('content-type', 'application/json'); 108 | 109 | db.createReadStream() 110 | .on('data', function (data) { 111 | console.log(data.value); 112 | if (is_first == true) 113 | { 114 | response.write('['); 115 | } 116 | else 117 | { 118 | response.write(','); 119 | } 120 | response.write(JSON.stringify(data.value)); 121 | 122 | is_first = false; 123 | 124 | }) 125 | .on('error', function (error) { 126 | console.log('Error while reading', error) 127 | }) 128 | .on('close', function () { 129 | console.log('Closing db stream'); 130 | }) 131 | .on('end', function () { 132 | console.log('Db stream closed'); 133 | response.end(']'); 134 | 135 | }) 136 | 137 | }); 138 | 139 | console.log('Running at port ' + app.get('port')); 140 | http.createServer(app).listen(app.get('port')); 141 | 142 | -------------------------------------------------------------------------------- /Chapter 4/sources/levelup.txt: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 4 | dependencies. 5 | */ 6 | 7 | 8 | 9 | var express = require('express') 10 | , http = require('http') 11 | , path = require('path') 12 | , bodyParser = require('body-parser') 13 | , logger = require('morgan') 14 | , methodOverride = require('method-override') 15 | , errorHandler = require('errorhandler') 16 | , levelup = require('levelup'); 17 | var app = express(); 18 | var url = require('url'); 19 | 20 | // all environments 21 | app.set('port', process.env.PORT || 3000); 22 | app.set('views', __dirname + '/views'); 23 | app.set('view engine', 'jade'); 24 | 25 | app.use(methodOverride()); 26 | app.use(bodyParser.json()); 27 | 28 | // development only 29 | if ('development' == app.get('env')) { 30 | app.use(errorHandler()); 31 | } 32 | 33 | var db = levelup('./data', {valueEncoding: 'json'}); 34 | db.put('+359777123456', { 35 | "firstname": "Joe", 36 | "lastname": "Smith", 37 | "title": "Mr.", 38 | "company": "Dev Inc.", 39 | "jobtitle": "Developer", 40 | "primarycontactnumber": "+359777123456", 41 | "othercontactnumbers": [ 42 | "+359777456789", 43 | "+359777112233" 44 | ], 45 | "primaryemailaddress": "joe.smith@xyz.com", 46 | "emailaddresses": [ 47 | "j.smith@xyz.com" 48 | ], 49 | "groups": [ 50 | "Dev", 51 | "Family" 52 | ] 53 | }); 54 | 55 | app.get('/contacts/:number', function(request, response) { 56 | 57 | console.log(request.url + ' : querying for ' + request.params.number); 58 | 59 | db.get(request.params.number, function(error, data) { 60 | 61 | if (error) { 62 | response.writeHead(404, { 63 | 'Content-Type' : 'text/plain'}); 64 | response.end('Not Found'); 65 | return; 66 | } 67 | 68 | response.setHeader('content-type', 'application/json'); 69 | response.send(data); 70 | }); 71 | }); 72 | 73 | console.log('Running at port ' + app.get('port')); 74 | http.createServer(app).listen(app.get('port')); 75 | 76 | -------------------------------------------------------------------------------- /Chapter 4/sources/modules/contactdataservice.txt: -------------------------------------------------------------------------------- 1 | exports.remove = function (model, _primarycontactnumber, response) { 2 | console.log('Deleting contact with primary number: ' 3 | + _primarycontactnumber); 4 | model.findOne({primarycontactnumber: _primarycontactnumber}, function(error, data) { 5 | if (error) { 6 | console.log(error); 7 | if (response != null) { 8 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 9 | response.end('Internal server error'); 10 | } 11 | return; 12 | } else { 13 | if (!data) { 14 | console.log('not found'); 15 | if (response != null) 16 | { 17 | response.writeHead(404, {'Content-Type' : 'text/plain'}); 18 | response.end('Not Found'); 19 | } 20 | return; 21 | } else { 22 | data.remove(function(error){ 23 | if (!error) { 24 | data.remove(); 25 | 26 | } 27 | else { 28 | console.log(error); 29 | } 30 | }); 31 | 32 | if (response != null){ 33 | response.send('Deleted'); 34 | } 35 | return; 36 | } 37 | } 38 | }); 39 | } 40 | 41 | exports.update = function (model, requestBody, response) { 42 | 43 | var primarynumber = requestBody.primarycontactnumber; 44 | model.findOne({primarycontactnumber: primarynumber}, function(error, data) { 45 | if (error) { 46 | console.log(error); 47 | if (response != null) { 48 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 49 | response.end('Internal server error'); 50 | } 51 | return; 52 | } else { 53 | var contact = toContact(requestBody, model); 54 | if (!data) { 55 | console.log('Contact with primary number: ' + primarynumber + 56 | ' does not exist. The contact will be created.'); 57 | 58 | contact.save(function(error) { 59 | if (!error) 60 | contact.save(); 61 | }); 62 | 63 | if (response != null) { 64 | response.end('Created'); 65 | } 66 | return; 67 | } 68 | //poulate the document with the updated values 69 | 70 | data.firstname = contact.firstname; 71 | data.lastname = contact.lastname; 72 | data.title = contact.title; 73 | data.company = contact.company; 74 | data.jobtitle = contact.jobtitle; 75 | data.primarycontactnumber = contact.primarycontactnumber; 76 | data.othercontactnumbers = contact.othercontactnumbers; 77 | data.emailaddresses = contact.emailaddresses; 78 | data.primaryemailaddress = contact.primaryemailaddress; 79 | data.groups = contact.groups; 80 | 81 | data.save(function (error) { 82 | if (!error) { 83 | console.log('Successfully updated contact with primary number: '+ primarynumber); 84 | data.save(); 85 | } else { 86 | console.log('error on save'); 87 | } 88 | }); 89 | if (response != null) { 90 | response.send('Updated'); 91 | } 92 | } 93 | }); 94 | }; 95 | 96 | exports.create = function (model, requestBody, response) { 97 | var contact = toContact(requestBody, model); 98 | var primarynumber = requestBody.primarycontactnumber; 99 | contact.save(function(error) { 100 | 101 | if (!error) { 102 | contact.save(); 103 | } else { 104 | console.log('Checking if contact saving failed due to already existing primary number:' + primarynumber); 105 | model.findOne({primarycontactnumber: primarynumber}, function(error, data) { 106 | if (error) { 107 | console.log(error); 108 | if (response != null) { 109 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 110 | response.end('Internal server error'); 111 | } 112 | return; 113 | } else { 114 | var contact = toContact(requestBody, model); 115 | if (!data) { 116 | console.log('The contact does not exist. It will be created'); 117 | contact.save(function(error) { 118 | if (!error) { 119 | contact.save(); 120 | } else { 121 | console.log(error); 122 | } 123 | }); 124 | 125 | if (response != null) { 126 | response.writeHead(201, {'Content-Type' : 'text/plain'}); 127 | response.end('Created'); 128 | } 129 | return; 130 | } else { 131 | console.log('Updating contact with primary contact number:' + primarynumber); 132 | data.firstname = contact.firstname; 133 | data.lastname = contact.lastname; 134 | data.title = contact.title; 135 | data.company = contact.company; 136 | data.jobtitle = contact.jobtitle; 137 | data.primarycontactnumber = contact.primarycontactnumber; 138 | data.othercontactnumbers = contact.othercontactnumbers; 139 | data.emailaddresses = contact.emailaddresses; 140 | data.primaryemailaddress = contact.primaryemailaddress; 141 | data.groups = contact.groups; 142 | 143 | data.save(function (error) { 144 | if (!error) { 145 | data.save(); 146 | response.end('Updated'); 147 | console.log('Successfully Updated contat with primary contact number: ' + primarynumber); 148 | } else { 149 | console.log('Error while saving contact with primary contact number:' + primarynumber); 150 | console.log(error); 151 | } 152 | }); 153 | } 154 | } 155 | }); 156 | } 157 | }); 158 | }; 159 | 160 | exports.findByNumber = function (model, _primarycontactnumber, response) { 161 | 162 | model.findOne({primarycontactnumber: _primarycontactnumber}, function(error, result) { 163 | if (error) { 164 | console.error(error); 165 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 166 | response.end('Internal server error'); 167 | return; 168 | } else { 169 | if (!result) { 170 | if (response != null) { 171 | response.writeHead(404, {'Content-Type' : 'text/plain'}); 172 | response.end('Not Found'); 173 | } 174 | return; 175 | } 176 | 177 | if (response != null){ 178 | response.setHeader('Content-Type', 'application/json'); 179 | response.send(result); 180 | } 181 | console.log(result); 182 | } 183 | }); 184 | }; 185 | 186 | exports.list = function (model, response) { 187 | model.find({}, function(error, result) { 188 | if (error) { 189 | console.error(error); 190 | return null; 191 | } 192 | if (response != null) { 193 | response.setHeader('content-type', 'application/json'); 194 | response.end(JSON.stringify(result)); 195 | } 196 | return JSON.stringify(result); 197 | }); 198 | } 199 | 200 | function toContact(body, Contact) { 201 | 202 | return new Contact( 203 | { 204 | firstname: body.firstname, 205 | lastname: body.lastname, 206 | title: body.title, 207 | company: body.company, 208 | jobtitle: body.jobtitle, 209 | primarycontactnumber: body.primarycontactnumber, 210 | primaryemailaddress: body.primaryemailaddress, 211 | emailaddresses: body.emailaddresses, 212 | groups: body.groups, 213 | othercontactnumbers: body.othercontactnumbers 214 | }); 215 | } -------------------------------------------------------------------------------- /Chapter 4/sources/mongodb-1.txt: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var contactSchema = new mongoose.Schema({ 4 | primarycontactnumber: {type: String, index: {unique: true}}, 5 | firstname: String, 6 | lastname: String, 7 | title: String, 8 | company: String, 9 | jobtitle: String, 10 | othercontactnumbers: [String], 11 | primaryemailaddress: String, 12 | emailaddresses: [String], 13 | groups: [String] 14 | }); 15 | 16 | var Contact = mongoose.model('Contact', contactSchema); 17 | 18 | 19 | var john_douglas = new Contact({ 20 | firstname: "John", 21 | lastname: "Douglas", 22 | title: "Mr.", 23 | company: "Dev Inc.", 24 | jobtitle: "Developer", 25 | primarycontactnumber: "+359777223345", 26 | othercontactnumbers: [], 27 | primaryemailaddress: "john.douglas@xyz.com", 28 | emailaddresses: ["j.douglas@xyz.com"], 29 | groups: ["Dev"] 30 | }); 31 | var db = mongoose.connection; 32 | mongoose.connect('mongodb://localhost/contacts'); 33 | john_douglas.save(function(error){ 34 | if (error) { 35 | console.log('Error while saving contact for Mr. John Douglas'); 36 | console.log(error); 37 | } 38 | else { 39 | john_douglas.save(); 40 | console.log('Contact for Mr. John Douglas has been successfully stored'); 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /Chapter 4/sources/mongodb-express.txt: -------------------------------------------------------------------------------- 1 | var express = require('express') 2 | , http = require('http') 3 | , path = require('path') 4 | , bodyParser = require('body-parser') 5 | , logger = require('morgan') 6 | , methodOverride = require('method-override') 7 | , errorHandler = require('errorhandler') 8 | , mongoose = require('mongoose') 9 | , dataservice = require('./modules/contactdataservice'); 10 | var app = express(); 11 | var url = require('url'); 12 | 13 | // all environments 14 | app.set('port', process.env.PORT || 3000); 15 | app.set('views', __dirname + '/views'); 16 | app.set('view engine', 'jade'); 17 | 18 | app.use(methodOverride()); 19 | app.use(bodyParser.json()); 20 | 21 | // development only 22 | if ('development' == app.get('env')) { 23 | app.use(errorHandler()); 24 | } 25 | 26 | //var mongodb = mongoose.connection; 27 | mongoose.connect('mongodb://localhost/contacts'); 28 | 29 | 30 | var contactSchema = new mongoose.Schema({ 31 | primarycontactnumber: {type: String, index: {unique: true}}, 32 | firstname: String, 33 | lastname: String, 34 | title: String, 35 | company: String, 36 | jobtitle: String, 37 | othercontactnumbers: [String], 38 | primaryemailaddress: String, 39 | emailaddresses: [String], 40 | groups: [String] 41 | }); 42 | 43 | var Contact = mongoose.model('Contact', contactSchema); 44 | 45 | app.get('/contacts/:number', function(request, response) { 46 | 47 | console.log(request.url + ' : querying for ' + request.params.number); 48 | dataservice.findByNumber(Contact, request.params.number, response); 49 | }); 50 | 51 | 52 | app.post('/contacts', function(request, response) { 53 | dataservice.update(Contact, request.body, response) 54 | }); 55 | 56 | app.put('/contacts', function(request, response) { 57 | dataservice.create(Contact, request.body, response) 58 | }); 59 | 60 | 61 | app.delete('/contacts/:primarycontactnumber', function(request, response) { 62 | console.log(dataservice.remove(Contact, request.params.primarycontactnumber, response)); 63 | }); 64 | 65 | app.get('/contacts', function(request, response) { 66 | 67 | dataservice.list(Contact, response); 68 | }); 69 | 70 | 71 | function toContact(body) 72 | { 73 | return new Contact( 74 | { 75 | firstname: body.firstname, 76 | lastname: body.lastname, 77 | title: body.title, 78 | company: body.company, 79 | jobtitle: body.jobtitle, 80 | primarycontactnumber: body.primarycontactnumber, 81 | othercontactnumbers: body.othercontactnumbers, 82 | primaryemailaddress: body.primaryemailaddress, 83 | emailaddresses: body.emailaddresses, 84 | groups: body.groups 85 | }); 86 | } 87 | 88 | 89 | 90 | console.log('Running at port ' + app.get('port')); 91 | http.createServer(app).listen(app.get('port')); -------------------------------------------------------------------------------- /Chapter 4/sources/package.txt: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chapter4", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www", 7 | "test": "mocha test/contact-model-test.js" 8 | }, 9 | "dependencies": { 10 | "body-parser": "~1.13.2", 11 | "cookie-parser": "~1.3.5", 12 | "debug": "~2.2.0", 13 | "express": "~4.13.1", 14 | "jade": "~1.11.0", 15 | "morgan": "~1.6.1", 16 | "serve-favicon": "~2.3.0" 17 | } 18 | } -------------------------------------------------------------------------------- /Chapter 4/sources/test/contact-model-test.txt: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var should = require('should'); 3 | var prepare = require('./prepare'); 4 | 5 | mongoose.connect('mongodb://localhost/contacts-test'); 6 | 7 | 8 | var contactSchema = new mongoose.Schema({ 9 | primarycontactnumber: {type: String, index: {unique: true}}, 10 | firstname: String, 11 | lastname: String, 12 | title: String, 13 | company: String, 14 | jobtitle: String, 15 | othercontactnumbers: [String], 16 | primaryemailaddress: String, 17 | emailaddresses: [String], 18 | groups: [String] 19 | }); 20 | 21 | var Contact = mongoose.model('Contact', contactSchema); 22 | 23 | describe('Contact: models', function () { 24 | 25 | 26 | describe('#create()', function () { 27 | it('Should create a new Contact', function (done) { 28 | 29 | var contactModel = { 30 | "firstname":"John", 31 | "lastname":"Douglas", 32 | "title":"Mr.", 33 | "company":"Dev Inc.", 34 | "jobtitle":"Developer", 35 | "primarycontactnumber":"+359777223345", 36 | "primaryemailaddress":"john.douglas@xyz.com", 37 | "groups":["Dev"], 38 | "emailaddresses":["j.douglas@xyz.com"], 39 | "othercontactnumbers":['+359777223346','+359777223347'] 40 | }; 41 | 42 | Contact.create(contactModel, function (err, createdModel) { 43 | // Check that no error occured 44 | should.not.exist(err); 45 | // Assert that the returned contact has is what we expect 46 | 47 | createdModel.firstname.should.equal('John'); 48 | createdModel.lastname.should.equal('Douglas'); 49 | createdModel.title.should.equal('Mr.'); 50 | createdModel.jobtitle.should.equal('Developer'); 51 | createdModel.primarycontactnumber.should.equal('+359777223345'); 52 | createdModel.primaryemailaddress.should.equal('john.douglas@xyz.com'); 53 | createdModel.groups[0].should.equal('Dev'); 54 | createdModel.emailaddresses[0].should.equal('j.douglas@xyz.com'); 55 | createdModel.othercontactnumbers[0].should.equal('+359777223346'); 56 | createdModel.othercontactnumbers[1].should.equal('+359777223347'); 57 | //Notify mocha that the test has completed 58 | done(); 59 | }); 60 | }); 61 | }); 62 | 63 | 64 | }); 65 | 66 | -------------------------------------------------------------------------------- /Chapter 4/sources/test/prepare.txt: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | beforeEach(function (done) { 4 | 5 | 6 | function clearDatabase() { 7 | for (var i in mongoose.connection.collections) { 8 | mongoose.connection.collections[i].remove(function() {}); 9 | } 10 | return done(); 11 | } 12 | 13 | 14 | if (mongoose.connection.readyState === 0) { 15 | mongoose.connect(config.db.test, function (err) { 16 | if (err) { 17 | throw err; 18 | } 19 | return clearDatabase(); 20 | }); 21 | } else { 22 | return clearDatabase(); 23 | } 24 | }); 25 | 26 | 27 | afterEach(function (done) { 28 | mongoose.disconnect(); 29 | return done(); 30 | }); 31 | -------------------------------------------------------------------------------- /Chapter 5/contacts.wadl.txt: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /Chapter 5/full-fledged-service.txt: -------------------------------------------------------------------------------- 1 | var express = require('express') 2 | , http = require('http') 3 | , path = require('path') 4 | , bodyParser = require('body-parser') 5 | , logger = require('morgan') 6 | , methodOverride = require('method-override') 7 | , errorHandler = require('errorhandler') 8 | , mongoose = require('mongoose') 9 | , _v1 = require('./modules/contactdataservice_v1') 10 | , _v2 = require('./modules/contactdataservice_v2') 11 | , CacheControl = require("express-cache-control") 12 | , fs = require('fs') 13 | , Grid = require('gridfs-stream') 14 | , expressPaginate = require('express-paginate') 15 | , mongoosePaginate = require('mongoose-paginate'); 16 | 17 | 18 | 19 | var app = express(); 20 | var url = require('url'); 21 | var cache = new CacheControl().middleware; 22 | 23 | // all environments 24 | app.set('port', process.env.PORT || 3000); 25 | app.set('views', __dirname + '/views'); 26 | app.set('view engine', 'jade'); 27 | 28 | app.use(methodOverride()); 29 | app.use(expressPaginate.middleware(10,100)); 30 | app.use(bodyParser.json()); 31 | 32 | // development only 33 | if ('development' == app.get('env')) { 34 | app.use(errorHandler()); 35 | } 36 | 37 | 38 | mongoose.connect('mongodb://localhost/contacts'); 39 | var mongodb = mongoose.connection; 40 | 41 | var contactSchema = new mongoose.Schema({ 42 | primarycontactnumber: {type: String, index: {unique: true}}, 43 | firstname: String, 44 | lastname: String, 45 | title: String, 46 | company: String, 47 | jobtitle: String, 48 | othercontactnumbers: [String], 49 | primaryemailaddress: String, 50 | emailaddresses: [String], 51 | groups: [String], 52 | }); 53 | 54 | contactSchema.plugin(mongoosePaginate); 55 | 56 | var Contact = mongoose.model('Contact', contactSchema); 57 | 58 | app.get('/v1/contacts/', function(request, response) { 59 | var get_params = url.parse(request.url, true).query; 60 | 61 | if (Object.keys(get_params).length == 0) 62 | { 63 | _v1.list(Contact, response); 64 | } 65 | else 66 | { 67 | var key = Object.keys(get_params)[0]; 68 | var value = get_params[key]; 69 | 70 | JSON.stringify(_v2.query_by_arg(Contact, 71 | key, 72 | value, 73 | response)); 74 | } 75 | }); 76 | 77 | app.get('/v1/contacts/:primarycontactnumber', function(request, response) { 78 | 79 | console.log(request.url + ' : querying for ' + request.params.primarycontactnumber); 80 | _v1.findByNumber(Contact, request.params.primarycontactnumber, response); 81 | }); 82 | 83 | app.post('/v1/contacts/', function(request, response) { 84 | _v1.update(Contact, request.body, response); 85 | }); 86 | 87 | app.put('/v1/contacts/', function(request, response) { 88 | _v1.create(Contact, request.body, response); 89 | }); 90 | 91 | app.delete('/v1/contacts/:primarycontactnumber', function(request, response) { 92 | _v1.remove(Contact, request.params.primarycontactnumber, response); 93 | }); 94 | 95 | //version 2 default routing 96 | 97 | app.get('/contacts/:primarycontactnumber', function(request, response) { 98 | 99 | console.log(request.url + ' : querying for ' + request.params.primarycontactnumber); 100 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 101 | }); 102 | 103 | app.post('/contacts/', function(request, response) { 104 | _v2.update(Contact, request.body, response) 105 | }); 106 | 107 | app.put('/contacts/', function(request, response) { 108 | _v2.create(Contact, request.body, response) 109 | }); 110 | 111 | app.delete('/contacts/:primarycontactnumber', function(request, response) { 112 | _v2.remove(Contact, request.params.primarycontactnumber, response); 113 | }); 114 | 115 | app.get('/contacts/:primarycontactnumber/image', function(request, response){ 116 | var gfs = Grid(mongodb.db, mongoose.mongo); 117 | _v2.getImage(gfs, request.params.primarycontactnumber, response); 118 | 119 | }) 120 | 121 | app.post('/contacts/:primarycontactnumber/image', function(request, response){ 122 | var gfs = Grid(mongodb.db, mongoose.mongo); 123 | _v2.updateImage(gfs, request, response); 124 | }) 125 | 126 | app.delete('/contacts/:primarycontactnumber/image', function(request, response){ 127 | var gfs = Grid(mongodb.db, mongoose.mongo); 128 | _v2.deleteImage(gfs, mongodb.db, request.params.primarycontactnumber, response); 129 | }); 130 | 131 | app.get('/contacts', cache('minutes',1), function(request, response) { 132 | console.log('redirecting to /v2/contacts'); 133 | response.redirect('/v2/contacts') 134 | 135 | }); 136 | 137 | 138 | app.get('/contacts/:primarycontactnumber', function(request, response) { 139 | 140 | console.log(request.url + ' : querying for ' + request.params.primarycontactnumber); 141 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 142 | }); 143 | 144 | 145 | 146 | //version 2 explicit routing 147 | 148 | app.get('/v2/contacts', cache('minutes',1), function(request, response) { 149 | var get_params = url.parse(request.url, true).query; 150 | 151 | if (Object.keys(get_params).length == 0) 152 | { 153 | _v2.list(Contact, response); 154 | } 155 | else 156 | { 157 | 158 | if (get_params['limit'] != null || get_params['page'] !=null) 159 | { 160 | _v2.paginate(Contact, request, response); 161 | } 162 | else 163 | { 164 | var key = Object.keys(get_params)[0]; 165 | var value = get_params[key]; 166 | 167 | _v2.query_by_arg(Contact, 168 | key, 169 | value, 170 | response); 171 | } 172 | } 173 | }); 174 | 175 | 176 | app.get('/v2/contacts/:primarycontactnumber/image', function(request, response){ 177 | var gfs = Grid(mongodb.db, mongoose.mongo); 178 | _v2.getImage(gfs, request.params.primarycontactnumber, response); 179 | 180 | }) 181 | 182 | 183 | app.post('/v2/contacts/:primarycontactnumber/image', function(request, response){ 184 | var gfs = Grid(mongodb.db, mongoose.mongo); 185 | _v2.updateImage(gfs, request, response); 186 | }) 187 | 188 | 189 | app.delete('/v2/contacts/:primarycontactnumber/image', function(request, response){ 190 | var gfs = Grid(mongodb.db, mongoose.mongo); 191 | _v2.deleteImage(gfs, mongodb.db, request.params.primarycontactnumber, response); 192 | }) 193 | 194 | 195 | app.get('/v2/contacts/:primarycontactnumber', function(request, response) { 196 | 197 | console.log(request.url + ' : querying for ' + request.params.primarycontactnumber); 198 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 199 | }); 200 | 201 | app.post('/v2/contacts/', function(request, response) { 202 | _v2.update(Contact, request.body, response) 203 | }); 204 | 205 | app.post('/v2/contacts/:primarycontactnumber', function(request, response) { 206 | _v2.update(Contact, request.body, response) 207 | }); 208 | 209 | app.put('/v2/contacts/', function(request, response) { 210 | _v2.create(Contact, request.body, response) 211 | }); 212 | 213 | app.delete('/v2/contacts/:primarycontactnumber', function(request, response) { 214 | _v2.remove(Contact, request.params.primarycontactnumber, response); 215 | }); 216 | 217 | 218 | function toContact(body) 219 | { 220 | return new Contact( 221 | { 222 | firstname: body.firstname, 223 | lastname: body.lastname, 224 | title: body.title, 225 | company: body.company, 226 | jobtitle: body.jobtitle, 227 | primarycontactnumber: body.primarycontactnumber, 228 | othercontactnumbers: body.othercontactnumbers, 229 | primaryemailaddress: body.primaryemailaddress, 230 | emailaddresses: body.emailaddresses, 231 | groups: body.groups 232 | }); 233 | } 234 | 235 | console.log('Running at port ' + app.get('port')); 236 | http.createServer(app).listen(app.get('port')); -------------------------------------------------------------------------------- /Chapter 5/modules/contactdataservice_v1.txt: -------------------------------------------------------------------------------- 1 | exports.remove = function (model, _primarycontactnumber, response) { 2 | console.log('Deleting contact with primary number: ' 3 | + _primarycontactnumber); 4 | model.findOne({primarycontactnumber: _primarycontactnumber}, function(error, data) { 5 | if (error) { 6 | console.log(error); 7 | if (response != null) { 8 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 9 | response.end('Internal server error'); 10 | } 11 | return; 12 | } else { 13 | if (!data) { 14 | console.log('not found'); 15 | if (response != null) 16 | { 17 | response.writeHead(404, {'Content-Type' : 'text/plain'}); 18 | response.end('Not Found'); 19 | } 20 | return; 21 | } else { 22 | data.remove(function(error){ 23 | if (!error) { 24 | data.remove(); 25 | 26 | } 27 | else { 28 | console.log(error); 29 | } 30 | }); 31 | 32 | if (response != null){ 33 | response.send('Deleted'); 34 | } 35 | return; 36 | } 37 | } 38 | }); 39 | } 40 | 41 | exports.update = function (model, requestBody, response) { 42 | 43 | var primarynumber = requestBody.primarycontactnumber; 44 | model.findOne({primarycontactnumber: primarynumber}, function(error, data) { 45 | if (error) { 46 | console.log(error); 47 | if (response != null) { 48 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 49 | response.end('Internal server error'); 50 | } 51 | return; 52 | } else { 53 | var contact = toContact(requestBody, model); 54 | if (!data) { 55 | console.log('Contact with primary number: ' + primarynumber + 56 | ' does not exist. The contact will be created.'); 57 | 58 | contact.save(function(error) { 59 | if (!error) 60 | contact.save(); 61 | }); 62 | 63 | if (response != null) { 64 | response.end('Created'); 65 | } 66 | return; 67 | } 68 | //poulate the document with the updated values 69 | 70 | data.firstname = contact.firstname; 71 | data.lastname = contact.lastname; 72 | data.title = contact.title; 73 | data.company = contact.company; 74 | data.jobtitle = contact.jobtitle; 75 | data.primarycontactnumber = contact.primarycontactnumber; 76 | data.othercontactnumbers = contact.othercontactnumbers; 77 | data.emailaddresses = contact.emailaddresses; 78 | data.primaryemailaddress = contact.primaryemailaddress; 79 | data.groups = contact.groups; 80 | 81 | data.save(function (error) { 82 | if (!error) { 83 | console.log('Successfully updated contact with primary number: '+ primarynumber); 84 | data.save(); 85 | } else { 86 | console.log('error on save'); 87 | } 88 | }); 89 | if (response != null) { 90 | response.send('Updated'); 91 | } 92 | } 93 | }); 94 | }; 95 | 96 | exports.create = function (model, requestBody, response) { 97 | var contact = toContact(requestBody, model); 98 | var primarynumber = requestBody.primarycontactnumber; 99 | contact.save(function(error) { 100 | 101 | if (!error) { 102 | contact.save(); 103 | } else { 104 | console.log('Checking if contact saving failed due to already existing primary number:' + primarynumber); 105 | model.findOne({primarycontactnumber: primarynumber}, function(error, data) { 106 | if (error) { 107 | console.log(error); 108 | if (response != null) { 109 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 110 | response.end('Internal server error'); 111 | } 112 | return; 113 | } else { 114 | var contact = toContact(requestBody, model); 115 | if (!data) { 116 | console.log('The contact does not exist. It will be created'); 117 | contact.save(function(error) { 118 | if (!error) { 119 | contact.save(); 120 | } else { 121 | console.log(error); 122 | } 123 | }); 124 | 125 | if (response != null) { 126 | response.writeHead(201, {'Content-Type' : 'text/plain'}); 127 | response.end('Created'); 128 | } 129 | return; 130 | } else { 131 | console.log('Updating contact with primary contact number:' + primarynumber); 132 | data.firstname = contact.firstname; 133 | data.lastname = contact.lastname; 134 | data.title = contact.title; 135 | data.company = contact.company; 136 | data.jobtitle = contact.jobtitle; 137 | data.primarycontactnumber = contact.primarycontactnumber; 138 | data.othercontactnumbers = contact.othercontactnumbers; 139 | data.emailaddresses = contact.emailaddresses; 140 | data.primaryemailaddress = contact.primaryemailaddress; 141 | data.groups = contact.groups; 142 | 143 | data.save(function (error) { 144 | if (!error) { 145 | data.save(); 146 | response.end('Updated'); 147 | console.log('Successfully Updated contat with primary contact number: ' + primarynumber); 148 | } else { 149 | console.log('Error while saving contact with primary contact number:' + primarynumber); 150 | console.log(error); 151 | } 152 | }); 153 | } 154 | } 155 | }); 156 | } 157 | }); 158 | }; 159 | 160 | exports.findByNumber = function (model, _primarycontactnumber, response) { 161 | 162 | model.findOne({primarycontactnumber: _primarycontactnumber}, function(error, result) { 163 | if (error) { 164 | console.error(error); 165 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 166 | response.end('Internal server error'); 167 | return; 168 | } else { 169 | if (!result) { 170 | if (response != null) { 171 | response.writeHead(404, {'Content-Type' : 'text/plain'}); 172 | response.end('Not Found'); 173 | } 174 | return; 175 | } 176 | 177 | if (response != null){ 178 | response.setHeader('Content-Type', 'application/json'); 179 | response.send(result); 180 | } 181 | } 182 | }); 183 | }; 184 | 185 | exports.list = function (model, response) { 186 | model.find({}, function(error, result) { 187 | if (error) { 188 | console.error(error); 189 | return null; 190 | } 191 | if (response != null) { 192 | response.setHeader('content-type', 'application/json'); 193 | response.end(JSON.stringify(result)); 194 | } 195 | return JSON.stringify(result); 196 | }); 197 | } 198 | 199 | function toContact(body, Contact) { 200 | 201 | return new Contact( 202 | { 203 | firstname: body.firstname, 204 | lastname: body.lastname, 205 | title: body.title, 206 | company: body.company, 207 | jobtitle: body.jobtitle, 208 | primarycontactnumber: body.primarycontactnumber, 209 | primaryemailaddress: body.primaryemailaddress, 210 | emailaddresses: body.emailaddresses, 211 | groups: body.groups, 212 | othercontactnumbers: body.othercontactnumbers 213 | }); 214 | } -------------------------------------------------------------------------------- /Chapter 5/modules/contactdataservice_v2.txt: -------------------------------------------------------------------------------- 1 | exports.remove = function (model, _primarycontactnumber, response) { 2 | console.log('Deleting contact with primary number: ' 3 | + _primarycontactnumber); 4 | model.findOne({primarycontactnumber: _primarycontactnumber}, function(error, data) { 5 | if (error) { 6 | console.log(error); 7 | if (response != null) { 8 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 9 | response.end('Internal server error'); 10 | } 11 | return; 12 | } else { 13 | if (!data) { 14 | console.log('not found'); 15 | if (response != null) 16 | { 17 | response.writeHead(404, {'Content-Type' : 'text/plain'}); 18 | response.end('Not Found'); 19 | } 20 | return; 21 | } else { 22 | data.remove(function(error){ 23 | if (!error) { 24 | data.remove(); 25 | 26 | } 27 | else { 28 | console.log(error); 29 | } 30 | }); 31 | 32 | if (response != null){ 33 | response.send('Deleted'); 34 | } 35 | return; 36 | } 37 | } 38 | }); 39 | } 40 | 41 | exports.update = function (model, requestBody, response) { 42 | 43 | var primarynumber = requestBody.primarycontactnumber; 44 | model.findOne({primarycontactnumber: primarynumber}, function(error, data) { 45 | if (error) { 46 | console.log(error); 47 | if (response != null) { 48 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 49 | response.end('Internal server error'); 50 | } 51 | return; 52 | } else { 53 | var contact = toContact(requestBody, model); 54 | if (!data) { 55 | console.log('Contact with primary number: ' + primarynumber + 56 | ' does not exist. The contact will be created.'); 57 | 58 | contact.save(function(error) { 59 | if (!error) 60 | contact.save(); 61 | }); 62 | 63 | if (response != null) { 64 | response.writeHead(201, {'Content-Type' : 'text/plain'}); 65 | response.end('Created'); 66 | } 67 | return; 68 | } 69 | //poulate the document with the updated values 70 | 71 | data.firstname = contact.firstname; 72 | data.lastname = contact.lastname; 73 | data.title = contact.title; 74 | data.company = contact.company; 75 | data.jobtitle = contact.jobtitle; 76 | data.primarycontactnumber = contact.primarycontactnumber; 77 | data.othercontactnumbers = contact.othercontactnumbers; 78 | data.emailaddresses = contact.emailaddresses; 79 | data.primaryemailaddress = contact.primaryemailaddress; 80 | data.groups = contact.groups; 81 | 82 | data.save(function (error) { 83 | if (!error) { 84 | console.log('Successfully updated contact with primary number: '+ primarynumber); 85 | data.save(); 86 | } else { 87 | console.log('error on save'); 88 | } 89 | }); 90 | if (response != null) { 91 | response.status(200).send('Updated'); 92 | } 93 | } 94 | }); 95 | }; 96 | 97 | exports.create = function (model, requestBody, response) { 98 | var contact = toContact(requestBody, model); 99 | var primarynumber = requestBody.primarycontactnumber; 100 | contact.save(function(error) { 101 | 102 | if (!error) { 103 | contact.save(); 104 | } else { 105 | console.log('Checking if contact saving failed due to already existing primary number:' + primarynumber); 106 | model.findOne({primarycontactnumber: primarynumber}, function(error, data) { 107 | if (error) { 108 | console.log(error); 109 | if (response != null) { 110 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 111 | response.end('Internal server error'); 112 | } 113 | return; 114 | } else { 115 | var contact = toContact(requestBody, model); 116 | if (!data) { 117 | console.log('The contact does not exist. It will be created'); 118 | contact.save(function(error) { 119 | if (!error) { 120 | contact.save(); 121 | } else { 122 | console.log(error); 123 | } 124 | }); 125 | 126 | if (response != null) { 127 | response.writeHead(201, {'Content-Type' : 'text/plain'}); 128 | response.end('Created'); 129 | } 130 | return; 131 | } else { 132 | console.log('Updating contact with primary contact number:' + primarynumber); 133 | data.firstname = contact.firstname; 134 | data.lastname = contact.lastname; 135 | data.title = contact.title; 136 | data.company = contact.company; 137 | data.jobtitle = contact.jobtitle; 138 | data.primarycontactnumber = contact.primarycontactnumber; 139 | data.othercontactnumbers = contact.othercontactnumbers; 140 | data.emailaddresses = contact.emailaddresses; 141 | data.primaryemailaddress = contact.primaryemailaddress; 142 | data.groups = contact.groups; 143 | 144 | data.save(function (error) { 145 | if (!error) { 146 | data.save(); 147 | response.end('Updated'); 148 | console.log('Successfully Updated contat with primary contact number: ' + primarynumber); 149 | } else { 150 | console.log('Error while saving contact with primary contact number:' + primarynumber); 151 | console.log(error); 152 | } 153 | }); 154 | } 155 | } 156 | }); 157 | } 158 | }); 159 | }; 160 | 161 | exports.findByNumber = function (model, _primarycontactnumber, response) { 162 | 163 | model.findOne({primarycontactnumber: _primarycontactnumber}, function(error, result) { 164 | if (error) { 165 | console.error(error); 166 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 167 | response.end('Internal server error'); 168 | return; 169 | } else { 170 | if (!result) { 171 | if (response != null) { 172 | response.writeHead(404, {'Content-Type' : 'text/plain'}); 173 | response.end('Not Found'); 174 | } 175 | return; 176 | } 177 | 178 | if (response != null){ 179 | response.setHeader('Content-Type', 'application/json'); 180 | response.send(result); 181 | } 182 | } 183 | }); 184 | }; 185 | 186 | exports.list = function (model, response) { 187 | model.find({}, function(error, result) { 188 | if (error) { 189 | console.error(error); 190 | return null; 191 | } 192 | if (response != null) { 193 | response.json({ 194 | object: 'contacts', 195 | result: result 196 | }); 197 | } 198 | }); 199 | } 200 | 201 | exports.paginate = function (model, request, response) { 202 | 203 | model.paginate({}, 204 | {page: request.query.page, limit: request.query.limit}, 205 | function(error, result) { 206 | if (error) { 207 | console.error(error); 208 | response.writeHead(500, { 209 | 'Content-Type' : 'text/plain'}); 210 | response.end('Internal server error'); 211 | return; 212 | } 213 | 214 | response.json({ 215 | object: 'contacts', 216 | page_count: 20, 217 | result: result 218 | }); 219 | 220 | }); 221 | } 222 | 223 | function toContact(body, Contact) { 224 | 225 | return new Contact( 226 | { 227 | firstname: body.firstname, 228 | lastname: body.lastname, 229 | title: body.title, 230 | company: body.company, 231 | jobtitle: body.jobtitle, 232 | primarycontactnumber: body.primarycontactnumber, 233 | primaryemailaddress: body.primaryemailaddress, 234 | emailaddresses: body.emailaddresses, 235 | groups: body.groups, 236 | othercontactnumbers: body.othercontactnumbers 237 | }); 238 | } 239 | 240 | exports.query_by_arg = function (model, key, value, response) { 241 | //build a JSON string with the attribute and the value 242 | var filterArg = '{\"'+key + '\":' +'\"'+ value + '\"}'; 243 | var filter = JSON.parse(filterArg); 244 | 245 | model.find(filter, function(error, result) { 246 | if (error) { 247 | console.error(error); 248 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 249 | response.end('Internal server error'); 250 | return; 251 | } else { 252 | if (!result) { 253 | if (response != null) { 254 | response.writeHead(404, {'Content-Type' : 'text/plain'}); 255 | response.end('Not Found'); 256 | } 257 | return; 258 | } 259 | 260 | if (response != null){ 261 | response.setHeader('Content-Type', 'application/json'); 262 | response.send(result); 263 | } 264 | } 265 | }); 266 | } 267 | 268 | exports.updateImage = function(gfs, request, response) { 269 | var _primarycontactnumber = request.params.primarycontactnumber; 270 | console.log('Updating image for primary contact number:' + _primarycontactnumber); 271 | request.pipe(gfs.createWriteStream({ 272 | _id : _primarycontactnumber, 273 | filename : 'image', 274 | mode : 'w' 275 | })); 276 | response.send("Successfully uploaded image for primary contact number: " 277 | + _primarycontactnumber); 278 | 279 | }; 280 | 281 | exports.getImage = function(gfs, _primarycontactnumber, response) { 282 | console.log('Requesting image for primary contact number: ' + _primarycontactnumber); 283 | var imageStream = gfs.createReadStream({ 284 | _id : _primarycontactnumber, 285 | filename : 'image', 286 | mode : 'r' 287 | }); 288 | 289 | imageStream.on('error', function(error) { 290 | response.send('404', 'Not found'); 291 | return; 292 | }); 293 | 294 | response.setHeader('Content-Type', 'image/jpeg'); 295 | imageStream.pipe(response); 296 | }; 297 | 298 | exports.deleteImage = function(gfs, mongodb, _primarycontactnumber, response) { 299 | console.log('Deleting image for primary contact number:' + _primarycontactnumber); 300 | var collection = mongodb.collection('fs.files'); 301 | 302 | collection.remove({_id: _primarycontactnumber, 303 | filename: 'image'}, function (error, contact) { 304 | if (error) { 305 | console.log(error); 306 | return; 307 | } 308 | 309 | if (contact === null) { 310 | response.send('404', 'Not found'); 311 | return; 312 | } 313 | else { 314 | console.log('Successfully deleted image for primary contact number: ' + _primarycontactnumber); 315 | } 316 | }); 317 | 318 | response.send('Successfully deleted image for primary contact number: ' + _primarycontactnumber); 319 | } 320 | -------------------------------------------------------------------------------- /Chapter 6/OLD/source/http-auth-full-fledged-service.txt: -------------------------------------------------------------------------------- 1 | var express = require('express') 2 | , http = require('http') 3 | , path = require('path') 4 | , bodyParser = require('body-parser') 5 | , logger = require('morgan') 6 | , methodOverride = require('method-override') 7 | , errorHandler = require('errorhandler') 8 | , mongoose = require('mongoose') 9 | , _v1 = require('./modules/contactdataservice_v1') 10 | , _v2 = require('./modules/contactdataservice_v2') 11 | , CacheControl = require("express-cache-control") 12 | , fs = require('fs') 13 | , Grid = require('gridfs-stream') 14 | , expressPaginate = require('express-paginate') 15 | , mongoosePaginate = require('mongoose-paginate') 16 | , auth = require('basic-auth'); 17 | 18 | var app = express(); 19 | var url = require('url'); 20 | var cache = new CacheControl().middleware; 21 | 22 | // all environments 23 | app.set('port', process.env.PORT || 3000); 24 | app.set('views', __dirname + '/views'); 25 | app.set('view engine', 'jade'); 26 | 27 | app.use(methodOverride()); 28 | app.use(expressPaginate.middleware(10,100)); 29 | app.use(bodyParser()); 30 | 31 | // development only 32 | if ('development' == app.get('env')) { 33 | app.use(errorHandler()); 34 | } 35 | 36 | 37 | mongoose.connect('mongodb://localhost/contacts'); 38 | var mongodb = mongoose.connection; 39 | 40 | var contactSchema = new mongoose.Schema({ 41 | primarycontactnumber: {type: String, index: {unique: true}}, 42 | firstname: String, 43 | lastname: String, 44 | title: String, 45 | company: String, 46 | jobtitle: String, 47 | othercontactnumbers: [String], 48 | primaryemailaddress: String, 49 | emailaddresses: [String], 50 | groups: [String], 51 | }); 52 | var authUserSchema = new mongoose.Schema({ 53 | username: {type: String, index: {unique: true}}, 54 | password: String, 55 | role: String, 56 | }); 57 | 58 | var AuthUser = mongoose.model('AuthUser', authUserSchema); 59 | 60 | var adminUser = new AuthUser({ 61 | username: 'admin', 62 | password: 'admin', 63 | role: 'Admin' 64 | }); 65 | 66 | adminUser.save(function(error) { 67 | if (!error) { 68 | adminUser.save(); 69 | console.log('Creating Admin user'); 70 | } else { 71 | console.log('Admin user already exist'); 72 | } 73 | }); 74 | 75 | app.use(function(request, response, next) { 76 | var user = auth(request); 77 | if (user === undefined) { 78 | console.log('User information is not available in the request'); 79 | response.statusCode = 401; 80 | response.setHeader('WWW-Authenticate', 'Basic'); 81 | response.end('Unauthorized'); 82 | } else { 83 | authenticate(user, response, next); 84 | } 85 | }); 86 | 87 | function authenticate(user, response, next) { 88 | var result = false; 89 | AuthUser.findOne({username: user['name'], password: user['pass']}, function(error, data) { 90 | if (error) { 91 | console.log(error); 92 | response.statusCode = 401; 93 | response.end('Unauthorized'); 94 | } else { 95 | 96 | if (!data) { 97 | console.log('unknown user'); 98 | response.statusCode = 401; 99 | response.end('Unauthorized'); 100 | } else { 101 | console.log(data.username + ' authenticated successfully'); 102 | next(); 103 | } 104 | } 105 | }); 106 | } 107 | 108 | contactSchema.plugin(mongoosePaginate); 109 | 110 | var Contact = mongoose.model('Contact', contactSchema); 111 | 112 | app.get('/v1/contacts/', function(request, response) { 113 | var get_params = url.parse(request.url, true).query; 114 | 115 | if (Object.keys(get_params).length == 0) 116 | { 117 | _v1.list(Contact, response); 118 | } 119 | else 120 | { 121 | var key = Object.keys(get_params)[0]; 122 | var value = get_params[key]; 123 | 124 | JSON.stringify(_v2.query_by_arg(Contact, 125 | key, 126 | value, 127 | response)); 128 | } 129 | }); 130 | 131 | app.get('/v1/contacts/:primarycontactnumber', function(request, response) { 132 | 133 | console.log(request.url + ' : querying for ' + request.params.number); 134 | _v1.findByNumber(Contact, request.params.primarycontactnumber, response); 135 | }); 136 | 137 | app.post('/v1/contacts/', function(request, response) { 138 | _v1.update(Contact, request.body, response) 139 | }); 140 | 141 | app.put('/v1/contacts/', function(request, response) { 142 | _v1.create(Contact, request.body, response) 143 | }); 144 | 145 | app.del('/v1/contacts/:primarycontactnumber', function(request, response) { 146 | _v1.remove(Contact, request.params.primarycontactnumber, response); 147 | }); 148 | 149 | //version 2 default routing 150 | 151 | app.get('/contacts/:primarycontactnumber', function(request, response) { 152 | 153 | console.log(request.url + ' : querying for ' + request.params.number); 154 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 155 | }); 156 | 157 | app.post('/contacts/', function(request, response) { 158 | _v2.update(Contact, request.body, response) 159 | }); 160 | 161 | app.put('/contacts/', function(request, response) { 162 | _v2.create(Contact, request.body, response) 163 | }); 164 | 165 | app.del('/contacts/:primarycontactnumber', function(request, response) { 166 | _v2.remove(Contact, request.params.primarycontactnumber, response); 167 | }); 168 | 169 | app.get('/contacts/:primarycontactnumber/image', function(request, response){ 170 | var gfs = Grid(mongodb.db, mongoose.mongo); 171 | _v2.getImage(gfs, request.params.primarycontactnumber, response); 172 | 173 | }) 174 | 175 | app.post('/contacts/:primarycontactnumber/image', function(request, response){ 176 | var gfs = Grid(mongodb.db, mongoose.mongo); 177 | _v2.updateImage(gfs, request, response); 178 | }) 179 | 180 | app.del('/contacts/:primarycontactnumber/image', function(request, response){ 181 | var gfs = Grid(mongodb.db, mongoose.mongo); 182 | _v2.deleteImage(gfs, mongodb.db, request.params.primarycontactnumber, response); 183 | }) 184 | 185 | app.get('/contacts', cache('minutes',1), function(request, response) { 186 | var get_params = url.parse(request.url, true).query; 187 | console.log('redirecting to /v2/contacts'); 188 | response.writeHead(302, {'Location' : '/v2/contacts/'}); 189 | response.end('Version 2 is found at URI /v2/contacts/ '); 190 | 191 | }); 192 | 193 | 194 | app.get('/contacts/:primarycontactnumber', function(request, response) { 195 | 196 | console.log(request.url + ' : querying for ' + request.params.number); 197 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 198 | }); 199 | 200 | 201 | 202 | //version 2 explicit routing 203 | 204 | app.get('/v2/contacts', cache('minutes',1), function(request, response) { 205 | var get_params = url.parse(request.url, true).query; 206 | 207 | if (Object.keys(get_params).length == 0) 208 | { 209 | _v2.paginate(Contact, request, response); 210 | } 211 | else 212 | { 213 | if (get_params['limit'] != null || get_params['page'] !=null) 214 | { 215 | _v2.paginate(Contact, request, response); 216 | } 217 | else 218 | { 219 | var key = Object.keys(get_params)[0]; 220 | var value = get_params[key]; 221 | 222 | _v2.query_by_arg(Contact, 223 | key, 224 | value, 225 | response); 226 | } 227 | } 228 | }); 229 | 230 | 231 | app.get('/v2/contacts/:primarycontactnumber/image', function(request, response){ 232 | var gfs = Grid(mongodb.db, mongoose.mongo); 233 | _v2.getImage(gfs, request.params.primarycontactnumber, response); 234 | 235 | }) 236 | 237 | 238 | app.post('/v2/contacts/:primarycontactnumber/image', function(request, response){ 239 | var gfs = Grid(mongodb.db, mongoose.mongo); 240 | _v2.updateImage(gfs, request, response); 241 | }) 242 | 243 | 244 | app.del('/v2/contacts/:primarycontactnumber/image', function(request, response){ 245 | var gfs = Grid(mongodb.db, mongoose.mongo); 246 | _v2.deleteImage(gfs, mongodb.db, request.params.primarycontactnumber, response); 247 | }) 248 | 249 | 250 | app.get('/v2/contacts/:primarycontactnumber', function(request, response) { 251 | 252 | console.log(request.url + ' : querying for ' + request.params.number); 253 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 254 | }); 255 | 256 | app.post('/v2/contacts/', function(request, response) { 257 | _v2.update(Contact, request.body, response) 258 | }); 259 | 260 | app.put('/v2/contacts/', function(request, response) { 261 | _v2.create(Contact, request.body, response) 262 | }); 263 | 264 | app.del('/v2/contacts/:primarycontactnumber', function(request, response) { 265 | _v2.remove(Contact, request.params.primarycontactnumber, response); 266 | }); 267 | 268 | function toContact(body) 269 | { 270 | return new Contact( 271 | { 272 | firstname: body.firstname, 273 | lastname: body.lastname, 274 | title: body.title, 275 | company: body.company, 276 | jobtitle: body.jobtitle, 277 | primarycontactnumber: body.primarycontactnumber, 278 | othercontactnumbers: body.othercontactnumbers, 279 | primaryemailaddress: body.primaryemailaddress, 280 | emailaddresses: body.emailaddresses, 281 | groups: body.groups 282 | }); 283 | } 284 | 285 | console.log('Running at port ' + app.get('port')); 286 | http.createServer(app).listen(app.get('port')); -------------------------------------------------------------------------------- /Chapter 6/OLD/source/http-basic-auth-full-fledged-service.txt: -------------------------------------------------------------------------------- 1 | var express = require('express') 2 | , http = require('http') 3 | , path = require('path') 4 | , bodyParser = require('body-parser') 5 | , logger = require('morgan') 6 | , methodOverride = require('method-override') 7 | , errorHandler = require('errorhandler') 8 | , mongoose = require('mongoose') 9 | , _v1 = require('./modules/contactdataservice_v1') 10 | , _v2 = require('./modules/contactdataservice_v2') 11 | , CacheControl = require("express-cache-control") 12 | , fs = require('fs') 13 | , Grid = require('gridfs-stream') 14 | , expressPaginate = require('express-paginate') 15 | , mongoosePaginate = require('mongoose-paginate') 16 | , auth = require('basic-auth'); 17 | 18 | var app = express(); 19 | var url = require('url'); 20 | var cache = new CacheControl().middleware; 21 | 22 | // all environments 23 | app.set('port', process.env.PORT || 3000); 24 | app.set('views', __dirname + '/views'); 25 | app.set('view engine', 'jade'); 26 | 27 | app.use(methodOverride()); 28 | app.use(expressPaginate.middleware(10,100)); 29 | app.use(bodyParser.json()); 30 | 31 | // development only 32 | if ('development' == app.get('env')) { 33 | app.use(errorHandler()); 34 | } 35 | 36 | 37 | mongoose.connect('mongodb://localhost/contacts'); 38 | var mongodb = mongoose.connection; 39 | 40 | var contactSchema = new mongoose.Schema({ 41 | primarycontactnumber: {type: String, index: {unique: true}}, 42 | firstname: String, 43 | lastname: String, 44 | title: String, 45 | company: String, 46 | jobtitle: String, 47 | othercontactnumbers: [String], 48 | primaryemailaddress: String, 49 | emailaddresses: [String], 50 | groups: [String], 51 | }); 52 | var authUserSchema = new mongoose.Schema({ 53 | username: {type: String, index: {unique: true}}, 54 | password: String, 55 | role: String, 56 | }); 57 | 58 | var AuthUser = mongoose.model('AuthUser', authUserSchema); 59 | 60 | var adminUser = new AuthUser({ 61 | username: 'admin', 62 | password: 'admin', 63 | role: 'Admin' 64 | }); 65 | 66 | adminUser.save(function(error) { 67 | if (!error) { 68 | adminUser.save(); 69 | console.log('Creating Admin user'); 70 | } else { 71 | console.log('Admin user already exist'); 72 | } 73 | }); 74 | 75 | app.use(function(request, response, next) { 76 | var credentials = auth(request); 77 | if (credentials === undefined) { 78 | console.log('User information is not available in the request'); 79 | response.statusCode = 401; 80 | response.setHeader('WWW-Authenticate', 'Basic'); 81 | response.end('Unauthorized'); 82 | } else { 83 | authenticate(credentials.name, credentials.pass, response, next); 84 | } 85 | }); 86 | 87 | function authenticate(_username, _password, response, callback) { 88 | AuthUser.findOne({username:_username, password: _password}, function(error, data) { 89 | if (error) { 90 | console.log(error); 91 | return; 92 | } else { 93 | if (!data) { 94 | console.log('User not found'); 95 | response.statusCode = 401; 96 | response.end(); 97 | return; 98 | } else { 99 | console.log(data.username + ' authenticated successfully'); 100 | return callback(null, data.username); 101 | } 102 | } 103 | }); 104 | } 105 | 106 | contactSchema.plugin(mongoosePaginate); 107 | 108 | var Contact = mongoose.model('Contact', contactSchema); 109 | 110 | app.get('/v1/contacts/', function(request, response) { 111 | var get_params = url.parse(request.url, true).query; 112 | 113 | if (Object.keys(get_params).length == 0) 114 | { 115 | _v1.list(Contact, response); 116 | } 117 | else 118 | { 119 | var key = Object.keys(get_params)[0]; 120 | var value = get_params[key]; 121 | 122 | JSON.stringify(_v2.query_by_arg(Contact, 123 | key, 124 | value, 125 | response)); 126 | } 127 | }); 128 | 129 | app.get('/v1/contacts/:primarycontactnumber', function(request, response) { 130 | 131 | console.log(request.url + ' : querying for ' + request.params.number); 132 | _v1.findByNumber(Contact, request.params.primarycontactnumber, response); 133 | }); 134 | 135 | app.post('/v1/contacts/', function(request, response) { 136 | _v1.update(Contact, request.body, response) 137 | }); 138 | 139 | app.put('/v1/contacts/', function(request, response) { 140 | _v1.create(Contact, request.body, response) 141 | }); 142 | 143 | app.del('/v1/contacts/:primarycontactnumber', function(request, response) { 144 | _v1.remove(Contact, request.params.primarycontactnumber, response); 145 | }); 146 | 147 | //version 2 default routing 148 | 149 | app.get('/contacts/:primarycontactnumber', function(request, response) { 150 | 151 | console.log(request.url + ' : querying for ' + request.params.number); 152 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 153 | }); 154 | 155 | app.post('/contacts/', function(request, response) { 156 | _v2.update(Contact, request.body, response) 157 | }); 158 | 159 | app.put('/contacts/', function(request, response) { 160 | _v2.create(Contact, request.body, response) 161 | }); 162 | 163 | app.del('/contacts/:primarycontactnumber', function(request, response) { 164 | _v2.remove(Contact, request.params.primarycontactnumber, response); 165 | }); 166 | 167 | app.get('/contacts/:primarycontactnumber/image', function(request, response){ 168 | var gfs = Grid(mongodb.db, mongoose.mongo); 169 | _v2.getImage(gfs, request.params.primarycontactnumber, response); 170 | 171 | }) 172 | 173 | app.post('/contacts/:primarycontactnumber/image', function(request, response){ 174 | var gfs = Grid(mongodb.db, mongoose.mongo); 175 | _v2.updateImage(gfs, request, response); 176 | }) 177 | 178 | app.del('/contacts/:primarycontactnumber/image', function(request, response){ 179 | var gfs = Grid(mongodb.db, mongoose.mongo); 180 | _v2.deleteImage(gfs, mongodb.db, request.params.primarycontactnumber, response); 181 | }) 182 | 183 | app.get('/contacts', cache('minutes',1), function(request, response) { 184 | var get_params = url.parse(request.url, true).query; 185 | console.log('redirecting to /v2/contacts'); 186 | response.writeHead(302, {'Location' : '/v2/contacts/'}); 187 | response.end('Version 2 is found at URI /v2/contacts/ '); 188 | 189 | }); 190 | 191 | 192 | app.get('/contacts/:primarycontactnumber', function(request, response) { 193 | 194 | console.log(request.url + ' : querying for ' + request.params.number); 195 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 196 | }); 197 | 198 | 199 | 200 | //version 2 explicit routing 201 | 202 | app.get('/v2/contacts', cache('minutes',1), function(request, response) { 203 | var get_params = url.parse(request.url, true).query; 204 | if (Object.keys(get_params).length == 0) 205 | { 206 | _v2.paginate(Contact, request, response); 207 | } 208 | else 209 | { 210 | if (get_params['limit'] != null || get_params['page'] !=null) 211 | { 212 | _v2.paginate(Contact, request, response); 213 | } 214 | else 215 | { 216 | var key = Object.keys(get_params)[0]; 217 | var value = get_params[key]; 218 | 219 | _v2.query_by_arg(Contact, 220 | key, 221 | value, 222 | response); 223 | } 224 | } 225 | }); 226 | 227 | 228 | app.get('/v2/contacts/:primarycontactnumber/image', function(request, response){ 229 | var gfs = Grid(mongodb.db, mongoose.mongo); 230 | _v2.getImage(gfs, request.params.primarycontactnumber, response); 231 | 232 | }) 233 | 234 | 235 | app.post('/v2/contacts/:primarycontactnumber/image', function(request, response){ 236 | var gfs = Grid(mongodb.db, mongoose.mongo); 237 | _v2.updateImage(gfs, request, response); 238 | }) 239 | 240 | 241 | app.del('/v2/contacts/:primarycontactnumber/image', function(request, response){ 242 | var gfs = Grid(mongodb.db, mongoose.mongo); 243 | _v2.deleteImage(gfs, mongodb.db, request.params.primarycontactnumber, response); 244 | }) 245 | 246 | 247 | app.get('/v2/contacts/:primarycontactnumber', function(request, response) { 248 | 249 | console.log(request.url + ' : querying for ' + request.params.number); 250 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 251 | }); 252 | 253 | app.post('/v2/contacts/', function(request, response) { 254 | _v2.update(Contact, request.body, response) 255 | }); 256 | 257 | app.put('/v2/contacts/', function(request, response) { 258 | _v2.create(Contact, request.body, response) 259 | }); 260 | 261 | app.del('/v2/contacts/:primarycontactnumber', function(request, response) { 262 | _v2.remove(Contact, request.params.primarycontactnumber, response); 263 | }); 264 | 265 | function toContact(body) 266 | { 267 | return new Contact( 268 | { 269 | firstname: body.firstname, 270 | lastname: body.lastname, 271 | title: body.title, 272 | company: body.company, 273 | jobtitle: body.jobtitle, 274 | primarycontactnumber: body.primarycontactnumber, 275 | othercontactnumbers: body.othercontactnumbers, 276 | primaryemailaddress: body.primaryemailaddress, 277 | emailaddresses: body.emailaddresses, 278 | groups: body.groups 279 | }); 280 | } 281 | 282 | 283 | console.log('Running at port ' + app.get('port')); 284 | http.createServer(app).listen(app.get('port')); -------------------------------------------------------------------------------- /Chapter 6/OLD/source/http-passport-basic-auth-full-fledged-service.txt: -------------------------------------------------------------------------------- 1 | var express = require('express') 2 | , http = require('http') 3 | , path = require('path') 4 | , bodyParser = require('body-parser') 5 | , logger = require('morgan') 6 | , methodOverride = require('method-override') 7 | , errorHandler = require('errorhandler') 8 | , mongoose = require('mongoose') 9 | , _v1 = require('./modules/contactdataservice_v1') 10 | , _v2 = require('./modules/contactdataservice_v2') 11 | , admin = require('./modules/admin') 12 | , CacheControl = require("express-cache-control") 13 | , fs = require('fs') 14 | , Grid = require('gridfs-stream') 15 | , expressPaginate = require('express-paginate') 16 | , mongoosePaginate = require('mongoose-paginate') 17 | 18 | var passport = require('passport') 19 | , BasicStrategy = require('passport-http').BasicStrategy; 20 | 21 | 22 | var app = express(); 23 | var url = require('url'); 24 | var cache = new CacheControl().middleware; 25 | 26 | // all environments 27 | app.set('port', process.env.PORT || 3000); 28 | app.set('views', __dirname + '/views'); 29 | app.set('view engine', 'jade'); 30 | 31 | app.use(methodOverride()); 32 | app.use(expressPaginate.middleware(10,100)); 33 | app.use(bodyParser.json()); 34 | app.use(passport.initialize()); 35 | // development only 36 | if ('development' == app.get('env')) { 37 | app.use(errorHandler()); 38 | } 39 | 40 | 41 | mongoose.connect('mongodb://localhost/contacts'); 42 | var mongodb = mongoose.connection; 43 | 44 | var contactSchema = new mongoose.Schema({ 45 | primarycontactnumber: {type: String, index: {unique: true}}, 46 | firstname: String, 47 | lastname: String, 48 | title: String, 49 | company: String, 50 | jobtitle: String, 51 | othercontactnumbers: [String], 52 | primaryemailaddress: String, 53 | emailaddresses: [String], 54 | groups: [String], 55 | }); 56 | var authUserSchema = new mongoose.Schema({ 57 | username: {type: String, index: {unique: true}}, 58 | password: String, 59 | role: String, 60 | }); 61 | 62 | var AuthUser = mongoose.model('AuthUser', authUserSchema); 63 | 64 | passport.use(new BasicStrategy( 65 | function(username, password, done) { 66 | AuthUser.findOne({username: username, password: password}, 67 | function(error, user) { 68 | if (error) { 69 | console.log(error); 70 | return done(error); 71 | } else { 72 | if (!user) { 73 | console.log('unknown user'); 74 | return done(error); 75 | } else { 76 | console.log(user.username + ' authenticated successfully'); 77 | return done(null, user); 78 | } 79 | } 80 | }); 81 | })); 82 | 83 | 84 | contactSchema.plugin(mongoosePaginate); 85 | 86 | var Contact = mongoose.model('Contact', contactSchema); 87 | 88 | app.get('/v1/contacts/', passport.authenticate('basic', { session: false }), function(request, response) { 89 | var get_params = url.parse(request.url, true).query; 90 | 91 | if (Object.keys(get_params).length == 0) 92 | { 93 | _v1.list(Contact, response); 94 | } 95 | else 96 | { 97 | var key = Object.keys(get_params)[0]; 98 | var value = get_params[key]; 99 | 100 | JSON.stringify(_v2.query_by_arg(Contact, 101 | key, 102 | value, 103 | response)); 104 | } 105 | }); 106 | 107 | app.get('/v1/contacts/:primarycontactnumber', passport.authenticate('basic', { session: false }), function(request, response) { 108 | 109 | console.log(request.url + ' : querying for ' + request.params.number); 110 | _v1.findByNumber(Contact, request.params.primarycontactnumber, response); 111 | }); 112 | 113 | app.post('/v1/contacts/', passport.authenticate('basic', { session: false }), function(request, response) { 114 | _v1.update(Contact, request.body, response) 115 | }); 116 | 117 | app.put('/v1/contacts/', passport.authenticate('basic', { session: false }), function(request, response) { 118 | _v1.create(Contact, request.body, response) 119 | }); 120 | 121 | app.del('/v1/contacts/:primarycontactnumber', passport.authenticate('basic', { session: false }), function(request, response) { 122 | _v1.remove(Contact, request.params.primarycontactnumber, response); 123 | }); 124 | 125 | //version 2 default routing 126 | 127 | app.get('/contacts/:primarycontactnumber', passport.authenticate('basic', { session: false }), function(request, response) { 128 | 129 | console.log(request.url + ' : querying for ' + request.params.number); 130 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 131 | }); 132 | 133 | app.post('/contacts/', passport.authenticate('basic', { session: false }), function(request, response) { 134 | _v2.update(Contact, request.body, response) 135 | }); 136 | 137 | app.put('/contacts/', passport.authenticate('basic', { session: false }), function(request, response) { 138 | _v2.create(Contact, request.body, response) 139 | }); 140 | 141 | app.del('/contacts/:primarycontactnumber', passport.authenticate('basic', { session: false }), function(request, response) { 142 | _v2.remove(Contact, request.params.primarycontactnumber, response); 143 | }); 144 | 145 | app.get('/contacts/:primarycontactnumber/image', passport.authenticate('basic', { session: false }), function(request, response){ 146 | var gfs = Grid(mongodb.db, mongoose.mongo); 147 | _v2.getImage(gfs, request.params.primarycontactnumber, response); 148 | 149 | }) 150 | 151 | app.post('/contacts/:primarycontactnumber/image', passport.authenticate('basic', { session: false }), function(request, response){ 152 | var gfs = Grid(mongodb.db, mongoose.mongo); 153 | _v2.updateImage(gfs, request, response); 154 | }) 155 | 156 | app.del('/contacts/:primarycontactnumber/image', passport.authenticate('basic', { session: false }), function(request, response){ 157 | var gfs = Grid(mongodb.db, mongoose.mongo); 158 | _v2.deleteImage(gfs, mongodb.db, request.params.primarycontactnumber, response); 159 | }) 160 | 161 | app.get('/contacts', cache('minutes',1), passport.authenticate('basic', { session: false }), function(request, response) { 162 | var get_params = url.parse(request.url, true).query; 163 | console.log('redirecting to /v2/contacts'); 164 | response.writeHead(302, {'Location' : '/v2/contacts/'}); 165 | response.end('Version 2 is found at URI /v2/contacts/ '); 166 | }); 167 | 168 | app.get('/contacts/:primarycontactnumber', passport.authenticate('basic', { session: false }), function(request, response) { 169 | 170 | console.log(request.url + ' : querying for ' + request.params.number); 171 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 172 | }); 173 | 174 | 175 | 176 | //version 2 explicit routing 177 | 178 | app.get('/v2/contacts', cache('minutes',1), passport.authenticate('basic', { session: false }), function(request, response) { 179 | 180 | var get_params = url.parse(request.url, true).query; 181 | if (Object.keys(get_params).length == 0) 182 | { 183 | _v2.paginate(Contact, request, response); 184 | } 185 | else 186 | { 187 | if (get_params['limit'] != null || get_params['page'] !=null) 188 | { 189 | _v2.paginate(Contact, request, response); 190 | } 191 | else 192 | { 193 | var key = Object.keys(get_params)[0]; 194 | var value = get_params[key]; 195 | 196 | _v2.query_by_arg(Contact, 197 | key, 198 | value, 199 | response); 200 | } 201 | } 202 | }); 203 | 204 | 205 | app.get('/v2/contacts/:primarycontactnumber/image', function(request, response){ 206 | var gfs = Grid(mongodb.db, mongoose.mongo); 207 | _v2.getImage(gfs, request.params.primarycontactnumber, response); 208 | 209 | }) 210 | 211 | 212 | app.post('/v2/contacts/:primarycontactnumber/image', function(request, response){ 213 | var gfs = Grid(mongodb.db, mongoose.mongo); 214 | _v2.updateImage(gfs, request, response); 215 | }) 216 | 217 | 218 | app.del('/v2/contacts/:primarycontactnumber/image', function(request, response){ 219 | var gfs = Grid(mongodb.db, mongoose.mongo); 220 | _v2.deleteImage(gfs, mongodb.db, request.params.primarycontactnumber, response); 221 | }) 222 | 223 | 224 | app.get('/v2/contacts/:primarycontactnumber', function(request, response) { 225 | 226 | console.log(request.url + ' : querying for ' + request.params.number); 227 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 228 | }); 229 | 230 | app.post('/v2/contacts/', function(request, response) { 231 | _v2.update(Contact, request.body, response) 232 | }); 233 | 234 | app.put('/v2/contacts/', function(request, response) { 235 | _v2.create(Contact, request.body, response) 236 | }); 237 | 238 | app.del('/v2/contacts/:primarycontactnumber', function(request, response) { 239 | _v2.remove(Contact, request.params.primarycontactnumber, response); 240 | }); 241 | 242 | //user administration 243 | 244 | app.post('/admin', passport.authenticate('basic', { session: false }), function(request, response) { 245 | 246 | authorize(request.user, response); 247 | if (!response.closed) { 248 | admin.update(AuthUser, request.body, response); 249 | } 250 | }); 251 | 252 | app.put('/admin', passport.authenticate('basic', { session: false }), function(request, response) { 253 | authorize(request.user, response); 254 | if (!response.closed) { 255 | admin.create(AuthUser, request.body, response); 256 | } 257 | }); 258 | 259 | app.del('/admin/:username', passport.authenticate('basic', { session: false }), function(request, response) { 260 | authorize(request.user, response); 261 | if (!response.closed) { 262 | admin.remove(AuthUser, request.params.username, response); 263 | } 264 | }); 265 | 266 | function authorize(user, response) { 267 | if ((user == null) || (user.role != 'Admin')) { 268 | response.writeHead(403, { 269 | 'Content-Type' : 'text/plain'}); 270 | response.end('Forbidden'); 271 | return; 272 | } 273 | } 274 | 275 | function toContact(body) 276 | { 277 | return new Contact( 278 | { 279 | firstname: body.firstname, 280 | lastname: body.lastname, 281 | title: body.title, 282 | company: body.company, 283 | jobtitle: body.jobtitle, 284 | primarycontactnumber: body.primarycontactnumber, 285 | othercontactnumbers: body.othercontactnumbers, 286 | primaryemailaddress: body.primaryemailaddress, 287 | emailaddresses: body.emailaddresses, 288 | groups: body.groups 289 | }); 290 | } 291 | 292 | console.log('Running at port ' + app.get('port')); 293 | http.createServer(app).listen(app.get('port')); -------------------------------------------------------------------------------- /Chapter 6/OLD/source/https-full-fledged-service.txt: -------------------------------------------------------------------------------- 1 | var express = require('express') 2 | , https = require('https') 3 | , path = require('path') 4 | , bodyParser = require('body-parser') 5 | , logger = require('morgan') 6 | , methodOverride = require('method-override') 7 | , errorHandler = require('errorhandler') 8 | , mongoose = require('mongoose') 9 | , _v1 = require('./modules/contactdataservice_v1') 10 | , _v2 = require('./modules/contactdataservice_v2') 11 | , CacheControl = require("express-cache-control") 12 | , fs = require('fs') 13 | , Grid = require('gridfs-stream') 14 | , expressPaginate = require('express-paginate') 15 | , mongoosePaginate = require('mongoose-paginate') 16 | , fs = require('fs') 17 | , auth = require('basic-auth'); 18 | var https = require('https'); 19 | 20 | var app = express(); 21 | var url = require('url'); 22 | var cache = new CacheControl().middleware; 23 | 24 | // all environments 25 | app.set('port', process.env.PORT || 3443); 26 | app.set('views', __dirname + '/views'); 27 | app.set('view engine', 'jade'); 28 | 29 | app.use(methodOverride()); 30 | app.use(expressPaginate.middleware(10,100)); 31 | app.use(bodyParser.json); 32 | 33 | // development only 34 | if ('development' == app.get('env')) { 35 | app.use(errorHandler()); 36 | } 37 | 38 | 39 | mongoose.connect('mongodb://localhost/contacts'); 40 | var mongodb = mongoose.connection; 41 | 42 | var contactSchema = new mongoose.Schema({ 43 | primarycontactnumber: {type: String, index: {unique: true}}, 44 | firstname: String, 45 | lastname: String, 46 | title: String, 47 | company: String, 48 | jobtitle: String, 49 | othercontactnumbers: [String], 50 | primaryemailaddress: String, 51 | emailaddresses: [String], 52 | groups: [String], 53 | }); 54 | var authUserSchema = new mongoose.Schema({ 55 | username: {type: String, index: {unique: true}}, 56 | password: String, 57 | role: String, 58 | }); 59 | 60 | var AuthUser = mongoose.model('AuthUser', authUserSchema); 61 | 62 | var adminUser = new AuthUser({ 63 | username: 'admin', 64 | password: 'admin', 65 | role: 'Admin' 66 | }); 67 | 68 | adminUser.save(function(error) { 69 | if (!error) { 70 | adminUser.save(); 71 | console.log('Creating Admin user'); 72 | } else { 73 | console.log('Admin user already exist'); 74 | } 75 | }); 76 | 77 | app.use(function(request, response, next) { 78 | var user = auth(request); 79 | if (user === undefined) { 80 | console.log('User information is not available in the request'); 81 | response.statusCode = 401; 82 | response.setHeader('WWW-Authenticate', 'Basic'); 83 | response.end('Unauthorized'); 84 | } else { 85 | authenticate(user, response, next); 86 | console.log(request); 87 | } 88 | }); 89 | 90 | function authenticate(_username, _password, callback) { 91 | var result = false; 92 | AuthUser.findOne({username:_username}, function(error, data) { 93 | if (error) { 94 | console.log(error); 95 | return callback(null, null); 96 | } else { 97 | if (!data) { 98 | console.log('User not found'); 99 | return callback(null, null); 100 | } else { 101 | console.log(data.username + ' authenticated successfully'); 102 | return callback(null, username); 103 | } 104 | } 105 | return callback(null, null); 106 | }); 107 | } 108 | 109 | contactSchema.plugin(mongoosePaginate); 110 | 111 | var Contact = mongoose.model('Contact', contactSchema); 112 | 113 | app.get('/v1/contacts/', function(request, response) { 114 | var get_params = url.parse(request.url, true).query; 115 | 116 | if (Object.keys(get_params).length == 0) 117 | { 118 | _v1.list(Contact, response); 119 | } 120 | else 121 | { 122 | var key = Object.keys(get_params)[0]; 123 | var value = get_params[key]; 124 | 125 | JSON.stringify(_v2.query_by_arg(Contact, 126 | key, 127 | value, 128 | response)); 129 | } 130 | }); 131 | 132 | app.get('/v1/contacts/:primarycontactnumber', function(request, response) { 133 | 134 | console.log(request.url + ' : querying for ' + request.params.number); 135 | _v1.findByNumber(Contact, request.params.primarycontactnumber, response); 136 | }); 137 | 138 | app.post('/v1/contacts/', function(request, response) { 139 | _v1.update(Contact, request.body, response) 140 | }); 141 | 142 | app.put('/v1/contacts/', function(request, response) { 143 | _v1.create(Contact, request.body, response) 144 | }); 145 | 146 | app.del('/v1/contacts/:primarycontactnumber', function(request, response) { 147 | _v1.remove(Contact, request.params.primarycontactnumber, response); 148 | }); 149 | 150 | //version 2 default routing 151 | 152 | app.get('/contacts/:primarycontactnumber', function(request, response) { 153 | 154 | console.log(request.url + ' : querying for ' + request.params.number); 155 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 156 | }); 157 | 158 | app.post('/contacts/', function(request, response) { 159 | _v2.update(Contact, request.body, response) 160 | }); 161 | 162 | app.put('/contacts/', function(request, response) { 163 | _v2.create(Contact, request.body, response) 164 | }); 165 | 166 | app.del('/contacts/:primarycontactnumber', function(request, response) { 167 | _v2.remove(Contact, request.params.primarycontactnumber, response); 168 | }); 169 | 170 | app.get('/contacts/:primarycontactnumber/image', function(request, response){ 171 | var gfs = Grid(mongodb.db, mongoose.mongo); 172 | _v2.getImage(gfs, request.params.primarycontactnumber, response); 173 | 174 | }) 175 | 176 | app.post('/contacts/:primarycontactnumber/image', function(request, response){ 177 | var gfs = Grid(mongodb.db, mongoose.mongo); 178 | _v2.updateImage(gfs, request, response); 179 | }) 180 | 181 | app.del('/contacts/:primarycontactnumber/image', function(request, response){ 182 | var gfs = Grid(mongodb.db, mongoose.mongo); 183 | _v2.deleteImage(gfs, mongodb.db, request.params.primarycontactnumber, response); 184 | }) 185 | 186 | app.get('/contacts', cache('minutes',1), function(request, response) { 187 | var get_params = url.parse(request.url, true).query; 188 | console.log('redirecting to /v2/contacts'); 189 | response.writeHead(302, {'Location' : '/v2/contacts/'}); 190 | response.end('Version 2 is found at URI /v2/contacts/ '); 191 | 192 | }); 193 | 194 | 195 | app.get('/contacts/:primarycontactnumber', function(request, response) { 196 | 197 | console.log(request.url + ' : querying for ' + request.params.number); 198 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 199 | }); 200 | 201 | 202 | 203 | //version 2 explicit routing 204 | 205 | app.get('/v2/contacts', cache('minutes',1), function(request, response) { 206 | var get_params = url.parse(request.url, true).query; 207 | 208 | if (Object.keys(get_params).length == 0) 209 | { 210 | _v2.paginate(Contact, request, response); 211 | } 212 | else 213 | { 214 | if (get_params['limit'] != null || get_params['page'] !=null) 215 | { 216 | _v2.paginate(Contact, request, response); 217 | } 218 | else 219 | { 220 | var key = Object.keys(get_params)[0]; 221 | var value = get_params[key]; 222 | 223 | _v2.query_by_arg(Contact, 224 | key, 225 | value, 226 | response); 227 | } 228 | } 229 | }); 230 | 231 | 232 | app.get('/v2/contacts/:primarycontactnumber/image', function(request, response){ 233 | var gfs = Grid(mongodb.db, mongoose.mongo); 234 | _v2.getImage(gfs, request.params.primarycontactnumber, response); 235 | 236 | }) 237 | 238 | 239 | app.post('/v2/contacts/:primarycontactnumber/image', function(request, response){ 240 | var gfs = Grid(mongodb.db, mongoose.mongo); 241 | _v2.updateImage(gfs, request, response); 242 | }) 243 | 244 | 245 | app.del('/v2/contacts/:primarycontactnumber/image', function(request, response){ 246 | var gfs = Grid(mongodb.db, mongoose.mongo); 247 | _v2.deleteImage(gfs, mongodb.db, request.params.primarycontactnumber, response); 248 | }) 249 | 250 | 251 | app.get('/v2/contacts/:primarycontactnumber', function(request, response) { 252 | 253 | console.log(request.url + ' : querying for ' + request.params.number); 254 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 255 | }); 256 | 257 | app.post('/v2/contacts/', function(request, response) { 258 | _v2.update(Contact, request.body, response) 259 | }); 260 | 261 | app.put('/v2/contacts/', function(request, response) { 262 | _v2.create(Contact, request.body, response) 263 | }); 264 | 265 | app.del('/v2/contacts/:primarycontactnumber', function(request, response) { 266 | _v2.remove(Contact, request.params.primarycontactnumber, response); 267 | }); 268 | 269 | 270 | function toContact(body) 271 | { 272 | return new Contact( 273 | { 274 | firstname: body.firstname, 275 | lastname: body.lastname, 276 | title: body.title, 277 | company: body.company, 278 | jobtitle: body.jobtitle, 279 | primarycontactnumber: body.primarycontactnumber, 280 | othercontactnumbers: body.othercontactnumbers, 281 | primaryemailaddress: body.primaryemailaddress, 282 | emailaddresses: body.emailaddresses, 283 | groups: body.groups 284 | }); 285 | } 286 | 287 | 288 | var options = {key : fs.readFileSync('./ssl/contacts.pem'), 289 | cert : fs.readFileSync('./ssl/contacts.crt')}; 290 | 291 | console.log('Running at port ' + app.get('port')); 292 | https.createServer(options, app).listen(app.get('port')); -------------------------------------------------------------------------------- /Chapter 6/OLD/source/modules/admin.txt: -------------------------------------------------------------------------------- 1 | exports.remove = function (model, _username, response) { 2 | console.log('Deleting user: '+ _username); 3 | 4 | model.findOne({username: _username}, function(error, data) { 5 | if (error) { 6 | console.log(error); 7 | if (response != null) { 8 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 9 | response.end('Internal server error'); 10 | } 11 | return; 12 | } else { 13 | if (!data) { 14 | console.log('User' + _username + ' not found'); 15 | if (response != null) 16 | { 17 | response.writeHead(404, {'Content-Type' : 'text/plain'}); 18 | response.end('Not Found'); 19 | } 20 | return; 21 | } else { 22 | data.remove(function(error){ 23 | if (!error) { 24 | data.remove(); 25 | } 26 | else { 27 | console.log(error); 28 | } 29 | }); 30 | 31 | if (response != null){ 32 | response.send('Deleted'); 33 | } 34 | return; 35 | } 36 | } 37 | }); 38 | } 39 | 40 | exports.update = function (model, requestBody, response) { 41 | 42 | var _username = requestBody.username; 43 | console.log (requestBody); 44 | model.findOne({username: _username}, function(error, data) { 45 | if (error) { 46 | console.log(error); 47 | if (response != null) { 48 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 49 | response.end('Internal server error'); 50 | } 51 | return; 52 | } else { 53 | var user = toAuthUser(requestBody, model); 54 | if (!data) { 55 | console.log('User: ' + _username + 56 | ' does not exist. It will be created.'); 57 | 58 | user.save(function(error) { 59 | if (!error) 60 | user.save(); 61 | }); 62 | 63 | if (response != null) { 64 | response.writeHead(201, {'Content-Type' : 'text/plain'}); 65 | response.end('Created'); 66 | } 67 | return; 68 | } 69 | 70 | data.username = user.username; 71 | data.password = user.password; 72 | data.role = user.role; 73 | 74 | data.save(function (error) { 75 | if (!error) { 76 | console.log('Successfully updated user: '+ _username); 77 | data.save(); 78 | } else { 79 | console.log('Error on save operation'); 80 | } 81 | }); 82 | if (response != null) { 83 | response.send('Updated'); 84 | } 85 | } 86 | }); 87 | }; 88 | 89 | exports.create = function (model, requestBody, response) { 90 | var user = toAuthUser(requestBody, model); 91 | var _username = requestBody.username; 92 | user.save(function(error) { 93 | 94 | if (!error) { 95 | user.save(); 96 | } else { 97 | console.log('Checking if user saving failed due to already existing user:' + _username); 98 | model.findOne({username: _username}, function(error, data) { 99 | if (error) { 100 | console.log(error); 101 | if (response != null) { 102 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 103 | response.end('Internal server error'); 104 | } 105 | return; 106 | } else { 107 | var user = toAuthUser(requestBody, model); 108 | if (!data) { 109 | console.log('The user does not exist. It will be created'); 110 | user.save(function(error) { 111 | if (!error) { 112 | user.save(); 113 | } else { 114 | console.log(error); 115 | } 116 | }); 117 | 118 | if (response != null) { 119 | response.writeHead(201, {'Content-Type' : 'text/plain'}); 120 | response.end('Created'); 121 | } 122 | return; 123 | } else { 124 | console.log('Updating user:' + _username); 125 | 126 | data.username = user.username; 127 | data.password = user.password; 128 | data.role = user.role; 129 | 130 | data.save(function (error) { 131 | if (!error) { 132 | data.save(); 133 | response.end('Updated'); 134 | console.log('Successfully updated user: ' + _username); 135 | } else { 136 | console.log('Error while saving user:' + _username); 137 | console.log(error); 138 | } 139 | }); 140 | } 141 | } 142 | }); 143 | } 144 | }); 145 | }; 146 | 147 | 148 | function toAuthUser(body, AuthUser) { 149 | 150 | return new AuthUser( 151 | { 152 | username: body.username, 153 | password: body.password, 154 | role: body.role 155 | }); 156 | } -------------------------------------------------------------------------------- /Chapter 6/OLD/source/modules/contactdataservice_v1.txt: -------------------------------------------------------------------------------- 1 | exports.remove = function (model, _primarycontactnumber, response) { 2 | console.log('Deleting contact with primary number: ' 3 | + _primarycontactnumber); 4 | model.findOne({primarycontactnumber: _primarycontactnumber}, function(error, data) { 5 | if (error) { 6 | console.log(error); 7 | if (response != null) { 8 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 9 | response.end('Internal server error'); 10 | } 11 | return; 12 | } else { 13 | if (!data) { 14 | console.log('not found'); 15 | if (response != null) 16 | { 17 | response.writeHead(404, {'Content-Type' : 'text/plain'}); 18 | response.end('Not Found'); 19 | } 20 | return; 21 | } else { 22 | data.remove(function(error){ 23 | if (!error) { 24 | data.remove(); 25 | 26 | } 27 | else { 28 | console.log(error); 29 | } 30 | }); 31 | 32 | if (response != null){ 33 | response.send('Deleted'); 34 | } 35 | return; 36 | } 37 | } 38 | }); 39 | } 40 | 41 | exports.update = function (model, requestBody, response) { 42 | 43 | var primarynumber = requestBody.primarycontactnumber; 44 | model.findOne({primarycontactnumber: primarynumber}, function(error, data) { 45 | if (error) { 46 | console.log(error); 47 | if (response != null) { 48 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 49 | response.end('Internal server error'); 50 | } 51 | return; 52 | } else { 53 | var contact = toContact(requestBody, model); 54 | if (!data) { 55 | console.log('Contact with primary number: ' + primarynumber + 56 | ' does not exist. The contact will be created.'); 57 | 58 | contact.save(function(error) { 59 | if (!error) 60 | contact.save(); 61 | }); 62 | 63 | if (response != null) { 64 | response.end('Created'); 65 | } 66 | return; 67 | } 68 | //poulate the document with the updated values 69 | 70 | data.firstname = contact.firstname; 71 | data.lastname = contact.lastname; 72 | data.title = contact.title; 73 | data.company = contact.company; 74 | data.jobtitle = contact.jobtitle; 75 | data.primarycontactnumber = contact.primarycontactnumber; 76 | data.othercontactnumbers = contact.othercontactnumbers; 77 | data.emailaddresses = contact.emailaddresses; 78 | data.primaryemailaddress = contact.primaryemailaddress; 79 | data.groups = contact.groups; 80 | 81 | data.save(function (error) { 82 | if (!error) { 83 | console.log('Successfully updated contact with primary number: '+ primarynumber); 84 | data.save(); 85 | } else { 86 | console.log('error on save'); 87 | } 88 | }); 89 | if (response != null) { 90 | response.send('Updated'); 91 | } 92 | } 93 | }); 94 | }; 95 | 96 | exports.create = function (model, requestBody, response) { 97 | var contact = toContact(requestBody, model); 98 | var primarynumber = requestBody.primarycontactnumber; 99 | contact.save(function(error) { 100 | 101 | if (!error) { 102 | contact.save(); 103 | } else { 104 | console.log('Checking if contact saving failed due to already existing primary number:' + primarynumber); 105 | model.findOne({primarycontactnumber: primarynumber}, function(error, data) { 106 | if (error) { 107 | console.log(error); 108 | if (response != null) { 109 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 110 | response.end('Internal server error'); 111 | } 112 | return; 113 | } else { 114 | var contact = toContact(requestBody, model); 115 | if (!data) { 116 | console.log('The contact does not exist. It will be created'); 117 | contact.save(function(error) { 118 | if (!error) { 119 | contact.save(); 120 | } else { 121 | console.log(error); 122 | } 123 | }); 124 | 125 | if (response != null) { 126 | response.writeHead(201, {'Content-Type' : 'text/plain'}); 127 | response.end('Created'); 128 | } 129 | return; 130 | } else { 131 | console.log('Updating contact with primary contact number:' + primarynumber); 132 | data.firstname = contact.firstname; 133 | data.lastname = contact.lastname; 134 | data.title = contact.title; 135 | data.company = contact.company; 136 | data.jobtitle = contact.jobtitle; 137 | data.primarycontactnumber = contact.primarycontactnumber; 138 | data.othercontactnumbers = contact.othercontactnumbers; 139 | data.emailaddresses = contact.emailaddresses; 140 | data.primaryemailaddress = contact.primaryemailaddress; 141 | data.groups = contact.groups; 142 | 143 | data.save(function (error) { 144 | if (!error) { 145 | data.save(); 146 | response.end('Updated'); 147 | console.log('Successfully Updated contat with primary contact number: ' + primarynumber); 148 | } else { 149 | console.log('Error while saving contact with primary contact number:' + primarynumber); 150 | console.log(error); 151 | } 152 | }); 153 | } 154 | } 155 | }); 156 | } 157 | }); 158 | }; 159 | 160 | exports.findByNumber = function (model, _primarycontactnumber, response) { 161 | 162 | model.findOne({primarycontactnumber: _primarycontactnumber}, function(error, result) { 163 | if (error) { 164 | console.error(error); 165 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 166 | response.end('Internal server error'); 167 | return; 168 | } else { 169 | if (!result) { 170 | if (response != null) { 171 | response.writeHead(404, {'Content-Type' : 'text/plain'}); 172 | response.end('Not Found'); 173 | } 174 | return; 175 | } 176 | 177 | if (response != null){ 178 | response.setHeader('Content-Type', 'application/json'); 179 | response.send(result); 180 | } 181 | } 182 | }); 183 | }; 184 | 185 | exports.list = function (model, response) { 186 | model.find({}, function(error, result) { 187 | if (error) { 188 | console.error(error); 189 | return null; 190 | } 191 | if (response != null) { 192 | response.setHeader('content-type', 'application/json'); 193 | response.end(JSON.stringify(result)); 194 | } 195 | return JSON.stringify(result); 196 | }); 197 | } 198 | 199 | function toContact(body, Contact) { 200 | 201 | return new Contact( 202 | { 203 | firstname: body.firstname, 204 | lastname: body.lastname, 205 | title: body.title, 206 | company: body.company, 207 | jobtitle: body.jobtitle, 208 | primarycontactnumber: body.primarycontactnumber, 209 | primaryemailaddress: body.primaryemailaddress, 210 | emailaddresses: body.emailaddresses, 211 | groups: body.groups, 212 | othercontactnumbers: body.othercontactnumbers 213 | }); 214 | } -------------------------------------------------------------------------------- /Chapter 6/OLD/source/modules/contactdataservice_v2.txt: -------------------------------------------------------------------------------- 1 | exports.remove = function (model, _primarycontactnumber, response) { 2 | console.log('Deleting contact with primary number: ' 3 | + _primarycontactnumber); 4 | model.findOne({primarycontactnumber: _primarycontactnumber}, function(error, data) { 5 | if (error) { 6 | console.log(error); 7 | if (response != null) { 8 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 9 | response.end('Internal server error'); 10 | } 11 | return; 12 | } else { 13 | if (!data) { 14 | console.log('not found'); 15 | if (response != null) 16 | { 17 | response.writeHead(404, {'Content-Type' : 'text/plain'}); 18 | response.end('Not Found'); 19 | } 20 | return; 21 | } else { 22 | data.remove(function(error){ 23 | if (!error) { 24 | data.remove(); 25 | 26 | } 27 | else { 28 | console.log(error); 29 | } 30 | }); 31 | 32 | if (response != null){ 33 | response.send('Deleted'); 34 | } 35 | return; 36 | } 37 | } 38 | }); 39 | } 40 | 41 | exports.update = function (model, requestBody, response) { 42 | 43 | var primarynumber = requestBody.primarycontactnumber; 44 | model.findOne({primarycontactnumber: primarynumber}, function(error, data) { 45 | if (error) { 46 | console.log(error); 47 | if (response != null) { 48 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 49 | response.end('Internal server error'); 50 | } 51 | return; 52 | } else { 53 | var contact = toContact(requestBody, model); 54 | if (!data) { 55 | console.log('Contact with primary number: ' + primarynumber + 56 | ' does not exist. The contact will be created.'); 57 | 58 | contact.save(function(error) { 59 | if (!error) 60 | contact.save(); 61 | }); 62 | 63 | if (response != null) { 64 | response.writeHead(201, {'Content-Type' : 'text/plain'}); 65 | response.end('Created'); 66 | } 67 | return; 68 | } 69 | //poulate the document with the updated values 70 | 71 | data.firstname = contact.firstname; 72 | data.lastname = contact.lastname; 73 | data.title = contact.title; 74 | data.company = contact.company; 75 | data.jobtitle = contact.jobtitle; 76 | data.primarycontactnumber = contact.primarycontactnumber; 77 | data.othercontactnumbers = contact.othercontactnumbers; 78 | data.emailaddresses = contact.emailaddresses; 79 | data.primaryemailaddress = contact.primaryemailaddress; 80 | data.groups = contact.groups; 81 | 82 | data.save(function (error) { 83 | if (!error) { 84 | console.log('Successfully updated contact with primary number: '+ primarynumber); 85 | data.save(); 86 | } else { 87 | console.log('error on save'); 88 | } 89 | }); 90 | if (response != null) { 91 | response.status(200).send('Updated'); 92 | } 93 | } 94 | }); 95 | }; 96 | 97 | exports.create = function (model, requestBody, response) { 98 | var contact = toContact(requestBody, model); 99 | var primarynumber = requestBody.primarycontactnumber; 100 | contact.save(function(error) { 101 | 102 | if (!error) { 103 | contact.save(); 104 | } else { 105 | console.log('Checking if contact saving failed due to already existing primary number:' + primarynumber); 106 | model.findOne({primarycontactnumber: primarynumber}, function(error, data) { 107 | if (error) { 108 | console.log(error); 109 | if (response != null) { 110 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 111 | response.end('Internal server error'); 112 | } 113 | return; 114 | } else { 115 | var contact = toContact(requestBody, model); 116 | if (!data) { 117 | console.log('The contact does not exist. It will be created'); 118 | contact.save(function(error) { 119 | if (!error) { 120 | contact.save(); 121 | } else { 122 | console.log(error); 123 | } 124 | }); 125 | 126 | if (response != null) { 127 | response.writeHead(201, {'Content-Type' : 'text/plain'}); 128 | response.end('Created'); 129 | } 130 | return; 131 | } else { 132 | console.log('Updating contact with primary contact number:' + primarynumber); 133 | data.firstname = contact.firstname; 134 | data.lastname = contact.lastname; 135 | data.title = contact.title; 136 | data.company = contact.company; 137 | data.jobtitle = contact.jobtitle; 138 | data.primarycontactnumber = contact.primarycontactnumber; 139 | data.othercontactnumbers = contact.othercontactnumbers; 140 | data.emailaddresses = contact.emailaddresses; 141 | data.primaryemailaddress = contact.primaryemailaddress; 142 | data.groups = contact.groups; 143 | 144 | data.save(function (error) { 145 | if (!error) { 146 | data.save(); 147 | response.end('Updated'); 148 | console.log('Successfully Updated contat with primary contact number: ' + primarynumber); 149 | } else { 150 | console.log('Error while saving contact with primary contact number:' + primarynumber); 151 | console.log(error); 152 | } 153 | }); 154 | } 155 | } 156 | }); 157 | } 158 | }); 159 | }; 160 | 161 | exports.findByNumber = function (model, _primarycontactnumber, response) { 162 | 163 | model.findOne({primarycontactnumber: _primarycontactnumber}, function(error, result) { 164 | if (error) { 165 | console.error(error); 166 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 167 | response.end('Internal server error'); 168 | return; 169 | } else { 170 | if (!result) { 171 | if (response != null) { 172 | response.writeHead(404, {'Content-Type' : 'text/plain'}); 173 | response.end('Not Found'); 174 | } 175 | return; 176 | } 177 | 178 | if (response != null){ 179 | response.setHeader('Content-Type', 'application/json'); 180 | response.send(result); 181 | } 182 | } 183 | }); 184 | }; 185 | 186 | exports.list = function (model, response) { 187 | model.find({}, function(error, result) { 188 | if (error) { 189 | console.error(error); 190 | return null; 191 | } 192 | if (response != null) { 193 | response.json({ 194 | object: 'contacts', 195 | result: result 196 | }); 197 | } 198 | }); 199 | } 200 | 201 | exports.paginate = function (model, request, response) { 202 | 203 | model.paginate({}, 204 | {page: request.query.page, limit: request.query.limit}, 205 | function(error, result) { 206 | if (error) { 207 | console.error(error); 208 | response.writeHead(500, { 209 | 'Content-Type' : 'text/plain'}); 210 | response.end('Internal server error'); 211 | return; 212 | } 213 | 214 | response.json({ 215 | object: 'contacts', 216 | page_count: 20, 217 | result: result 218 | }); 219 | 220 | }); 221 | } 222 | 223 | function toContact(body, Contact) { 224 | 225 | return new Contact( 226 | { 227 | firstname: body.firstname, 228 | lastname: body.lastname, 229 | title: body.title, 230 | company: body.company, 231 | jobtitle: body.jobtitle, 232 | primarycontactnumber: body.primarycontactnumber, 233 | primaryemailaddress: body.primaryemailaddress, 234 | emailaddresses: body.emailaddresses, 235 | groups: body.groups, 236 | othercontactnumbers: body.othercontactnumbers 237 | }); 238 | } 239 | 240 | exports.query_by_arg = function (model, key, value, response) { 241 | //build a JSON string with the attribute and the value 242 | var filterArg = '{\"'+key + '\":' +'\"'+ value + '\"}'; 243 | var filter = JSON.parse(filterArg); 244 | 245 | model.find(filter, function(error, result) { 246 | if (error) { 247 | console.error(error); 248 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 249 | response.end('Internal server error'); 250 | return; 251 | } else { 252 | if (!result) { 253 | if (response != null) { 254 | response.writeHead(404, {'Content-Type' : 'text/plain'}); 255 | response.end('Not Found'); 256 | } 257 | return; 258 | } 259 | 260 | if (response != null){ 261 | response.setHeader('Content-Type', 'application/json'); 262 | response.send(result); 263 | } 264 | } 265 | }); 266 | } 267 | 268 | exports.updateImage = function(gfs, request, response) { 269 | var _primarycontactnumber = request.params.primarycontactnumber; 270 | console.log('Updating image for primary contact number:' + _primarycontactnumber); 271 | request.pipe(gfs.createWriteStream({ 272 | _id : _primarycontactnumber, 273 | filename : 'image', 274 | mode : 'w' 275 | })); 276 | response.send("Successfully uploaded image for primary contact number: " 277 | + _primarycontactnumber); 278 | 279 | }; 280 | 281 | exports.getImage = function(gfs, _primarycontactnumber, response) { 282 | console.log('Requesting image for primary contact number: ' + _primarycontactnumber); 283 | var imageStream = gfs.createReadStream({ 284 | _id : _primarycontactnumber, 285 | filename : 'image', 286 | mode : 'r' 287 | }); 288 | 289 | imageStream.on('error', function(error) { 290 | response.send('404', 'Not found'); 291 | return; 292 | }); 293 | 294 | response.setHeader('Content-Type', 'image/jpeg'); 295 | imageStream.pipe(response); 296 | }; 297 | 298 | exports.deleteImage = function(gfs, mongodb, _primarycontactnumber, response) { 299 | console.log('Deleting image for primary contact number:' + _primarycontactnumber); 300 | var collection = mongodb.collection('fs.files'); 301 | 302 | collection.remove({_id: _primarycontactnumber, 303 | filename: 'image'}, function (error, contact) { 304 | if (error) { 305 | console.log(error); 306 | return; 307 | } 308 | 309 | if (contact === null) { 310 | response.send('404', 'Not found'); 311 | return; 312 | } 313 | else { 314 | console.log('Successfully deleted image for primary contact number: ' + _primarycontactnumber); 315 | } 316 | }); 317 | 318 | response.send('Successfully deleted image for primary contact number: ' + _primarycontactnumber); 319 | } 320 | -------------------------------------------------------------------------------- /Chapter 6/source/http-auth-full-fledged-service.txt: -------------------------------------------------------------------------------- 1 | var express = require('express') 2 | , http = require('http') 3 | , path = require('path') 4 | , bodyParser = require('body-parser') 5 | , logger = require('morgan') 6 | , methodOverride = require('method-override') 7 | , errorHandler = require('errorhandler') 8 | , mongoose = require('mongoose') 9 | , _v1 = require('./modules/contactdataservice_v1') 10 | , _v2 = require('./modules/contactdataservice_v2') 11 | , CacheControl = require("express-cache-control") 12 | , fs = require('fs') 13 | , Grid = require('gridfs-stream') 14 | , expressPaginate = require('express-paginate') 15 | , mongoosePaginate = require('mongoose-paginate') 16 | , auth = require('basic-auth'); 17 | 18 | var app = express(); 19 | var url = require('url'); 20 | var cache = new CacheControl().middleware; 21 | 22 | // all environments 23 | app.set('port', process.env.PORT || 3000); 24 | app.set('views', __dirname + '/views'); 25 | app.set('view engine', 'jade'); 26 | 27 | app.use(methodOverride()); 28 | app.use(expressPaginate.middleware(10,100)); 29 | app.use(bodyParser()); 30 | 31 | // development only 32 | if ('development' == app.get('env')) { 33 | app.use(errorHandler()); 34 | } 35 | 36 | 37 | mongoose.connect('mongodb://localhost/contacts'); 38 | var mongodb = mongoose.connection; 39 | 40 | var contactSchema = new mongoose.Schema({ 41 | primarycontactnumber: {type: String, index: {unique: true}}, 42 | firstname: String, 43 | lastname: String, 44 | title: String, 45 | company: String, 46 | jobtitle: String, 47 | othercontactnumbers: [String], 48 | primaryemailaddress: String, 49 | emailaddresses: [String], 50 | groups: [String], 51 | }); 52 | var authUserSchema = new mongoose.Schema({ 53 | username: {type: String, index: {unique: true}}, 54 | password: String, 55 | role: String, 56 | }); 57 | 58 | var AuthUser = mongoose.model('AuthUser', authUserSchema); 59 | 60 | var adminUser = new AuthUser({ 61 | username: 'admin', 62 | password: 'admin', 63 | role: 'Admin' 64 | }); 65 | 66 | adminUser.save(function(error) { 67 | if (!error) { 68 | adminUser.save(); 69 | console.log('Creating Admin user'); 70 | } else { 71 | console.log('Admin user already exist'); 72 | } 73 | }); 74 | 75 | app.use(function(request, response, next) { 76 | var user = auth(request); 77 | if (user === undefined) { 78 | console.log('User information is not available in the request'); 79 | response.statusCode = 401; 80 | response.setHeader('WWW-Authenticate', 'Basic'); 81 | response.end('Unauthorized'); 82 | } else { 83 | authenticate(user, response, next); 84 | } 85 | }); 86 | 87 | function authenticate(user, response, next) { 88 | var result = false; 89 | AuthUser.findOne({username: user['name'], password: user['pass']}, function(error, data) { 90 | if (error) { 91 | console.log(error); 92 | response.statusCode = 401; 93 | response.end('Unauthorized'); 94 | } else { 95 | 96 | if (!data) { 97 | console.log('unknown user'); 98 | response.statusCode = 401; 99 | response.end('Unauthorized'); 100 | } else { 101 | console.log(data.username + ' authenticated successfully'); 102 | next(); 103 | } 104 | } 105 | }); 106 | } 107 | 108 | contactSchema.plugin(mongoosePaginate); 109 | 110 | var Contact = mongoose.model('Contact', contactSchema); 111 | 112 | app.get('/v1/contacts/', function(request, response) { 113 | var get_params = url.parse(request.url, true).query; 114 | 115 | if (Object.keys(get_params).length == 0) 116 | { 117 | _v1.list(Contact, response); 118 | } 119 | else 120 | { 121 | var key = Object.keys(get_params)[0]; 122 | var value = get_params[key]; 123 | 124 | JSON.stringify(_v2.query_by_arg(Contact, 125 | key, 126 | value, 127 | response)); 128 | } 129 | }); 130 | 131 | app.get('/v1/contacts/:primarycontactnumber', function(request, response) { 132 | 133 | console.log(request.url + ' : querying for ' + request.params.number); 134 | _v1.findByNumber(Contact, request.params.primarycontactnumber, response); 135 | }); 136 | 137 | app.post('/v1/contacts/', function(request, response) { 138 | _v1.update(Contact, request.body, response) 139 | }); 140 | 141 | app.put('/v1/contacts/', function(request, response) { 142 | _v1.create(Contact, request.body, response) 143 | }); 144 | 145 | app.del('/v1/contacts/:primarycontactnumber', function(request, response) { 146 | _v1.remove(Contact, request.params.primarycontactnumber, response); 147 | }); 148 | 149 | //version 2 default routing 150 | 151 | app.get('/contacts/:primarycontactnumber', function(request, response) { 152 | 153 | console.log(request.url + ' : querying for ' + request.params.number); 154 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 155 | }); 156 | 157 | app.post('/contacts/', function(request, response) { 158 | _v2.update(Contact, request.body, response) 159 | }); 160 | 161 | app.put('/contacts/', function(request, response) { 162 | _v2.create(Contact, request.body, response) 163 | }); 164 | 165 | app.del('/contacts/:primarycontactnumber', function(request, response) { 166 | _v2.remove(Contact, request.params.primarycontactnumber, response); 167 | }); 168 | 169 | app.get('/contacts/:primarycontactnumber/image', function(request, response){ 170 | var gfs = Grid(mongodb.db, mongoose.mongo); 171 | _v2.getImage(gfs, request.params.primarycontactnumber, response); 172 | 173 | }) 174 | 175 | app.post('/contacts/:primarycontactnumber/image', function(request, response){ 176 | var gfs = Grid(mongodb.db, mongoose.mongo); 177 | _v2.updateImage(gfs, request, response); 178 | }) 179 | 180 | app.del('/contacts/:primarycontactnumber/image', function(request, response){ 181 | var gfs = Grid(mongodb.db, mongoose.mongo); 182 | _v2.deleteImage(gfs, mongodb.db, request.params.primarycontactnumber, response); 183 | }) 184 | 185 | app.get('/contacts', cache('minutes',1), function(request, response) { 186 | var get_params = url.parse(request.url, true).query; 187 | console.log('redirecting to /v2/contacts'); 188 | response.writeHead(302, {'Location' : '/v2/contacts/'}); 189 | response.end('Version 2 is found at URI /v2/contacts/ '); 190 | 191 | }); 192 | 193 | 194 | app.get('/contacts/:primarycontactnumber', function(request, response) { 195 | 196 | console.log(request.url + ' : querying for ' + request.params.number); 197 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 198 | }); 199 | 200 | 201 | 202 | //version 2 explicit routing 203 | 204 | app.get('/v2/contacts', cache('minutes',1), function(request, response) { 205 | var get_params = url.parse(request.url, true).query; 206 | 207 | if (Object.keys(get_params).length == 0) 208 | { 209 | _v2.paginate(Contact, request, response); 210 | } 211 | else 212 | { 213 | if (get_params['limit'] != null || get_params['page'] !=null) 214 | { 215 | _v2.paginate(Contact, request, response); 216 | } 217 | else 218 | { 219 | var key = Object.keys(get_params)[0]; 220 | var value = get_params[key]; 221 | 222 | _v2.query_by_arg(Contact, 223 | key, 224 | value, 225 | response); 226 | } 227 | } 228 | }); 229 | 230 | 231 | app.get('/v2/contacts/:primarycontactnumber/image', function(request, response){ 232 | var gfs = Grid(mongodb.db, mongoose.mongo); 233 | _v2.getImage(gfs, request.params.primarycontactnumber, response); 234 | 235 | }) 236 | 237 | 238 | app.post('/v2/contacts/:primarycontactnumber/image', function(request, response){ 239 | var gfs = Grid(mongodb.db, mongoose.mongo); 240 | _v2.updateImage(gfs, request, response); 241 | }) 242 | 243 | 244 | app.del('/v2/contacts/:primarycontactnumber/image', function(request, response){ 245 | var gfs = Grid(mongodb.db, mongoose.mongo); 246 | _v2.deleteImage(gfs, mongodb.db, request.params.primarycontactnumber, response); 247 | }) 248 | 249 | 250 | app.get('/v2/contacts/:primarycontactnumber', function(request, response) { 251 | 252 | console.log(request.url + ' : querying for ' + request.params.number); 253 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 254 | }); 255 | 256 | app.post('/v2/contacts/', function(request, response) { 257 | _v2.update(Contact, request.body, response) 258 | }); 259 | 260 | app.put('/v2/contacts/', function(request, response) { 261 | _v2.create(Contact, request.body, response) 262 | }); 263 | 264 | app.del('/v2/contacts/:primarycontactnumber', function(request, response) { 265 | _v2.remove(Contact, request.params.primarycontactnumber, response); 266 | }); 267 | 268 | function toContact(body) 269 | { 270 | return new Contact( 271 | { 272 | firstname: body.firstname, 273 | lastname: body.lastname, 274 | title: body.title, 275 | company: body.company, 276 | jobtitle: body.jobtitle, 277 | primarycontactnumber: body.primarycontactnumber, 278 | othercontactnumbers: body.othercontactnumbers, 279 | primaryemailaddress: body.primaryemailaddress, 280 | emailaddresses: body.emailaddresses, 281 | groups: body.groups 282 | }); 283 | } 284 | 285 | console.log('Running at port ' + app.get('port')); 286 | http.createServer(app).listen(app.get('port')); -------------------------------------------------------------------------------- /Chapter 6/source/http-basic-auth-full-fledged-service.txt: -------------------------------------------------------------------------------- 1 | var express = require('express') 2 | , http = require('http') 3 | , path = require('path') 4 | , bodyParser = require('body-parser') 5 | , logger = require('morgan') 6 | , methodOverride = require('method-override') 7 | , errorHandler = require('errorhandler') 8 | , mongoose = require('mongoose') 9 | , _v1 = require('./modules/contactdataservice_v1') 10 | , _v2 = require('./modules/contactdataservice_v2') 11 | , CacheControl = require("express-cache-control") 12 | , fs = require('fs') 13 | , Grid = require('gridfs-stream') 14 | , expressPaginate = require('express-paginate') 15 | , mongoosePaginate = require('mongoose-paginate') 16 | , auth = require('basic-auth'); 17 | 18 | var app = express(); 19 | var url = require('url'); 20 | var cache = new CacheControl().middleware; 21 | 22 | // all environments 23 | app.set('port', process.env.PORT || 3000); 24 | app.set('views', __dirname + '/views'); 25 | app.set('view engine', 'jade'); 26 | 27 | app.use(methodOverride()); 28 | app.use(expressPaginate.middleware(10,100)); 29 | app.use(bodyParser.json()); 30 | 31 | // development only 32 | if ('development' == app.get('env')) { 33 | app.use(errorHandler()); 34 | } 35 | 36 | 37 | mongoose.connect('mongodb://localhost/contacts'); 38 | var mongodb = mongoose.connection; 39 | 40 | var contactSchema = new mongoose.Schema({ 41 | primarycontactnumber: {type: String, index: {unique: true}}, 42 | firstname: String, 43 | lastname: String, 44 | title: String, 45 | company: String, 46 | jobtitle: String, 47 | othercontactnumbers: [String], 48 | primaryemailaddress: String, 49 | emailaddresses: [String], 50 | groups: [String], 51 | }); 52 | var authUserSchema = new mongoose.Schema({ 53 | username: {type: String, index: {unique: true}}, 54 | password: String, 55 | role: String, 56 | }); 57 | 58 | var AuthUser = mongoose.model('AuthUser', authUserSchema); 59 | 60 | var adminUser = new AuthUser({ 61 | username: 'admin', 62 | password: 'admin', 63 | role: 'Admin' 64 | }); 65 | 66 | adminUser.save(function(error) { 67 | if (!error) { 68 | adminUser.save(); 69 | console.log('Creating Admin user'); 70 | } else { 71 | console.log('Admin user already exist'); 72 | } 73 | }); 74 | 75 | app.use(function(request, response, next) { 76 | var credentials = auth(request); 77 | if (credentials === undefined) { 78 | console.log('User information is not available in the request'); 79 | response.statusCode = 401; 80 | response.setHeader('WWW-Authenticate', 'Basic'); 81 | response.end('Unauthorized'); 82 | } else { 83 | authenticate(credentials.name, credentials.pass, response, next); 84 | } 85 | }); 86 | 87 | function authenticate(_username, _password, response, callback) { 88 | AuthUser.findOne({username:_username, password: _password}, function(error, data) { 89 | if (error) { 90 | console.log(error); 91 | return; 92 | } else { 93 | if (!data) { 94 | console.log('User not found'); 95 | response.statusCode = 401; 96 | response.end(); 97 | return; 98 | } else { 99 | console.log(data.username + ' authenticated successfully'); 100 | return callback(null, data.username); 101 | } 102 | } 103 | }); 104 | } 105 | 106 | contactSchema.plugin(mongoosePaginate); 107 | 108 | var Contact = mongoose.model('Contact', contactSchema); 109 | 110 | app.get('/v1/contacts/', function(request, response) { 111 | var get_params = url.parse(request.url, true).query; 112 | 113 | if (Object.keys(get_params).length == 0) 114 | { 115 | _v1.list(Contact, response); 116 | } 117 | else 118 | { 119 | var key = Object.keys(get_params)[0]; 120 | var value = get_params[key]; 121 | 122 | JSON.stringify(_v2.query_by_arg(Contact, 123 | key, 124 | value, 125 | response)); 126 | } 127 | }); 128 | 129 | app.get('/v1/contacts/:primarycontactnumber', function(request, response) { 130 | 131 | console.log(request.url + ' : querying for ' + request.params.number); 132 | _v1.findByNumber(Contact, request.params.primarycontactnumber, response); 133 | }); 134 | 135 | app.post('/v1/contacts/', function(request, response) { 136 | _v1.update(Contact, request.body, response) 137 | }); 138 | 139 | app.put('/v1/contacts/', function(request, response) { 140 | _v1.create(Contact, request.body, response) 141 | }); 142 | 143 | app.del('/v1/contacts/:primarycontactnumber', function(request, response) { 144 | _v1.remove(Contact, request.params.primarycontactnumber, response); 145 | }); 146 | 147 | //version 2 default routing 148 | 149 | app.get('/contacts/:primarycontactnumber', function(request, response) { 150 | 151 | console.log(request.url + ' : querying for ' + request.params.number); 152 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 153 | }); 154 | 155 | app.post('/contacts/', function(request, response) { 156 | _v2.update(Contact, request.body, response) 157 | }); 158 | 159 | app.put('/contacts/', function(request, response) { 160 | _v2.create(Contact, request.body, response) 161 | }); 162 | 163 | app.del('/contacts/:primarycontactnumber', function(request, response) { 164 | _v2.remove(Contact, request.params.primarycontactnumber, response); 165 | }); 166 | 167 | app.get('/contacts/:primarycontactnumber/image', function(request, response){ 168 | var gfs = Grid(mongodb.db, mongoose.mongo); 169 | _v2.getImage(gfs, request.params.primarycontactnumber, response); 170 | 171 | }) 172 | 173 | app.post('/contacts/:primarycontactnumber/image', function(request, response){ 174 | var gfs = Grid(mongodb.db, mongoose.mongo); 175 | _v2.updateImage(gfs, request, response); 176 | }) 177 | 178 | app.del('/contacts/:primarycontactnumber/image', function(request, response){ 179 | var gfs = Grid(mongodb.db, mongoose.mongo); 180 | _v2.deleteImage(gfs, mongodb.db, request.params.primarycontactnumber, response); 181 | }) 182 | 183 | app.get('/contacts', cache('minutes',1), function(request, response) { 184 | var get_params = url.parse(request.url, true).query; 185 | console.log('redirecting to /v2/contacts'); 186 | response.writeHead(302, {'Location' : '/v2/contacts/'}); 187 | response.end('Version 2 is found at URI /v2/contacts/ '); 188 | 189 | }); 190 | 191 | 192 | app.get('/contacts/:primarycontactnumber', function(request, response) { 193 | 194 | console.log(request.url + ' : querying for ' + request.params.number); 195 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 196 | }); 197 | 198 | 199 | 200 | //version 2 explicit routing 201 | 202 | app.get('/v2/contacts', cache('minutes',1), function(request, response) { 203 | var get_params = url.parse(request.url, true).query; 204 | if (Object.keys(get_params).length == 0) 205 | { 206 | _v2.paginate(Contact, request, response); 207 | } 208 | else 209 | { 210 | if (get_params['limit'] != null || get_params['page'] !=null) 211 | { 212 | _v2.paginate(Contact, request, response); 213 | } 214 | else 215 | { 216 | var key = Object.keys(get_params)[0]; 217 | var value = get_params[key]; 218 | 219 | _v2.query_by_arg(Contact, 220 | key, 221 | value, 222 | response); 223 | } 224 | } 225 | }); 226 | 227 | 228 | app.get('/v2/contacts/:primarycontactnumber/image', function(request, response){ 229 | var gfs = Grid(mongodb.db, mongoose.mongo); 230 | _v2.getImage(gfs, request.params.primarycontactnumber, response); 231 | 232 | }) 233 | 234 | 235 | app.post('/v2/contacts/:primarycontactnumber/image', function(request, response){ 236 | var gfs = Grid(mongodb.db, mongoose.mongo); 237 | _v2.updateImage(gfs, request, response); 238 | }) 239 | 240 | 241 | app.del('/v2/contacts/:primarycontactnumber/image', function(request, response){ 242 | var gfs = Grid(mongodb.db, mongoose.mongo); 243 | _v2.deleteImage(gfs, mongodb.db, request.params.primarycontactnumber, response); 244 | }) 245 | 246 | 247 | app.get('/v2/contacts/:primarycontactnumber', function(request, response) { 248 | 249 | console.log(request.url + ' : querying for ' + request.params.number); 250 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 251 | }); 252 | 253 | app.post('/v2/contacts/', function(request, response) { 254 | _v2.update(Contact, request.body, response) 255 | }); 256 | 257 | app.put('/v2/contacts/', function(request, response) { 258 | _v2.create(Contact, request.body, response) 259 | }); 260 | 261 | app.del('/v2/contacts/:primarycontactnumber', function(request, response) { 262 | _v2.remove(Contact, request.params.primarycontactnumber, response); 263 | }); 264 | 265 | function toContact(body) 266 | { 267 | return new Contact( 268 | { 269 | firstname: body.firstname, 270 | lastname: body.lastname, 271 | title: body.title, 272 | company: body.company, 273 | jobtitle: body.jobtitle, 274 | primarycontactnumber: body.primarycontactnumber, 275 | othercontactnumbers: body.othercontactnumbers, 276 | primaryemailaddress: body.primaryemailaddress, 277 | emailaddresses: body.emailaddresses, 278 | groups: body.groups 279 | }); 280 | } 281 | 282 | 283 | console.log('Running at port ' + app.get('port')); 284 | http.createServer(app).listen(app.get('port')); -------------------------------------------------------------------------------- /Chapter 6/source/http-passport-basic-auth-full-fledged-service.txt: -------------------------------------------------------------------------------- 1 | var express = require('express') 2 | , http = require('http') 3 | , path = require('path') 4 | , bodyParser = require('body-parser') 5 | , logger = require('morgan') 6 | , methodOverride = require('method-override') 7 | , errorHandler = require('errorhandler') 8 | , mongoose = require('mongoose') 9 | , _v1 = require('./modules/contactdataservice_v1') 10 | , _v2 = require('./modules/contactdataservice_v2') 11 | , admin = require('./modules/admin') 12 | , CacheControl = require("express-cache-control") 13 | , fs = require('fs') 14 | , Grid = require('gridfs-stream') 15 | , expressPaginate = require('express-paginate') 16 | , mongoosePaginate = require('mongoose-paginate') 17 | 18 | var passport = require('passport') 19 | , BasicStrategy = require('passport-http').BasicStrategy; 20 | 21 | 22 | var app = express(); 23 | var url = require('url'); 24 | var cache = new CacheControl().middleware; 25 | 26 | // all environments 27 | app.set('port', process.env.PORT || 3000); 28 | app.set('views', __dirname + '/views'); 29 | app.set('view engine', 'jade'); 30 | 31 | app.use(methodOverride()); 32 | app.use(expressPaginate.middleware(10,100)); 33 | app.use(bodyParser.json()); 34 | app.use(passport.initialize()); 35 | // development only 36 | if ('development' == app.get('env')) { 37 | app.use(errorHandler()); 38 | } 39 | 40 | 41 | mongoose.connect('mongodb://localhost/contacts'); 42 | var mongodb = mongoose.connection; 43 | 44 | var contactSchema = new mongoose.Schema({ 45 | primarycontactnumber: {type: String, index: {unique: true}}, 46 | firstname: String, 47 | lastname: String, 48 | title: String, 49 | company: String, 50 | jobtitle: String, 51 | othercontactnumbers: [String], 52 | primaryemailaddress: String, 53 | emailaddresses: [String], 54 | groups: [String], 55 | }); 56 | var authUserSchema = new mongoose.Schema({ 57 | username: {type: String, index: {unique: true}}, 58 | password: String, 59 | role: String, 60 | }); 61 | 62 | var AuthUser = mongoose.model('AuthUser', authUserSchema); 63 | 64 | passport.use(new BasicStrategy( 65 | function(username, password, done) { 66 | AuthUser.findOne({username: username, password: password}, 67 | function(error, user) { 68 | if (error) { 69 | console.log(error); 70 | return done(error); 71 | } else { 72 | if (!user) { 73 | console.log('unknown user'); 74 | return done(error); 75 | } else { 76 | console.log(user.username + ' authenticated successfully'); 77 | return done(null, user); 78 | } 79 | } 80 | }); 81 | })); 82 | 83 | 84 | contactSchema.plugin(mongoosePaginate); 85 | 86 | var Contact = mongoose.model('Contact', contactSchema); 87 | 88 | app.get('/v1/contacts/', passport.authenticate('basic', { session: false }), function(request, response) { 89 | var get_params = url.parse(request.url, true).query; 90 | 91 | if (Object.keys(get_params).length == 0) 92 | { 93 | _v1.list(Contact, response); 94 | } 95 | else 96 | { 97 | var key = Object.keys(get_params)[0]; 98 | var value = get_params[key]; 99 | 100 | JSON.stringify(_v2.query_by_arg(Contact, 101 | key, 102 | value, 103 | response)); 104 | } 105 | }); 106 | 107 | app.get('/v1/contacts/:primarycontactnumber', passport.authenticate('basic', { session: false }), function(request, response) { 108 | 109 | console.log(request.url + ' : querying for ' + request.params.number); 110 | _v1.findByNumber(Contact, request.params.primarycontactnumber, response); 111 | }); 112 | 113 | app.post('/v1/contacts/', passport.authenticate('basic', { session: false }), function(request, response) { 114 | _v1.update(Contact, request.body, response) 115 | }); 116 | 117 | app.put('/v1/contacts/', passport.authenticate('basic', { session: false }), function(request, response) { 118 | _v1.create(Contact, request.body, response) 119 | }); 120 | 121 | app.del('/v1/contacts/:primarycontactnumber', passport.authenticate('basic', { session: false }), function(request, response) { 122 | _v1.remove(Contact, request.params.primarycontactnumber, response); 123 | }); 124 | 125 | //version 2 default routing 126 | 127 | app.get('/contacts/:primarycontactnumber', passport.authenticate('basic', { session: false }), function(request, response) { 128 | 129 | console.log(request.url + ' : querying for ' + request.params.number); 130 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 131 | }); 132 | 133 | app.post('/contacts/', passport.authenticate('basic', { session: false }), function(request, response) { 134 | _v2.update(Contact, request.body, response) 135 | }); 136 | 137 | app.put('/contacts/', passport.authenticate('basic', { session: false }), function(request, response) { 138 | _v2.create(Contact, request.body, response) 139 | }); 140 | 141 | app.del('/contacts/:primarycontactnumber', passport.authenticate('basic', { session: false }), function(request, response) { 142 | _v2.remove(Contact, request.params.primarycontactnumber, response); 143 | }); 144 | 145 | app.get('/contacts/:primarycontactnumber/image', passport.authenticate('basic', { session: false }), function(request, response){ 146 | var gfs = Grid(mongodb.db, mongoose.mongo); 147 | _v2.getImage(gfs, request.params.primarycontactnumber, response); 148 | 149 | }) 150 | 151 | app.post('/contacts/:primarycontactnumber/image', passport.authenticate('basic', { session: false }), function(request, response){ 152 | var gfs = Grid(mongodb.db, mongoose.mongo); 153 | _v2.updateImage(gfs, request, response); 154 | }) 155 | 156 | app.del('/contacts/:primarycontactnumber/image', passport.authenticate('basic', { session: false }), function(request, response){ 157 | var gfs = Grid(mongodb.db, mongoose.mongo); 158 | _v2.deleteImage(gfs, mongodb.db, request.params.primarycontactnumber, response); 159 | }) 160 | 161 | app.get('/contacts', cache('minutes',1), passport.authenticate('basic', { session: false }), function(request, response) { 162 | var get_params = url.parse(request.url, true).query; 163 | console.log('redirecting to /v2/contacts'); 164 | response.writeHead(302, {'Location' : '/v2/contacts/'}); 165 | response.end('Version 2 is found at URI /v2/contacts/ '); 166 | }); 167 | 168 | app.get('/contacts/:primarycontactnumber', passport.authenticate('basic', { session: false }), function(request, response) { 169 | 170 | console.log(request.url + ' : querying for ' + request.params.number); 171 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 172 | }); 173 | 174 | 175 | 176 | //version 2 explicit routing 177 | 178 | app.get('/v2/contacts', cache('minutes',1), passport.authenticate('basic', { session: false }), function(request, response) { 179 | 180 | var get_params = url.parse(request.url, true).query; 181 | if (Object.keys(get_params).length == 0) 182 | { 183 | _v2.paginate(Contact, request, response); 184 | } 185 | else 186 | { 187 | if (get_params['limit'] != null || get_params['page'] !=null) 188 | { 189 | _v2.paginate(Contact, request, response); 190 | } 191 | else 192 | { 193 | var key = Object.keys(get_params)[0]; 194 | var value = get_params[key]; 195 | 196 | _v2.query_by_arg(Contact, 197 | key, 198 | value, 199 | response); 200 | } 201 | } 202 | }); 203 | 204 | 205 | app.get('/v2/contacts/:primarycontactnumber/image', function(request, response){ 206 | var gfs = Grid(mongodb.db, mongoose.mongo); 207 | _v2.getImage(gfs, request.params.primarycontactnumber, response); 208 | 209 | }) 210 | 211 | 212 | app.post('/v2/contacts/:primarycontactnumber/image', function(request, response){ 213 | var gfs = Grid(mongodb.db, mongoose.mongo); 214 | _v2.updateImage(gfs, request, response); 215 | }) 216 | 217 | 218 | app.del('/v2/contacts/:primarycontactnumber/image', function(request, response){ 219 | var gfs = Grid(mongodb.db, mongoose.mongo); 220 | _v2.deleteImage(gfs, mongodb.db, request.params.primarycontactnumber, response); 221 | }) 222 | 223 | 224 | app.get('/v2/contacts/:primarycontactnumber', function(request, response) { 225 | 226 | console.log(request.url + ' : querying for ' + request.params.number); 227 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 228 | }); 229 | 230 | app.post('/v2/contacts/', function(request, response) { 231 | _v2.update(Contact, request.body, response) 232 | }); 233 | 234 | app.put('/v2/contacts/', function(request, response) { 235 | _v2.create(Contact, request.body, response) 236 | }); 237 | 238 | app.del('/v2/contacts/:primarycontactnumber', function(request, response) { 239 | _v2.remove(Contact, request.params.primarycontactnumber, response); 240 | }); 241 | 242 | //user administration 243 | 244 | app.post('/admin', passport.authenticate('basic', { session: false }), function(request, response) { 245 | 246 | authorize(request.user, response); 247 | if (!response.closed) { 248 | admin.update(AuthUser, request.body, response); 249 | } 250 | }); 251 | 252 | app.put('/admin', passport.authenticate('basic', { session: false }), function(request, response) { 253 | authorize(request.user, response); 254 | if (!response.closed) { 255 | admin.create(AuthUser, request.body, response); 256 | } 257 | }); 258 | 259 | app.del('/admin/:username', passport.authenticate('basic', { session: false }), function(request, response) { 260 | authorize(request.user, response); 261 | if (!response.closed) { 262 | admin.remove(AuthUser, request.params.username, response); 263 | } 264 | }); 265 | 266 | function authorize(user, response) { 267 | if ((user == null) || (user.role != 'Admin')) { 268 | response.writeHead(403, { 269 | 'Content-Type' : 'text/plain'}); 270 | response.end('Forbidden'); 271 | return; 272 | } 273 | } 274 | 275 | function toContact(body) 276 | { 277 | return new Contact( 278 | { 279 | firstname: body.firstname, 280 | lastname: body.lastname, 281 | title: body.title, 282 | company: body.company, 283 | jobtitle: body.jobtitle, 284 | primarycontactnumber: body.primarycontactnumber, 285 | othercontactnumbers: body.othercontactnumbers, 286 | primaryemailaddress: body.primaryemailaddress, 287 | emailaddresses: body.emailaddresses, 288 | groups: body.groups 289 | }); 290 | } 291 | 292 | console.log('Running at port ' + app.get('port')); 293 | http.createServer(app).listen(app.get('port')); -------------------------------------------------------------------------------- /Chapter 6/source/https-full-fledged-service.txt: -------------------------------------------------------------------------------- 1 | var express = require('express') 2 | , https = require('https') 3 | , path = require('path') 4 | , bodyParser = require('body-parser') 5 | , logger = require('morgan') 6 | , methodOverride = require('method-override') 7 | , errorHandler = require('errorhandler') 8 | , mongoose = require('mongoose') 9 | , _v1 = require('./modules/contactdataservice_v1') 10 | , _v2 = require('./modules/contactdataservice_v2') 11 | , CacheControl = require("express-cache-control") 12 | , fs = require('fs') 13 | , Grid = require('gridfs-stream') 14 | , expressPaginate = require('express-paginate') 15 | , mongoosePaginate = require('mongoose-paginate') 16 | , fs = require('fs') 17 | , auth = require('basic-auth'); 18 | var https = require('https'); 19 | 20 | var app = express(); 21 | var url = require('url'); 22 | var cache = new CacheControl().middleware; 23 | 24 | // all environments 25 | app.set('port', process.env.PORT || 3443); 26 | app.set('views', __dirname + '/views'); 27 | app.set('view engine', 'jade'); 28 | 29 | app.use(methodOverride()); 30 | app.use(expressPaginate.middleware(10,100)); 31 | app.use(bodyParser.json); 32 | 33 | // development only 34 | if ('development' == app.get('env')) { 35 | app.use(errorHandler()); 36 | } 37 | 38 | 39 | mongoose.connect('mongodb://localhost/contacts'); 40 | var mongodb = mongoose.connection; 41 | 42 | var contactSchema = new mongoose.Schema({ 43 | primarycontactnumber: {type: String, index: {unique: true}}, 44 | firstname: String, 45 | lastname: String, 46 | title: String, 47 | company: String, 48 | jobtitle: String, 49 | othercontactnumbers: [String], 50 | primaryemailaddress: String, 51 | emailaddresses: [String], 52 | groups: [String], 53 | }); 54 | var authUserSchema = new mongoose.Schema({ 55 | username: {type: String, index: {unique: true}}, 56 | password: String, 57 | role: String, 58 | }); 59 | 60 | var AuthUser = mongoose.model('AuthUser', authUserSchema); 61 | 62 | var adminUser = new AuthUser({ 63 | username: 'admin', 64 | password: 'admin', 65 | role: 'Admin' 66 | }); 67 | 68 | adminUser.save(function(error) { 69 | if (!error) { 70 | adminUser.save(); 71 | console.log('Creating Admin user'); 72 | } else { 73 | console.log('Admin user already exist'); 74 | } 75 | }); 76 | 77 | app.use(function(request, response, next) { 78 | var user = auth(request); 79 | if (user === undefined) { 80 | console.log('User information is not available in the request'); 81 | response.statusCode = 401; 82 | response.setHeader('WWW-Authenticate', 'Basic'); 83 | response.end('Unauthorized'); 84 | } else { 85 | authenticate(user, response, next); 86 | console.log(request); 87 | } 88 | }); 89 | 90 | function authenticate(_username, _password, callback) { 91 | var result = false; 92 | AuthUser.findOne({username:_username}, function(error, data) { 93 | if (error) { 94 | console.log(error); 95 | return callback(null, null); 96 | } else { 97 | if (!data) { 98 | console.log('User not found'); 99 | return callback(null, null); 100 | } else { 101 | console.log(data.username + ' authenticated successfully'); 102 | return callback(null, username); 103 | } 104 | } 105 | return callback(null, null); 106 | }); 107 | } 108 | 109 | contactSchema.plugin(mongoosePaginate); 110 | 111 | var Contact = mongoose.model('Contact', contactSchema); 112 | 113 | app.get('/v1/contacts/', function(request, response) { 114 | var get_params = url.parse(request.url, true).query; 115 | 116 | if (Object.keys(get_params).length == 0) 117 | { 118 | _v1.list(Contact, response); 119 | } 120 | else 121 | { 122 | var key = Object.keys(get_params)[0]; 123 | var value = get_params[key]; 124 | 125 | JSON.stringify(_v2.query_by_arg(Contact, 126 | key, 127 | value, 128 | response)); 129 | } 130 | }); 131 | 132 | app.get('/v1/contacts/:primarycontactnumber', function(request, response) { 133 | 134 | console.log(request.url + ' : querying for ' + request.params.number); 135 | _v1.findByNumber(Contact, request.params.primarycontactnumber, response); 136 | }); 137 | 138 | app.post('/v1/contacts/', function(request, response) { 139 | _v1.update(Contact, request.body, response) 140 | }); 141 | 142 | app.put('/v1/contacts/', function(request, response) { 143 | _v1.create(Contact, request.body, response) 144 | }); 145 | 146 | app.del('/v1/contacts/:primarycontactnumber', function(request, response) { 147 | _v1.remove(Contact, request.params.primarycontactnumber, response); 148 | }); 149 | 150 | //version 2 default routing 151 | 152 | app.get('/contacts/:primarycontactnumber', function(request, response) { 153 | 154 | console.log(request.url + ' : querying for ' + request.params.number); 155 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 156 | }); 157 | 158 | app.post('/contacts/', function(request, response) { 159 | _v2.update(Contact, request.body, response) 160 | }); 161 | 162 | app.put('/contacts/', function(request, response) { 163 | _v2.create(Contact, request.body, response) 164 | }); 165 | 166 | app.del('/contacts/:primarycontactnumber', function(request, response) { 167 | _v2.remove(Contact, request.params.primarycontactnumber, response); 168 | }); 169 | 170 | app.get('/contacts/:primarycontactnumber/image', function(request, response){ 171 | var gfs = Grid(mongodb.db, mongoose.mongo); 172 | _v2.getImage(gfs, request.params.primarycontactnumber, response); 173 | 174 | }) 175 | 176 | app.post('/contacts/:primarycontactnumber/image', function(request, response){ 177 | var gfs = Grid(mongodb.db, mongoose.mongo); 178 | _v2.updateImage(gfs, request, response); 179 | }) 180 | 181 | app.del('/contacts/:primarycontactnumber/image', function(request, response){ 182 | var gfs = Grid(mongodb.db, mongoose.mongo); 183 | _v2.deleteImage(gfs, mongodb.db, request.params.primarycontactnumber, response); 184 | }) 185 | 186 | app.get('/contacts', cache('minutes',1), function(request, response) { 187 | var get_params = url.parse(request.url, true).query; 188 | console.log('redirecting to /v2/contacts'); 189 | response.writeHead(302, {'Location' : '/v2/contacts/'}); 190 | response.end('Version 2 is found at URI /v2/contacts/ '); 191 | 192 | }); 193 | 194 | 195 | app.get('/contacts/:primarycontactnumber', function(request, response) { 196 | 197 | console.log(request.url + ' : querying for ' + request.params.number); 198 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 199 | }); 200 | 201 | 202 | 203 | //version 2 explicit routing 204 | 205 | app.get('/v2/contacts', cache('minutes',1), function(request, response) { 206 | var get_params = url.parse(request.url, true).query; 207 | 208 | if (Object.keys(get_params).length == 0) 209 | { 210 | _v2.paginate(Contact, request, response); 211 | } 212 | else 213 | { 214 | if (get_params['limit'] != null || get_params['page'] !=null) 215 | { 216 | _v2.paginate(Contact, request, response); 217 | } 218 | else 219 | { 220 | var key = Object.keys(get_params)[0]; 221 | var value = get_params[key]; 222 | 223 | _v2.query_by_arg(Contact, 224 | key, 225 | value, 226 | response); 227 | } 228 | } 229 | }); 230 | 231 | 232 | app.get('/v2/contacts/:primarycontactnumber/image', function(request, response){ 233 | var gfs = Grid(mongodb.db, mongoose.mongo); 234 | _v2.getImage(gfs, request.params.primarycontactnumber, response); 235 | 236 | }) 237 | 238 | 239 | app.post('/v2/contacts/:primarycontactnumber/image', function(request, response){ 240 | var gfs = Grid(mongodb.db, mongoose.mongo); 241 | _v2.updateImage(gfs, request, response); 242 | }) 243 | 244 | 245 | app.del('/v2/contacts/:primarycontactnumber/image', function(request, response){ 246 | var gfs = Grid(mongodb.db, mongoose.mongo); 247 | _v2.deleteImage(gfs, mongodb.db, request.params.primarycontactnumber, response); 248 | }) 249 | 250 | 251 | app.get('/v2/contacts/:primarycontactnumber', function(request, response) { 252 | 253 | console.log(request.url + ' : querying for ' + request.params.number); 254 | _v2.findByNumber(Contact, request.params.primarycontactnumber, response); 255 | }); 256 | 257 | app.post('/v2/contacts/', function(request, response) { 258 | _v2.update(Contact, request.body, response) 259 | }); 260 | 261 | app.put('/v2/contacts/', function(request, response) { 262 | _v2.create(Contact, request.body, response) 263 | }); 264 | 265 | app.del('/v2/contacts/:primarycontactnumber', function(request, response) { 266 | _v2.remove(Contact, request.params.primarycontactnumber, response); 267 | }); 268 | 269 | 270 | function toContact(body) 271 | { 272 | return new Contact( 273 | { 274 | firstname: body.firstname, 275 | lastname: body.lastname, 276 | title: body.title, 277 | company: body.company, 278 | jobtitle: body.jobtitle, 279 | primarycontactnumber: body.primarycontactnumber, 280 | othercontactnumbers: body.othercontactnumbers, 281 | primaryemailaddress: body.primaryemailaddress, 282 | emailaddresses: body.emailaddresses, 283 | groups: body.groups 284 | }); 285 | } 286 | 287 | 288 | var options = {key : fs.readFileSync('./ssl/contacts.pem'), 289 | cert : fs.readFileSync('./ssl/contacts.crt')}; 290 | 291 | console.log('Running at port ' + app.get('port')); 292 | https.createServer(options, app).listen(app.get('port')); -------------------------------------------------------------------------------- /Chapter 6/source/modules/admin.txt: -------------------------------------------------------------------------------- 1 | exports.remove = function (model, _username, response) { 2 | console.log('Deleting user: '+ _username); 3 | 4 | model.findOne({username: _username}, function(error, data) { 5 | if (error) { 6 | console.log(error); 7 | if (response != null) { 8 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 9 | response.end('Internal server error'); 10 | } 11 | return; 12 | } else { 13 | if (!data) { 14 | console.log('User' + _username + ' not found'); 15 | if (response != null) 16 | { 17 | response.writeHead(404, {'Content-Type' : 'text/plain'}); 18 | response.end('Not Found'); 19 | } 20 | return; 21 | } else { 22 | data.remove(function(error){ 23 | if (!error) { 24 | data.remove(); 25 | } 26 | else { 27 | console.log(error); 28 | } 29 | }); 30 | 31 | if (response != null){ 32 | response.send('Deleted'); 33 | } 34 | return; 35 | } 36 | } 37 | }); 38 | } 39 | 40 | exports.update = function (model, requestBody, response) { 41 | 42 | var _username = requestBody.username; 43 | console.log (requestBody); 44 | model.findOne({username: _username}, function(error, data) { 45 | if (error) { 46 | console.log(error); 47 | if (response != null) { 48 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 49 | response.end('Internal server error'); 50 | } 51 | return; 52 | } else { 53 | var user = toAuthUser(requestBody, model); 54 | if (!data) { 55 | console.log('User: ' + _username + 56 | ' does not exist. It will be created.'); 57 | 58 | user.save(function(error) { 59 | if (!error) 60 | user.save(); 61 | }); 62 | 63 | if (response != null) { 64 | response.writeHead(201, {'Content-Type' : 'text/plain'}); 65 | response.end('Created'); 66 | } 67 | return; 68 | } 69 | 70 | data.username = user.username; 71 | data.password = user.password; 72 | data.role = user.role; 73 | 74 | data.save(function (error) { 75 | if (!error) { 76 | console.log('Successfully updated user: '+ _username); 77 | data.save(); 78 | } else { 79 | console.log('Error on save operation'); 80 | } 81 | }); 82 | if (response != null) { 83 | response.send('Updated'); 84 | } 85 | } 86 | }); 87 | }; 88 | 89 | exports.create = function (model, requestBody, response) { 90 | var user = toAuthUser(requestBody, model); 91 | var _username = requestBody.username; 92 | user.save(function(error) { 93 | 94 | if (!error) { 95 | user.save(); 96 | } else { 97 | console.log('Checking if user saving failed due to already existing user:' + _username); 98 | model.findOne({username: _username}, function(error, data) { 99 | if (error) { 100 | console.log(error); 101 | if (response != null) { 102 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 103 | response.end('Internal server error'); 104 | } 105 | return; 106 | } else { 107 | var user = toAuthUser(requestBody, model); 108 | if (!data) { 109 | console.log('The user does not exist. It will be created'); 110 | user.save(function(error) { 111 | if (!error) { 112 | user.save(); 113 | } else { 114 | console.log(error); 115 | } 116 | }); 117 | 118 | if (response != null) { 119 | response.writeHead(201, {'Content-Type' : 'text/plain'}); 120 | response.end('Created'); 121 | } 122 | return; 123 | } else { 124 | console.log('Updating user:' + _username); 125 | 126 | data.username = user.username; 127 | data.password = user.password; 128 | data.role = user.role; 129 | 130 | data.save(function (error) { 131 | if (!error) { 132 | data.save(); 133 | response.end('Updated'); 134 | console.log('Successfully updated user: ' + _username); 135 | } else { 136 | console.log('Error while saving user:' + _username); 137 | console.log(error); 138 | } 139 | }); 140 | } 141 | } 142 | }); 143 | } 144 | }); 145 | }; 146 | 147 | 148 | function toAuthUser(body, AuthUser) { 149 | 150 | return new AuthUser( 151 | { 152 | username: body.username, 153 | password: body.password, 154 | role: body.role 155 | }); 156 | } -------------------------------------------------------------------------------- /Chapter 6/source/modules/contactdataservice_v1.txt: -------------------------------------------------------------------------------- 1 | exports.remove = function (model, _primarycontactnumber, response) { 2 | console.log('Deleting contact with primary number: ' 3 | + _primarycontactnumber); 4 | model.findOne({primarycontactnumber: _primarycontactnumber}, function(error, data) { 5 | if (error) { 6 | console.log(error); 7 | if (response != null) { 8 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 9 | response.end('Internal server error'); 10 | } 11 | return; 12 | } else { 13 | if (!data) { 14 | console.log('not found'); 15 | if (response != null) 16 | { 17 | response.writeHead(404, {'Content-Type' : 'text/plain'}); 18 | response.end('Not Found'); 19 | } 20 | return; 21 | } else { 22 | data.remove(function(error){ 23 | if (!error) { 24 | data.remove(); 25 | 26 | } 27 | else { 28 | console.log(error); 29 | } 30 | }); 31 | 32 | if (response != null){ 33 | response.send('Deleted'); 34 | } 35 | return; 36 | } 37 | } 38 | }); 39 | } 40 | 41 | exports.update = function (model, requestBody, response) { 42 | 43 | var primarynumber = requestBody.primarycontactnumber; 44 | model.findOne({primarycontactnumber: primarynumber}, function(error, data) { 45 | if (error) { 46 | console.log(error); 47 | if (response != null) { 48 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 49 | response.end('Internal server error'); 50 | } 51 | return; 52 | } else { 53 | var contact = toContact(requestBody, model); 54 | if (!data) { 55 | console.log('Contact with primary number: ' + primarynumber + 56 | ' does not exist. The contact will be created.'); 57 | 58 | contact.save(function(error) { 59 | if (!error) 60 | contact.save(); 61 | }); 62 | 63 | if (response != null) { 64 | response.end('Created'); 65 | } 66 | return; 67 | } 68 | //poulate the document with the updated values 69 | 70 | data.firstname = contact.firstname; 71 | data.lastname = contact.lastname; 72 | data.title = contact.title; 73 | data.company = contact.company; 74 | data.jobtitle = contact.jobtitle; 75 | data.primarycontactnumber = contact.primarycontactnumber; 76 | data.othercontactnumbers = contact.othercontactnumbers; 77 | data.emailaddresses = contact.emailaddresses; 78 | data.primaryemailaddress = contact.primaryemailaddress; 79 | data.groups = contact.groups; 80 | 81 | data.save(function (error) { 82 | if (!error) { 83 | console.log('Successfully updated contact with primary number: '+ primarynumber); 84 | data.save(); 85 | } else { 86 | console.log('error on save'); 87 | } 88 | }); 89 | if (response != null) { 90 | response.send('Updated'); 91 | } 92 | } 93 | }); 94 | }; 95 | 96 | exports.create = function (model, requestBody, response) { 97 | var contact = toContact(requestBody, model); 98 | var primarynumber = requestBody.primarycontactnumber; 99 | contact.save(function(error) { 100 | 101 | if (!error) { 102 | contact.save(); 103 | } else { 104 | console.log('Checking if contact saving failed due to already existing primary number:' + primarynumber); 105 | model.findOne({primarycontactnumber: primarynumber}, function(error, data) { 106 | if (error) { 107 | console.log(error); 108 | if (response != null) { 109 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 110 | response.end('Internal server error'); 111 | } 112 | return; 113 | } else { 114 | var contact = toContact(requestBody, model); 115 | if (!data) { 116 | console.log('The contact does not exist. It will be created'); 117 | contact.save(function(error) { 118 | if (!error) { 119 | contact.save(); 120 | } else { 121 | console.log(error); 122 | } 123 | }); 124 | 125 | if (response != null) { 126 | response.writeHead(201, {'Content-Type' : 'text/plain'}); 127 | response.end('Created'); 128 | } 129 | return; 130 | } else { 131 | console.log('Updating contact with primary contact number:' + primarynumber); 132 | data.firstname = contact.firstname; 133 | data.lastname = contact.lastname; 134 | data.title = contact.title; 135 | data.company = contact.company; 136 | data.jobtitle = contact.jobtitle; 137 | data.primarycontactnumber = contact.primarycontactnumber; 138 | data.othercontactnumbers = contact.othercontactnumbers; 139 | data.emailaddresses = contact.emailaddresses; 140 | data.primaryemailaddress = contact.primaryemailaddress; 141 | data.groups = contact.groups; 142 | 143 | data.save(function (error) { 144 | if (!error) { 145 | data.save(); 146 | response.end('Updated'); 147 | console.log('Successfully Updated contat with primary contact number: ' + primarynumber); 148 | } else { 149 | console.log('Error while saving contact with primary contact number:' + primarynumber); 150 | console.log(error); 151 | } 152 | }); 153 | } 154 | } 155 | }); 156 | } 157 | }); 158 | }; 159 | 160 | exports.findByNumber = function (model, _primarycontactnumber, response) { 161 | 162 | model.findOne({primarycontactnumber: _primarycontactnumber}, function(error, result) { 163 | if (error) { 164 | console.error(error); 165 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 166 | response.end('Internal server error'); 167 | return; 168 | } else { 169 | if (!result) { 170 | if (response != null) { 171 | response.writeHead(404, {'Content-Type' : 'text/plain'}); 172 | response.end('Not Found'); 173 | } 174 | return; 175 | } 176 | 177 | if (response != null){ 178 | response.setHeader('Content-Type', 'application/json'); 179 | response.send(result); 180 | } 181 | } 182 | }); 183 | }; 184 | 185 | exports.list = function (model, response) { 186 | model.find({}, function(error, result) { 187 | if (error) { 188 | console.error(error); 189 | return null; 190 | } 191 | if (response != null) { 192 | response.setHeader('content-type', 'application/json'); 193 | response.end(JSON.stringify(result)); 194 | } 195 | return JSON.stringify(result); 196 | }); 197 | } 198 | 199 | function toContact(body, Contact) { 200 | 201 | return new Contact( 202 | { 203 | firstname: body.firstname, 204 | lastname: body.lastname, 205 | title: body.title, 206 | company: body.company, 207 | jobtitle: body.jobtitle, 208 | primarycontactnumber: body.primarycontactnumber, 209 | primaryemailaddress: body.primaryemailaddress, 210 | emailaddresses: body.emailaddresses, 211 | groups: body.groups, 212 | othercontactnumbers: body.othercontactnumbers 213 | }); 214 | } -------------------------------------------------------------------------------- /Chapter 6/source/modules/contactdataservice_v2.txt: -------------------------------------------------------------------------------- 1 | exports.remove = function (model, _primarycontactnumber, response) { 2 | console.log('Deleting contact with primary number: ' 3 | + _primarycontactnumber); 4 | model.findOne({primarycontactnumber: _primarycontactnumber}, function(error, data) { 5 | if (error) { 6 | console.log(error); 7 | if (response != null) { 8 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 9 | response.end('Internal server error'); 10 | } 11 | return; 12 | } else { 13 | if (!data) { 14 | console.log('not found'); 15 | if (response != null) 16 | { 17 | response.writeHead(404, {'Content-Type' : 'text/plain'}); 18 | response.end('Not Found'); 19 | } 20 | return; 21 | } else { 22 | data.remove(function(error){ 23 | if (!error) { 24 | data.remove(); 25 | 26 | } 27 | else { 28 | console.log(error); 29 | } 30 | }); 31 | 32 | if (response != null){ 33 | response.send('Deleted'); 34 | } 35 | return; 36 | } 37 | } 38 | }); 39 | } 40 | 41 | exports.update = function (model, requestBody, response) { 42 | 43 | var primarynumber = requestBody.primarycontactnumber; 44 | model.findOne({primarycontactnumber: primarynumber}, function(error, data) { 45 | if (error) { 46 | console.log(error); 47 | if (response != null) { 48 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 49 | response.end('Internal server error'); 50 | } 51 | return; 52 | } else { 53 | var contact = toContact(requestBody, model); 54 | if (!data) { 55 | console.log('Contact with primary number: ' + primarynumber + 56 | ' does not exist. The contact will be created.'); 57 | 58 | contact.save(function(error) { 59 | if (!error) 60 | contact.save(); 61 | }); 62 | 63 | if (response != null) { 64 | response.writeHead(201, {'Content-Type' : 'text/plain'}); 65 | response.end('Created'); 66 | } 67 | return; 68 | } 69 | //poulate the document with the updated values 70 | 71 | data.firstname = contact.firstname; 72 | data.lastname = contact.lastname; 73 | data.title = contact.title; 74 | data.company = contact.company; 75 | data.jobtitle = contact.jobtitle; 76 | data.primarycontactnumber = contact.primarycontactnumber; 77 | data.othercontactnumbers = contact.othercontactnumbers; 78 | data.emailaddresses = contact.emailaddresses; 79 | data.primaryemailaddress = contact.primaryemailaddress; 80 | data.groups = contact.groups; 81 | 82 | data.save(function (error) { 83 | if (!error) { 84 | console.log('Successfully updated contact with primary number: '+ primarynumber); 85 | data.save(); 86 | } else { 87 | console.log('error on save'); 88 | } 89 | }); 90 | if (response != null) { 91 | response.status(200).send('Updated'); 92 | } 93 | } 94 | }); 95 | }; 96 | 97 | exports.create = function (model, requestBody, response) { 98 | var contact = toContact(requestBody, model); 99 | var primarynumber = requestBody.primarycontactnumber; 100 | contact.save(function(error) { 101 | 102 | if (!error) { 103 | contact.save(); 104 | } else { 105 | console.log('Checking if contact saving failed due to already existing primary number:' + primarynumber); 106 | model.findOne({primarycontactnumber: primarynumber}, function(error, data) { 107 | if (error) { 108 | console.log(error); 109 | if (response != null) { 110 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 111 | response.end('Internal server error'); 112 | } 113 | return; 114 | } else { 115 | var contact = toContact(requestBody, model); 116 | if (!data) { 117 | console.log('The contact does not exist. It will be created'); 118 | contact.save(function(error) { 119 | if (!error) { 120 | contact.save(); 121 | } else { 122 | console.log(error); 123 | } 124 | }); 125 | 126 | if (response != null) { 127 | response.writeHead(201, {'Content-Type' : 'text/plain'}); 128 | response.end('Created'); 129 | } 130 | return; 131 | } else { 132 | console.log('Updating contact with primary contact number:' + primarynumber); 133 | data.firstname = contact.firstname; 134 | data.lastname = contact.lastname; 135 | data.title = contact.title; 136 | data.company = contact.company; 137 | data.jobtitle = contact.jobtitle; 138 | data.primarycontactnumber = contact.primarycontactnumber; 139 | data.othercontactnumbers = contact.othercontactnumbers; 140 | data.emailaddresses = contact.emailaddresses; 141 | data.primaryemailaddress = contact.primaryemailaddress; 142 | data.groups = contact.groups; 143 | 144 | data.save(function (error) { 145 | if (!error) { 146 | data.save(); 147 | response.end('Updated'); 148 | console.log('Successfully Updated contat with primary contact number: ' + primarynumber); 149 | } else { 150 | console.log('Error while saving contact with primary contact number:' + primarynumber); 151 | console.log(error); 152 | } 153 | }); 154 | } 155 | } 156 | }); 157 | } 158 | }); 159 | }; 160 | 161 | exports.findByNumber = function (model, _primarycontactnumber, response) { 162 | 163 | model.findOne({primarycontactnumber: _primarycontactnumber}, function(error, result) { 164 | if (error) { 165 | console.error(error); 166 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 167 | response.end('Internal server error'); 168 | return; 169 | } else { 170 | if (!result) { 171 | if (response != null) { 172 | response.writeHead(404, {'Content-Type' : 'text/plain'}); 173 | response.end('Not Found'); 174 | } 175 | return; 176 | } 177 | 178 | if (response != null){ 179 | response.setHeader('Content-Type', 'application/json'); 180 | response.send(result); 181 | } 182 | } 183 | }); 184 | }; 185 | 186 | exports.list = function (model, response) { 187 | model.find({}, function(error, result) { 188 | if (error) { 189 | console.error(error); 190 | return null; 191 | } 192 | if (response != null) { 193 | response.json({ 194 | object: 'contacts', 195 | result: result 196 | }); 197 | } 198 | }); 199 | } 200 | 201 | exports.paginate = function (model, request, response) { 202 | 203 | model.paginate({}, 204 | {page: request.query.page, limit: request.query.limit}, 205 | function(error, result) { 206 | if (error) { 207 | console.error(error); 208 | response.writeHead(500, { 209 | 'Content-Type' : 'text/plain'}); 210 | response.end('Internal server error'); 211 | return; 212 | } 213 | 214 | response.json({ 215 | object: 'contacts', 216 | page_count: 20, 217 | result: result 218 | }); 219 | 220 | }); 221 | } 222 | 223 | function toContact(body, Contact) { 224 | 225 | return new Contact( 226 | { 227 | firstname: body.firstname, 228 | lastname: body.lastname, 229 | title: body.title, 230 | company: body.company, 231 | jobtitle: body.jobtitle, 232 | primarycontactnumber: body.primarycontactnumber, 233 | primaryemailaddress: body.primaryemailaddress, 234 | emailaddresses: body.emailaddresses, 235 | groups: body.groups, 236 | othercontactnumbers: body.othercontactnumbers 237 | }); 238 | } 239 | 240 | exports.query_by_arg = function (model, key, value, response) { 241 | //build a JSON string with the attribute and the value 242 | var filterArg = '{\"'+key + '\":' +'\"'+ value + '\"}'; 243 | var filter = JSON.parse(filterArg); 244 | 245 | model.find(filter, function(error, result) { 246 | if (error) { 247 | console.error(error); 248 | response.writeHead(500, {'Content-Type' : 'text/plain'}); 249 | response.end('Internal server error'); 250 | return; 251 | } else { 252 | if (!result) { 253 | if (response != null) { 254 | response.writeHead(404, {'Content-Type' : 'text/plain'}); 255 | response.end('Not Found'); 256 | } 257 | return; 258 | } 259 | 260 | if (response != null){ 261 | response.setHeader('Content-Type', 'application/json'); 262 | response.send(result); 263 | } 264 | } 265 | }); 266 | } 267 | 268 | exports.updateImage = function(gfs, request, response) { 269 | var _primarycontactnumber = request.params.primarycontactnumber; 270 | console.log('Updating image for primary contact number:' + _primarycontactnumber); 271 | request.pipe(gfs.createWriteStream({ 272 | _id : _primarycontactnumber, 273 | filename : 'image', 274 | mode : 'w' 275 | })); 276 | response.send("Successfully uploaded image for primary contact number: " 277 | + _primarycontactnumber); 278 | 279 | }; 280 | 281 | exports.getImage = function(gfs, _primarycontactnumber, response) { 282 | console.log('Requesting image for primary contact number: ' + _primarycontactnumber); 283 | var imageStream = gfs.createReadStream({ 284 | _id : _primarycontactnumber, 285 | filename : 'image', 286 | mode : 'r' 287 | }); 288 | 289 | imageStream.on('error', function(error) { 290 | response.send('404', 'Not found'); 291 | return; 292 | }); 293 | 294 | response.setHeader('Content-Type', 'image/jpeg'); 295 | imageStream.pipe(response); 296 | }; 297 | 298 | exports.deleteImage = function(gfs, mongodb, _primarycontactnumber, response) { 299 | console.log('Deleting image for primary contact number:' + _primarycontactnumber); 300 | var collection = mongodb.collection('fs.files'); 301 | 302 | collection.remove({_id: _primarycontactnumber, 303 | filename: 'image'}, function (error, contact) { 304 | if (error) { 305 | console.log(error); 306 | return; 307 | } 308 | 309 | if (contact === null) { 310 | response.send('404', 'Not found'); 311 | return; 312 | } 313 | else { 314 | console.log('Successfully deleted image for primary contact number: ' + _primarycontactnumber); 315 | } 316 | }); 317 | 318 | response.send('Successfully deleted image for primary contact number: ' + _primarycontactnumber); 319 | } 320 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Packt Publishing 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RESTful Web API Design with Node.JS Second Edition 2 | 3 | This code targets developers who want to enrich their development skills by learning how to develop scalable, server-side, RESTful applications based on the Node.js platform. You also need to be aware of HTTP communication concepts and should have a working knowledge of the JavaScript language. Knowledge of REST would be an added advantage but is definitely not a necessity. 4 | 5 | You can also refer to the following books: 6 | 7 | * [Node.js Design Patterns](https://www.packtpub.com/web-development/nodejs-design-patterns?utm_source=github&utm_medium=related&utm_campaign=9781783287314) 8 | * [Node.js Blueprints](https://www.packtpub.com/web-development/nodejs-blueprints?utm_source=github&utm_medium=related&utm_campaign=9781783287338) 9 | * [Mastering Node.js](https://www.packtpub.com/web-development/mastering-nodejs?utm_source=github&utm_medium=related&utm_campaign=9781782166320) 10 | --------------------------------------------------------------------------------