Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

EWD 3 Training Course Part 45: Using QEWD's Advanced MicroService Functionality

281 views

Published on

This is Part 45 of the EWD Training Course. In this presentation we'll examine in detail the advanced MicroService capabilities of QEWD, eg dynamic routes, templated paths, custom response handling and chained MicroServices

Published in: Software
  • Be the first to comment

EWD 3 Training Course Part 45: Using QEWD's Advanced MicroService Functionality

  1. 1. Copyright © 2016 M/Gateway Developments Ltd EWD 3 Training Course Part 45 Using QEWD's Advanced MicroService Functionality Rob Tweed Director, M/Gateway Developments Ltd Twitter: @rtweed
  2. 2. Copyright © 2016 M/Gateway Developments Ltd Using QEWD's Advanced MicroService Functionality • This part of the course assumes that you've taken, at least: • Part 31: Using QEWD for Web and REST Services • http://www.slideshare.net/robtweed/ewd-3-training-course-part-31-ewdxpress-for-web-and-rest-services • Part 43: Using JSON Web Tokens with QEWD REST Services – https://www.slideshare.net/robtweed/ewd-3-training-course-part-43-using-json-web-tokens-with-qewd-rest-services • Part 44: Creating MicroServices with QEWD.js
  3. 3. Copyright © 2016 M/Gateway Developments Ltd MicroServices: Background • For an introduction on MicroServices, what they are, how they work and when and why they should be considered, see this overview on QEWD.js and MicroServices: • https://www.slideshare.net/robtweed/qewdjs-json-web-tokens-microservices
  4. 4. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroService Fabric ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process User authentication Demographics Pharmacy ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Client Orchestration HTTPS WebSocket Connections
  5. 5. Copyright © 2016 M/Gateway Developments Ltd QEWD.js Solution ewd-qoper8 queue Express Node.js socket.io Equivalent to ewd-client socket.io-client Incoming REST request
  6. 6. Copyright © 2016 M/Gateway Developments Ltd QEWD.js Solution ewd-qoper8 queue Express Node.js socket.io Equivalent to ewd-client socket.io-client Incoming WebSocket request
  7. 7. Copyright © 2016 M/Gateway Developments Ltd QEWD.js Solution ewd-qoper8 queue Express Node.js socket.io Equivalent to ewd-client socket.io-client Incoming WebSocket request Process Locally?
  8. 8. Copyright © 2016 M/Gateway Developments Ltd QEWD.js Solution ewd-qoper8 queue Express Node.js socket.io Equivalent to ewd-client socket.io-client Incoming WebSocket request Handled by remote MicroService?
  9. 9. Copyright © 2016 M/Gateway Developments Ltd QEWD.js Solution ewd-qoper8 queue Express Node.js socket.io Persistent Bi-directional WebSocket connection Secured over HTTPS ewd-qoper8 queue Express Node.js socket.io Equivalent to ewd-client socket.io-client Remote QEWD MicroService
  10. 10. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroService Architecture • Incoming requests can be either from: – a browser over WebSockets or Ajax – REST over HTTP / HTTPS • This tutorial will focus on REST
  11. 11. Copyright © 2016 M/Gateway Developments Ltd Advanced QEWD MicroServices • In this Part of this course, we'll look at the advanced MicroServices features of QEWD, including: – Templated routes – Dynamic path-defined destinations – Federated composite MicroServices from a group of destinations – Re-direction of MicroService responses to another MicroService – Chained MicroServices
  12. 12. Copyright © 2016 M/Gateway Developments Ltd Example in Part 44 • In the previous part of this course we set up an example with: – A primary server • One local service: /api/info – A Login MicroService server • One MicroService: /api/login • /api/info could not be run until /api/login had been run, and its returned JWT used in the Authorization Header
  13. 13. Copyright © 2016 M/Gateway Developments Ltd Example in Part 44 ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process User authentication ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Client Primary Server HTTPS WebSocket Connections Login MicroService Server
  14. 14. Copyright © 2016 M/Gateway Developments Ltd Advanced MicroService Functionality • To demonstrate the advanced MicroService Functionality, we'll extend the examples used in Part 44 • Primary Server: – Startup file: ~/qewd/ms-startup.js – Local REST services: • ~/qewd/node_modules/myLocalServices • Login MicroService Server: – Startup file: ~/qewd/loginservice.js – MicroService Handler Module: • ~/qewd/node_modules/login-micro-service.js
  15. 15. Copyright © 2016 M/Gateway Developments Ltd Advanced MicroService Functionality • To demonstrate the advanced MicroService Functionality, we'll extend the examples used in Part 44 • In the example code, I'll assume the host addresses for the servers are: – Primary Server: 192.168.1.120:8080 – Login MicroService Server: 192.168.1.121:8080 • Adjust these to match your servers
  16. 16. Copyright © 2016 M/Gateway Developments Ltd Templated Routes • In part 44, the login MicroService route was defined in the Primary Server's startup file using an absolute route: u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' } ] } };
  17. 17. Copyright © 2016 M/Gateway Developments Ltd Templated Routes • Just as with QEWD's REST routing, your MicroService Routes can define variable components, eg: routes: [ { path: '/api/patient/:patientId/demographics', method: 'GET', destination: 'login-micro-service' } ]
  18. 18. Copyright © 2016 M/Gateway Developments Ltd Templated Routes • Templated Routes can contain any number of variable parts, eg: routes: [ { path: '/api/patient/:patientId/headings/:headingName/info', method: 'GET', destination: 'login-micro-service' } ]
  19. 19. Copyright © 2016 M/Gateway Developments Ltd How Templated Routes Work • The message sent to the MicroService will contain: – The actual path that was used – The path template that it matched – The values of the variable path components, as name/value pairs
  20. 20. Copyright © 2016 M/Gateway Developments Ltd How Templated Routes Work • For example, if the specified route is routes: [ { path: '/api/patient/:patientId/headings/:headingName/info', method: 'GET', destination: 'patientInfo_service' } ]
  21. 21. Copyright © 2016 M/Gateway Developments Ltd How Templated Routes Work • For example, if the specified route is • and the actual request was: – /api/patient/123456/headings/allergies/info routes: [ { path: '/api/patient/:patientId/headings/:headingName/info', method: 'GET', destination: 'patientInfo_service' } ]
  22. 22. Copyright © 2016 M/Gateway Developments Ltd How Templated Routes Work • For example, if the specified route is • and the actual request was: – /api/patient/123456/headings/allergies/info • Message to MicroService will include: – Path: /api/patient/123456/headings/allergies/info – Matched Template Path: /api/patient/:patientId/headings/:headingName/info – Parameters: {patientId: 123456, headingName: 'allergies'} routes: [ { path: '/api/patient/:patientId/headings/:headingName/info', method: 'GET', destination: 'patientInfo_service' } ]
  23. 23. Copyright © 2016 M/Gateway Developments Ltd Example primary Server Startup File (1) var config = { managementPassword: 'keepThisSecret!', serverName: 'New QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' }, u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/patient/:patientId/demographics', method: 'GET', destination: 'login_service' } ] } }; Note that both routes have the same destination
  24. 24. Copyright © 2016 M/Gateway Developments Ltd Example primary Server Startup File (2) var routes = [ { path: '/api', module: 'myLocalServices', errors: { notfound: { text: 'Resource Not Recognised', statusCode: 404 } } } ]; var qewd = require('qewd').master; var q = qewd.start(config, routes);
  25. 25. Copyright © 2016 M/Gateway Developments Ltd Handling Templated Routes on MicroService Server • Define the templated route in your Worker Process Handler module, eg – ~/qewd/node_modules/login-micro-service.js
  26. 26. Copyright © 2016 M/Gateway Developments Ltd Handling Templated Routes on MicroService Server • Define the templated route in your Worker Process Handler module, eg – ~/qewd/node_modules/login-micro-service.js init: function() { routes = { '/api/login': { POST: login } }; router.addMicroServiceHandler(routes, module.exports); } This is what we'd created in Part 44 when we defined the login MicroService
  27. 27. Copyright © 2016 M/Gateway Developments Ltd Handling Templated Routes on MicroService Server init: function() { routes = { '/api/login': { POST: login }, ' /api/patient/:patientId/demographics ': { GET: getDemographics } }; router.addMicroServiceHandler(routes, module.exports); } Add the new templated route definition
  28. 28. Copyright © 2016 M/Gateway Developments Ltd Handling Templated Routes on MicroService Server init: function() { routes = { '/api/login': { POST: login }, ' /api/patient/:patientId/demographics ': { GET: getDemographics } }; router.addMicroServiceHandler(routes, module.exports); } Specify the route template exactly the same as on the primary server
  29. 29. Copyright © 2016 M/Gateway Developments Ltd Handling Templated Routes on MicroService Server init: function() { routes = { '/api/login': { POST: login }, ' /api/patient/:patientId/demographics ': { GET: getDemographics } }; router.addMicroServiceHandler(routes, module.exports); } So how will the handler function – getDemographics() in our example here – handle the variable path?
  30. 30. Copyright © 2016 M/Gateway Developments Ltd Handling Templated Routes on MicroService Server function getDemographics(args, finished) { var patientId = args.patientId; // ..etc } The values of variable path components are automatically made available to you as properties of the args object It's identical to handling variable routes in standard QEWD REST services (see Part 31)
  31. 31. Copyright © 2016 M/Gateway Developments Ltd Handling Templated Routes on MicroService Server var patients = { '123456': { firstName: 'Rob', lastName: 'Tweed', gender: 'Male', country: 'UK' }, '123457': { firstName: 'Jane', lastName: 'Smith', gender: 'Female', country: 'USA' }, }; function getDemographics(args, finished) { var patientId = args.patientId.toString(); if (!patientId || patientId === '') { return finished({error: 'You must specify a patientId'}); } if (!patients[patientId]) { return finished({error: 'Invalid patientId'}); } finished(patients[patientId]); } For example: hard-coded simulation of a patient database
  32. 32. Copyright © 2016 M/Gateway Developments Ltd Example MicroService Module (1) var router = require('qewd-router'); var routes; function login(args, finished) { var username = args.req.body.username; var password = args.req.body.password; var session = args.session; if (username === 'rob' && password === 'secret') { session.userText = 'Welcome Rob'; session.username = username; session.authenticated = true; session.timeout = 1200; session.makeSecret('username'); session.makeSecret('authenticated'); return finished({ok: true}); } else { return finished({error: 'Invalid login'}); } }
  33. 33. Copyright © 2016 M/Gateway Developments Ltd Example MicroService Module (2) var patients = { '123456': { firstName: 'Rob', lastName: 'Tweed', gender: 'Male', country: 'UK' }, '123457': { firstName: 'Jane', lastName: 'Smith', gender: 'Female', country: 'USA' }, }; function getDemographics(args, finished) { var patientId = args.patientId.toString(); if (!patientId || patientId === '') { return finished({error: 'You must specify a patientId'}); } if (!patients[patientId]) { return finished({error: 'Invalid patientId'}); } finished(patients[patientId]); }
  34. 34. Copyright © 2016 M/Gateway Developments Ltd Example MicroService Module (3) module.exports = { init: function() { routes = { '/api/login': { POST: login }, '/api/patient/:patientId/demographics': { GET: getDemographics } }; router.addMicroServiceHandler(routes, module.exports); } };
  35. 35. Copyright © 2016 M/Gateway Developments Ltd Try it • Stop and restart the startup files on the Primary and MicroService servers • In your REST Client: – GET /api/patient/1234567/demographics
  36. 36. Copyright © 2016 M/Gateway Developments Ltd Try it
  37. 37. Copyright © 2016 M/Gateway Developments Ltd Try it It successfully returned the results for patient 123457 Try changing the patientId in the path to 123456
  38. 38. Copyright © 2016 M/Gateway Developments Ltd Security Issue • We have a problem – the demographics MicroService can be invoked by anyone • It should only be accessible if you've logged in using the Login MicroService • We can fix this by adding a beforeMicroServiceHandler() function into the MicroService module
  39. 39. Copyright © 2016 M/Gateway Developments Ltd Example MicroService Module (3) module.exports = { init: function() { routes = { '/api/login': { POST: login }, '/api/patient/:patientId/demographics': { GET: getDemographics } }; router.addMicroServiceHandler(routes, module.exports); }, beforeMicroServiceHandler: function(req, finished) { // authenticate against the JWT (except for Login requests) if (req.path !== '/api/login') { return this.jwt.handlers.validateRestRequest.call(this, req, finished); } } };
  40. 40. Copyright © 2016 M/Gateway Developments Ltd Example MicroService Module (3) module.exports = { init: function() { routes = { '/api/login': { POST: login }, '/api/patient/:patientId/demographics': { GET: getDemographics } }; router.addMicroServiceHandler(routes, module.exports); }, beforeMicroServiceHandler: function(req, finished) { // authenticate against the JWT (except for Login requests) if (req.path !== '/api/login') { return this.jwt.handlers.validateRestRequest.call(this, req, finished); } } }; Ignore Login requests – no JWT needed for them
  41. 41. Copyright © 2016 M/Gateway Developments Ltd Example MicroService Module (3) module.exports = { init: function() { routes = { '/api/login': { POST: login }, '/api/patient/:patientId/demographics': { GET: getDemographics } }; router.addMicroServiceHandler(routes, module.exports); }, beforeMicroServiceHandler: function(req, finished) { // authenticate against the JWT (except for Login requests) if (req.path !== '/api/login') { return this.jwt.handlers.validateRestRequest.call(this, req, finished); } } }; This is the same API that we used in the previous Part of the course. There we applied it to the beforeHandler() function of the Local REST Service Module
  42. 42. Copyright © 2016 M/Gateway Developments Ltd Try it • Stop and restart the startup files on the Primary and MicroService servers • In your REST Client: – GET /api/patient/1234567/demographics • Now you should get an error: • Authorization Header missing or JWT not found in header (expected format: Bearer {{JWT}}
  43. 43. Copyright © 2016 M/Gateway Developments Ltd Try it • OK so try logging in: • In your REST Client: – POST /api/login • {"username": "rob", "password": "secret"} • If it logged you in successfully, copy the returned JWT into the Authorization Header: • Authorization: Bearer {{JWT}}
  44. 44. Copyright © 2016 M/Gateway Developments Ltd Try it • Now try the Demographics request again • In your REST Client: – GET /api/patient/1234567/demographics • Now it should work • Only logged in users can invoke the demographics request
  45. 45. Copyright © 2016 M/Gateway Developments Ltd Handling Templated Routes on MicroService Server init: function() { routes = { '/api/login': { POST: login }, ' /api/patient/:patientId/headings/:heading/summary ': { GET: getHeadingSummary } }; router.addMicroServiceHandler(routes, module.exports); } A path can include as many variable components as you require. Here, we have :patientId and :heading
  46. 46. Copyright © 2016 M/Gateway Developments Ltd Handling Templated Routes on MicroService Server function getHeadingSummary(args, finished) { var patientId = args.patientId; var heading = args.heading; // ..etc } The values of variable path components are automatically made available to you as properties of the args object
  47. 47. Copyright © 2016 M/Gateway Developments Ltd Dynamic Routes
  48. 48. Copyright © 2016 M/Gateway Developments Ltd Dynamic Routes • So far, our examples have used fixed destinations: – A URL route is tied to a specific destination, eg: u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' } ] }
  49. 49. Copyright © 2016 M/Gateway Developments Ltd Dynamic Routes • You can also have dynamic routes • :destination is a reserved URL path component name • It's mapped at run-time to the destination actually specified in the URL, and the request is routed to that physical destination
  50. 50. Copyright © 2016 M/Gateway Developments Ltd Dynamic Routes • For example, suppose we have several stores, and we want to retrieve a list of items in stock at a particular store
  51. 51. Copyright © 2016 M/Gateway Developments Ltd Dynamic Routes • Define the store destinations in the primary server's startup configuration: u_services: { destinations: { store1: { host: 'http://store1.retailer.com:8080', application: 'stock-list' }, store2: { host: 'http://store2.retailer.com:8080', application: 'stock-list' }, store3: { host: 'http://store3.retailer.com:8080', application: 'stock-list' } } }
  52. 52. Copyright © 2016 M/Gateway Developments Ltd Dynamic Routes • Then define the route with the variable destination: u_services: { destinations: { // }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET' } ] }
  53. 53. Copyright © 2016 M/Gateway Developments Ltd Dynamic Routes • Each store would have the stock-list application module installed: var router = require('qewd-router'); var routes; function getStockList(args, finished) { // collate the stock list for the store var stockListObj = { stock: 'stock list here...' }; finished(stockListObj);} module.exports = { init: function() { routes = { '/api/store/:destination/stocklist': { GET: getStockList } }; router.addMicroServiceHandler(routes, module.exports); } };
  54. 54. Copyright © 2016 M/Gateway Developments Ltd Dynamic Routes • Each store would have the stock-list application module installed: var router = require('qewd-router'); var routes; function getStockList(args, finished) { // collate the stock list for the store var stockListObj = { stock: 'stock list here...' }; finished(stockListObj);} module.exports = { init: function() { routes = { '/api/store/:destination/stocklist': { GET: getStockList } }; router.addMicroServiceHandler(routes, module.exports); } }; URL path must be specified identically to primary server's route
  55. 55. Copyright © 2016 M/Gateway Developments Ltd Dynamic Routes • The client then specifies the URL: – GET /api/store/store2/stocklist – In this case, the request will be routed to the destination named store2
  56. 56. Copyright © 2016 M/Gateway Developments Ltd Dynamic Routes • The URL route can include other variable components, eg • The handler function getStockListByCategory() would access the category using args.category routes = { '/api/store/:destination/category/:category/stocklist': { GET: getStockListByCategory }, }
  57. 57. Copyright © 2016 M/Gateway Developments Ltd Adding Security • Our earlier example included a Login MicroService on a server specifically for user authentication • We've now added 3 stores as additional locations • We'll want to make sure that the stores can't be interrogated until the user has logged in
  58. 58. Copyright © 2016 M/Gateway Developments Ltd Adding Security • Just add the beforeMicroServiceHandler() function to the handler module in each of the stores: beforeMicroServiceHandler: function(req, finished) { return this.jwt.handlers.validateRestRequest.call(this, req, finished); } Every incoming request to each store will now need a valid JWT that originated from a valid Login
  59. 59. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroService Fabric ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process ewd-qoper8 queue Expre ss Nod e.js socke t.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process User authentication Store 1 Store 2 ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Client Primary Server HTTPS WebSocket Connections ewd-qoper8 queue Express Node.j s socket.i o Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Store 3
  60. 60. Copyright © 2016 M/Gateway Developments Ltd Grouped Destinations
  61. 61. Copyright © 2016 M/Gateway Developments Ltd Grouped Destinations • In the previous example, you requested the stock list from a single store that you specified • What if you wanted to get a combined stock listing from all stores? • That's where Grouped Destinations can be used
  62. 62. Copyright © 2016 M/Gateway Developments Ltd Setting up a Group Destination • On the primary server: – Define the destination and any routes using the group destination • On the servers within the group destination: – Define the route(s) within the application module
  63. 63. Copyright © 2016 M/Gateway Developments Ltd Primary Server • Within the startup file configuration: u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service' }, store1: { host: 'http://192.168.1.114:8080', application: 'stock-list' }, store2: { host: 'http://192.168.1.119:8080', application: 'stock-list' }, allStores: { destinations: ['store1', 'store2'] } }, routes: [ ] }
  64. 64. Copyright © 2016 M/Gateway Developments Ltd Primary Server • Within the startup file configuration: u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service' }, store1: { host: 'http://192.168.1.114:8080', application: 'stock-list' }, store2: { host: 'http://192.168.1.119:8080', application: 'stock-list' }, allStores: { destinations: ['store1', 'store2'] } }, routes: [ ] } We're defining a new destination named allStores which is a group consisting of store1 and store2, each of which must be separately defined
  65. 65. Copyright © 2016 M/Gateway Developments Ltd Primary Server • Within the startup file configuration: u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service' }, store1: { host: 'http://192.168.1.114:8080', application: 'stock-list' }, store2: { host: 'http://192.168.1.119:8080', application: 'stock-list' }, allStores: { destinations: ['store1', 'store2'] } }, routes: [ ] } We're defining a new destination named allStores which is a group consisting of store1 and store2, each of which must be separately defined
  66. 66. Copyright © 2016 M/Gateway Developments Ltd Primary Server • Now define any routes for this group: u_services: { destinations: { }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/store/:destination/stocklist', method: 'GET' }, { path: '/api/store/:destination/category/:category/stocklist', method: 'GET' }, { path: '/api/all/stocklist', method: 'GET', destination: 'allStores' } ] }
  67. 67. Copyright © 2016 M/Gateway Developments Ltd On each Server in the Group • Define route(s) in the application module init: function() { routes = { '/api/store/:destination/stocklist': { GET: getStockList }, '/api/store/:destination/category/:category/stocklist': { GET: getStockListByCategory }, '/api/all/stocklist': { GET: getStockList } }; router.addMicroServiceHandler(routes, module.exports); },
  68. 68. Copyright © 2016 M/Gateway Developments Ltd On each Server in the Group • Define route(s) in the application module init: function() { routes = { '/api/store/:destination/stocklist': { GET: getStockList }, '/api/store/:destination/category/:category/stocklist': { GET: getStockListByCategory }, '/api/all/stocklist': { GET: getStockList } }; router.addMicroServiceHandler(routes, module.exports); }, In this example, we'll just re-use the getStockList() function that we previously defined
  69. 69. Copyright © 2016 M/Gateway Developments Ltd Security / Authentication • We've already added the beforeMicroServiceHandler() function, so you'll have to first login before you can use the new Grouped destination request. beforeMicroServiceHandler: function(req, finished) { return this.jwt.handlers.validateRestRequest.call(this, req, finished); }
  70. 70. Copyright © 2016 M/Gateway Developments Ltd Try it • Stop and restart the startup files on the Primary and MicroService servers • In your REST Client: – GET /api/all/stocklist • You should see activity on all machines, but you should get an error: • Authorization Header missing or JWT not found in header (expected format: Bearer {{JWT}}
  71. 71. Copyright © 2016 M/Gateway Developments Ltd Try it • OK so try logging in: • In your REST Client: – POST /api/login • {"username": "rob", "password": "secret"} • If it logged you in successfully, copy the returned JWT into the Authorization Header: • Authorization: Bearer {{JWT}}
  72. 72. Copyright © 2016 M/Gateway Developments Ltd Try it • Now try again: – GET /api/all/stocklist • This time you should get a composite result back from your store MicroServices – Remember that we asked to run the same getStockList function as before on each store MicroService
  73. 73. Copyright © 2016 M/Gateway Developments Ltd Try It
  74. 74. Copyright © 2016 M/Gateway Developments Ltd Try It
  75. 75. Copyright © 2016 M/Gateway Developments Ltd Try It Each store's results appears within a results object, identified by each destination's name
  76. 76. Copyright © 2016 M/Gateway Developments Ltd Try It Response format for all Grouped destinations {results: destination1: {results object}, destination2: {results object}, …etc }
  77. 77. Copyright © 2016 M/Gateway Developments Ltd Try It A new JWT with updated expiry is also returned
  78. 78. Copyright © 2016 M/Gateway Developments Ltd Dynamic Grouped Destination? u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service' }, store1: { host: 'http://192.168.1.114:8080', application: 'stock-list' }, store2: { host: 'http://192.168.1.119:8080', application: 'stock-list' }, allStores: { destinations: ['store1', 'store2'] } }, routes: [ ] } Our grouped destination – allStores – is still just a destination.
  79. 79. Copyright © 2016 M/Gateway Developments Ltd Dynamic Grouped Destination? u_services: { destinations: { }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/store/:destination/stocklist', method: 'GET' }, { path: '/api/store/:destination/category/:category/stocklist', method: 'GET' }, { path: '/api/all/stocklist', method: 'GET', destination: 'allStores' } ] } So what happens if we use it for this route?
  80. 80. Copyright © 2016 M/Gateway Developments Ltd Try It
  81. 81. Copyright © 2016 M/Gateway Developments Ltd Try It
  82. 82. Copyright © 2016 M/Gateway Developments Ltd Try It Almost identical results as before! A composite result from each destination within the group Is returned
  83. 83. Copyright © 2016 M/Gateway Developments Ltd Try It If you look carefully, there is a small difference. This time each store returns a store property with a value of allStores.
  84. 84. Copyright © 2016 M/Gateway Developments Ltd Try It That's because the getStockList() function that ran on each store returned the value of args.destination function getStockList(args, finished) { // collate the stock list for the store var stockListObj = { store: args.destination, ip: '192.168.1.119', stock: 'stock list here...' }; finished(stockListObj); }
  85. 85. Copyright © 2016 M/Gateway Developments Ltd Try It That's because the getStockList() function that ran on each store returned the value of args.destination :destination was defined in the dynamic route that we just used, but not in the /api/all/stocklist route we used previously function getStockList(args, finished) { // collate the stock list for the store var stockListObj = { store: args.destination, ip: '192.168.1.119', stock: 'stock list here...' }; finished(stockListObj); }
  86. 86. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling
  87. 87. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling • So far we've let QEWD automatically return the responses to the REST client
  88. 88. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling • So far we've let QEWD automatically return the responses to the REST client • What if you want to do something with the response before it's returned?
  89. 89. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling • So far we've let QEWD automatically return the responses to the REST client • What if you want to do something with the response before it's returned, eg: – Re-packaging / re-formatting of the response – Conditional behaviour depending on the response content – Triggering a new request to another MicroService
  90. 90. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling • Each route that you define in your Primary server's startup file can have an optional additional property: – onResponse
  91. 91. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling • Each route that you define in your Primary server's startup file can have an optional additional property: – onResponse • You define a function to handle the response • The function has a single argument, args, that contains all you need to handle the response
  92. 92. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling u_services: { destinations: { }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/store/:destination/stocklist', method: 'GET' }, { path: '/api/store/:destination/category/:category/stocklist', method: 'GET' }, { path: '/api/all/stocklist', method: 'GET', destination: 'allStores' } ] } Here's our routes as defined in the Primary Server's startup file
  93. 93. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling u_services: { destinations: { }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/store/:destination/stocklist', method: 'GET' }, { path: '/api/store/:destination/category/:category/stocklist', method: 'GET' }, { path: '/api/all/stocklist', method: 'GET', destination: 'allStores' } ] } We'll add custom handling to this one
  94. 94. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling u_services: { destinations: { }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { console.log('** onResponse: ' + JSON.stringify(args)); } }, { path: '/api/store/:destination/category/:category/stocklist', method: 'GET' }, { path: '/api/all/stocklist', method: 'GET', destination: 'allStores' } ] } Let's initially just inspect that args object
  95. 95. Copyright © 2016 M/Gateway Developments Ltd Try it • First log in: • In your REST Client: – POST /api/login • {"username": "rob", "password": "secret"} • If it logged you in successfully, copy the returned JWT into the Authorization Header: • Authorization: Bearer {{JWT}}
  96. 96. Copyright © 2016 M/Gateway Developments Ltd Try it • Now send this request: – GET /api/store/store1/stocklist • Now look in the Node.js console log for your Primary server
  97. 97. Copyright © 2016 M/Gateway Developments Ltd onResponse args Object received: {"type":"restRequest","finished":true,"message":{"store":"store1","ip":"192.168.1.114","stock":"stock list here...","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDQwODQ0NzcsImlhdCI6MTUwNDA4MzI3NywiaX NzIjoicWV3ZC5qd3QiLCJhcHBsaWNhdGlvbiI6InN0b2NrLWxpc3QiLCJ0aW1lb3V0IjoxMjAwLCJxZXdkIjoiZmU3NDYwYTNmYz hhZjFmOWI4ZTFmNTFkNjNhYWM1N2Q3OTNkYzk1NmJiYWQ0YThjZGZiNDBhODE5OTc5NmE3ODNjMGJkZjJmNDIxZWU2 YzU3Y2Q1MjIxOGYwNTE3MTRlMTE1YjRiYTI1NjhhMzY0MmRjNDVjYWY5Y2I4MDIzNmYzNDNkNGFiOTJhODM1OWU2YTQ 0YzRmZTU2MmUzNjRhNGYyYzkwZjhkY2Y3ODIzM2I5N2NkOTA0OGZkZDM1NGY0NzJmMmU2YTk4Y2ZiNmU1MTBkMGI3 MTVkIiwidXNlclRleHQiOiJXZWxjb21lIFJvYiJ9.yAxPEOHRPWsBc7jNDfq_Rul- VCOSOchRAAD0Vqmbs1o"},"responseTime":"8ms"} ** onResponse: {"message":{"type":"ewd-qoper8-express","path":"/api/store/store1/stocklist","method":"GET","headers": {"host":"192.168.1.120:8080","authorization":"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDQwODQ0NDQsImlhdCI6MTUwNDA4MzI0NCwiaXNzIjoicWV3ZC5 qd3QiLCJhcHBsaWNhdGlvbiI6ImxvZ2luLW1pY3JvLXNlcnZpY2UiLCJ0aW1lb3V0IjoxMjAwLCJxZXdkIjoiZmU3NDYwYTNmYzh hZjFmOWI4ZTFmNTFkNjNhYWM1N2Q3OTNkYzk1NmJiYWQ0YThjZGZiNDBhODE5OTc5NmE3ODNjMGJkZjJmNDIxZWU2Y zU3Y2Q1MjIxOGYwNTE3MTRlMTE1YjRiYTI1NjhhMzY0MmRjNDVjYWY5Y2I4MDIzNmYzNDNkNGFiOTJhODM1OWU2YTQ0 YzRmZTU2MmUzNjRhNGYyYzkwZjhkY2Y3ODIzM2I5N2NkOTA0OGZkZDM1NGY0NzJmMmU2YTk4Y2ZiNmU1MTBkMGI3M TVkIiwidXNlclRleHQiOiJXZWxjb21lIFJvYiJ9.8pY7HEBIdhWZEXR0WBeIJBGEgn-WpRwcwDZf-0uJ0AM","content- type":"application/json"},"params":{"0":"store1/stocklist","type":"store"},"query":{},"body":{},"ip":"::ffff:192.168.1.74","ips": [],"application":"api","expressType":"store"},"destination":"store1","responseObj": {"type":"restRequest","finished":true,"message":{"store":"store1","ip":"192.168.1.114","stock":"stock list here...","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDQwODQ0NzcsImlhdCI6MTUwNDA4MzI3NywiaX NzIjoicWV3ZC5qd3QiLCJhcHBsaWNhdGlvbiI6InN0b2NrLWxpc3QiLCJ0aW1lb3V0IjoxMjAwLCJxZXdkIjoiZmU3NDYwYTNmYz hhZjFmOWI4ZTFmNTFkNjNhYWM1N2Q3OTNkYzk1NmJiYWQ0YThjZGZiNDBhODE5OTc5NmE3ODNjMGJkZjJmNDIxZWU2 YzU3Y2Q1MjIxOGYwNTE3MTRlMTE1YjRiYTI1NjhhMzY0MmRjNDVjYWY5Y2I4MDIzNmYzNDNkNGFiOTJhODM1OWU2YTQ 0YzRmZTU2MmUzNjRhNGYyYzkwZjhkY2Y3ODIzM2I5N2NkOTA0OGZkZDM1NGY0NzJmMmU2YTk4Y2ZiNmU1MTBkMGI3 MTVkIiwidXNlclRleHQiOiJXZWxjb21lIFJvYiJ9.yAxPEOHRPWsBc7jNDfq_Rul- VCOSOchRAAD0Vqmbs1o"},"responseTime":"8ms"}}
  98. 98. Copyright © 2016 M/Gateway Developments Ltd onResponse args Object{ "message": { "type": "ewd-qoper8-express", "path": "/api/store/store1/stocklist", "method": "GET", "headers": { "host": "192.168.1.120:8080", "authorization": "Bearer eyJ0eXAiOiJKV1..", "content-type": "application/json" }, "params": { "0": "store1/stocklist", "type": "store" }, "query": {}, "body": {}, "ip": "::ffff:192.168.1.74", "ips": [], "application": "api", "expressType": "store" }, "destination": "store1", "responseObj": { "type": "restRequest", "finished": true, "message": { "store": "store1", "ip": "192.168.1.114", "stock": "stock list here...", "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI..." }, "responseTime": "8ms" } } Let's prettify the args Object, so we can see what it contains
  99. 99. Copyright © 2016 M/Gateway Developments Ltd onResponse args Object { "message": { "type": "ewd-qoper8-express", "path": "/api/store/store1/stocklist", "method": "GET", "headers": { "host": "192.168.1.120:8080", "authorization": "Bearer eyJ0eXAiOiJKV1..", "content-type": "application/json" }, "params": { "0": "store1/stocklist", "type": "store" }, "query": {}, "body": {}, "ip": "::ffff:192.168.1.74", "ips": [], "application": "api", "expressType": "store" }, args.message: -The original incoming request object
  100. 100. Copyright © 2016 M/Gateway Developments Ltd onResponse args Object { "destination": "store1", } args.destination: -Any variable path components will be available as properties in their own right. So the value of :destination in our request is available to us
  101. 101. Copyright © 2016 M/Gateway Developments Ltd onResponse args Object { "responseObj": { "type": "restRequest", "finished": true, "message": { "store": "store1", "ip": "192.168.1.114", "stock": "stock list here...", "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI..." }, "responseTime": "8ms" } } args.responseObj: -The raw response object that was returned from the MicroService
  102. 102. Copyright © 2016 M/Gateway Developments Ltd onResponse args Object • It also contains two other properties that aren't shown, as they are functions: – args.handleResponse – args.send
  103. 103. Copyright © 2016 M/Gateway Developments Ltd onResponse args Object • It also contains two other properties that aren't shown, as they are functions: – args.handleResponse • If you want to re-package / re-format the response, you must use this function to return your response to the REST client. – args.send
  104. 104. Copyright © 2016 M/Gateway Developments Ltd onResponse args Object • It also contains two other properties that aren't shown, as they are functions: – args.handleResponse – args.send • If you want to send a request to another MicroService, you use this function
  105. 105. Copyright © 2016 M/Gateway Developments Ltd onResponse args Object • It also contains two other properties that aren't shown, as they are functions: – args.handleResponse – args.send • Note: if you use either of these functions, your onResponse() function MUST return true. This tells QEWD not to try to handle the response itself
  106. 106. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { console.log('** onResponse: ' + JSON.stringify(args)); } }, { path: '/api/store/:destination/category/:category/stocklist', method: 'GET' }, { path: '/api/all/stocklist', method: 'GET', destination: 'allStores' } ] } Let's return our own custom response…
  107. 107. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var respObj = { youSent: args.message.path, usingMethod: args.message.method, toStore: args.destination, stock: args.responseObj.message.stock }; args.handleResponse(respObj); return true; } } } ] }
  108. 108. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var respObj = { youSent: args.message.path, usingMethod: args.message.method, toStore: args.destination, stock: args.responseObj.message.stock }; args.handleResponse(respObj); return true; } } } ] } If an error has occurred, we'll let QEWD handle it
  109. 109. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var respObj = { youSent: args.message.path, usingMethod: args.message.method, toStore: args.destination, stock: args.responseObj.message.stock }; args.handleResponse(respObj); return true; } } } ] } Otherwise, we'll create our own response object from information in the args object
  110. 110. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var respObj = { youSent: args.message.path, usingMethod: args.message.method, toStore: args.destination, stock: args.responseObj.message.stock }; args.handleResponse(respObj); return true; } } } ] } Use the handleResponse() function to return the response to the REST client
  111. 111. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var respObj = { youSent: args.message.path, usingMethod: args.message.method, toStore: args.destination, stock: args.responseObj.message.stock }; args.handleResponse(respObj); return true; } } } ] } And finally tell QEWD that we've handled the response ourselves, so it doesn't need to
  112. 112. Copyright © 2016 M/Gateway Developments Ltd Try It • Remember to first log in
  113. 113. Copyright © 2016 M/Gateway Developments Ltd Try It
  114. 114. Copyright © 2016 M/Gateway Developments Ltd Try It There's our re-packaged custom response
  115. 115. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var respObj = { youSent: args.message.path, usingMethod: args.message.method, toStore: args.destination, stock: args.responseObj.message.stock }; args.handleResponse(respObj); return true; } } } ] } One thing to remember: Your onResponse() function is invoked in the QEWD Master process
  116. 116. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var respObj = { youSent: args.message.path, usingMethod: args.message.method, toStore: args.destination, stock: args.responseObj.message.stock }; args.handleResponse(respObj); return true; } } } ] } So make sure you don't do anything CPU-intensive or long-running
  117. 117. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var respObj = { youSent: args.message.path, usingMethod: args.message.method, toStore: args.destination, stock: args.responseObj.message.stock }; args.handleResponse(respObj); return true; } } } ] } You should consider running your logic in a QEWD worker process. If you do, you'd also have access to the database
  118. 118. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var respObj = { youSent: args.message.path, usingMethod: args.message.method, toStore: args.destination, stock: args.responseObj.message.stock }; args.handleResponse(respObj); return true; } } } ] } But how do you use a QEWD Worker Process from here? It's actually very easy…
  119. 119. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var msg = { type: 'save', token: args.responseObj.message.token, jwt: true }; } } } ] } First we create a message object It must contain at least these 3 properties. The value of type is up to you, but use the values shown for token and jwt
  120. 120. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var msg = { type: 'save', token: args.responseObj.message.token, jwt: true, params: { path: args.message.path, method: args.message.method, store: args.destination, stock: args.responseObj.message.stock } }; } } } ] } Now we'll add the data we want to send to the Worker in our message
  121. 121. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var msg = { type: 'save', token: args.responseObj.message.token, jwt: true, params: { path: args.message.path, method: args.message.method, store: args.destination, stock: args.responseObj.message.stock } }; this.handleMessage(msg, function(responseObj) { args.handleResponse(responseObj); }); } } } ] } this.handleMessage() is the ewd-qoper8 API for sending a message to a Worker. Its callback allows us to process the response from the Worker – we'll send the response to the REST client, using args.handleResponse()
  122. 122. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var msg = { type: 'save', token: args.responseObj.message.token, jwt: true, params: { path: args.message.path, method: args.message.method, store: args.destination, stock: args.responseObj.message.stock } }; this.handleMessage(msg, function(responseObj) { args.handleResponse(responseObj); }); return true; } } } ] } Finally, tell QEWD that we're Handling the response
  123. 123. Copyright © 2016 M/Gateway Developments Ltd Try It • Login and put the JWT into the Authorization header as before • Then send: – GET /api/store/store1/stocklist • You should get back an error: {"error": "Unable to load handler module for: stock-list"}
  124. 124. Copyright © 2016 M/Gateway Developments Ltd Why That Error? • It was expected that we'd get an error back from the QEWD Worker process – We haven't yet written a handler function for the message type we sent to it
  125. 125. Copyright © 2016 M/Gateway Developments Ltd Why That Error? • It was expected that we'd get an error back from the QEWD Worker process – We haven't yet written a handler function for the message type we sent to it • But the error was because it couldn't find a module named stock-list – Why was it looking for a module of this name?
  126. 126. Copyright © 2016 M/Gateway Developments Ltd Why That Error? • It was expected that we'd get an error back from the QEWD Worker process – We haven't yet written a handler function for the message type we sent to it • But the error was because it couldn't find a module named stock-list – Why was it looking for a module of this name? • It's because we sent the JWT as the message token
  127. 127. Copyright © 2016 M/Gateway Developments Ltd Custom Response Example u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { var msg = { type: 'save', token: args.responseObj.message.token, jwt: true, params: { path: args.message.path, method: args.message.method, store: args.destination, stock: args.responseObj.message.stock } }; this.handleMessage(msg, function(responseObj) { args.handleResponse(responseObj); }); return true; } } } ] } We sent the JWT that was returned by the MicroService
  128. 128. Copyright © 2016 M/Gateway Developments Ltd Time to Inspect the JWT u_services: { destinations: { }, routes: [ { path: '/api/store/:destination/stocklist', method: 'GET', onResponse: function(args) { if (!args.responseObj.message.error) { console.log('** token: ' + args.responseObj.message.token); var msg = { type: 'save', token: args.responseObj.message.token, jwt: true, params: { path: args.message.path, method: args.message.method, store: args.destination, stock: args.responseObj.message.stock } }; this.handleMessage(msg, function(responseObj) { args.handleResponse(responseObj); }); return true; } } } ] } We'll display the token in the Node.js console log
  129. 129. Copyright © 2016 M/Gateway Developments Ltd Time to Inspect the JWT • Restart QEWD on the Primary server and try sending that same request • Then take a look in the Node.js console log
  130. 130. Copyright © 2016 M/Gateway Developments Ltd Time to Inspect the JWT ** token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDQwOTQwNjcsImlhd CI6MTUwNDA5Mjg2NywiaXNzIjoicWV3ZC5qd3QiLCJhcHBsaWNhdGlvbiI6InN0 b2NrLWxpc3QiLCJ0aW1lb3V0IjoxMjAwLCJxZXdkIjoiZmU3NDYwYTNmYzhhZjF mOWI4ZTFmNTFkNjNkN2FlNmM3ZjAzZjg0MmJjYjQ0OWNkZmU4Mjc4YTY5O Dc5NmE3ODAwMGJkZjJmNDIxZWU2YzU3Y2Q1MjIxOGYwNTE3MTRlMTE1Yj RiYTI1NjhhMzY0MmRjNDVjYWY5Y2I4MDIzNmYzNDNkNGFiOTJhODM1OWU2 YTQ0YzRmZTU2MmUzNjRhNGYyYzkwZjhkY2Y3ODIzM2I5N2NkOTA0OGZkZ DM1NGY0NzJmMmU2YTk4Y2ZiNmU1MTBkMGI3MTVkIiwidXNlclRleHQiOiJXZ Wxjb21lIFJvYiJ9.2xGoGnrdFjMf6rn5GEkOAlHamFPceQC6jiS4M6siPb0 Wed, 30 Aug 2017 11:34:27 GMT; worker 13037 received message: {"type":"save","params": {"path":"/api/store/store1/stocklist","method":"GET","store":"store1","stock":"stock list
  131. 131. Copyright © 2016 M/Gateway Developments Ltd Inspect the JWT using jwt.io
  132. 132. Copyright © 2016 M/Gateway Developments Ltd Inspect the JWT using jwt.io This is why the QEWD Worker Is looking for a module named stock-list
  133. 133. Copyright © 2016 M/Gateway Developments Ltd Let's create that module • On your Primary server, create a new text file: – ~/qewd/node_modules/stock-list.js
  134. 134. Copyright © 2016 M/Gateway Developments Ltd Let's create that module • On your Primary server, create a new text file: – ~/qewd/node_modules/stock-list.js module.exports = { handlers: { save: function(messageObj, session, send, finished) { messageObj.handledByWorker = process.pid + ' at ' + Date.now(); finished(messageObj); } } };
  135. 135. Copyright © 2016 M/Gateway Developments Ltd Let's create that module module.exports = { handlers: { save: function(messageObj, session, send, finished) { messageObj.handledByWorker = process.pid + ' at ' + Date.now(); finished(messageObj); } } }; For the purpose of this example/demo, we're just going to send the incoming message back to the Master process
  136. 136. Copyright © 2016 M/Gateway Developments Ltd Let's create that module module.exports = { handlers: { save: function(messageObj, session, send, finished) { messageObj.handledByWorker = process.pid + ' at ' + Date.now(); finished(messageObj); } } }; But so that we can be sure that a Worker really handled it, we'll add this to the message
  137. 137. Copyright © 2016 M/Gateway Developments Ltd Try It • Restart QEWD on the Primary Server • Login again and copy the JWT to the Authorization Header • Then send: – GET /api/store/store1/stocklist
  138. 138. Copyright © 2016 M/Gateway Developments Ltd Try It You should see a response similar to this
  139. 139. Copyright © 2016 M/Gateway Developments Ltd Try It You can see it's the message we sent to the Worker for processing
  140. 140. Copyright © 2016 M/Gateway Developments Ltd Try It Here's the additional property added by our Worker module's handler function
  141. 141. Copyright © 2016 M/Gateway Developments Ltd By running in a Worker • You have access to the integrated QEWD database • You have access to the Session data contained within the JWT
  142. 142. Copyright © 2016 M/Gateway Developments Ltd Let's enhance that module module.exports = { handlers: { save: function(messageObj, session, send, finished) { messageObj.handledByWorker = process.pid + ' at ' + Date.now(); var db = this.db.use('stockList'); var ix = db.increment(); db.$(ix).setDocument(messageObj); finished(messageObj); } } }; For example, we could save the message object to the database
  143. 143. Copyright © 2016 M/Gateway Developments Ltd Let's enhance that module module.exports = { handlers: { save: function(messageObj, session, send, finished) { messageObj.handledByWorker = process.pid + ' at ' + Date.now(); var db = this.db.use('stockList'); var ix = db.increment(); db.$(ix).setDocument(messageObj); session.customHandledAt = Date.now(); finished(messageObj); } } }; And we could add a new data item to the Session / JWT
  144. 144. Copyright © 2016 M/Gateway Developments Ltd Try It • Restart QEWD • Login and copy the JWT to the Authorization Header as before • Send: – GET /api/store/store1/stocklist
  145. 145. Copyright © 2016 M/Gateway Developments Ltd Try It
  146. 146. Copyright © 2016 M/Gateway Developments Ltd Now Check the Database using qewd-monitor Click the Document Store Tab
  147. 147. Copyright © 2016 M/Gateway Developments Ltd Now Check the Database using qewd-monitor Here's the saved Document we created, containing the message we sent to the Worker
  148. 148. Copyright © 2016 M/Gateway Developments Ltd Reminder of how that happened module.exports = { handlers: { save: function(messageObj, session, send, finished) { messageObj.handledByWorker = process.pid + ' at ' + Date.now(); var db = this.db.use('stockList'); var ix = db.increment(); db.$(ix).setDocument(messageObj); session.customHandledAt = Date.now(); finished(messageObj); } } };
  149. 149. Copyright © 2016 M/Gateway Developments Ltd Now Inspect the JWT Copy this token and paste it into the jwt.io inspector
  150. 150. Copyright © 2016 M/Gateway Developments Ltd Now Inspect the JWT Here's the field we added to the Session and therefore to the JWT
  151. 151. Copyright © 2016 M/Gateway Developments Ltd Reminder of how that happened module.exports = { handlers: { save: function(messageObj, session, send, finished) { messageObj.handledByWorker = process.pid + ' at ' + Date.now(); var db = this.db.use('stockList'); var ix = db.increment(); db.$(ix).setDocument(messageObj); session.customHandledAt = Date.now(); finished(messageObj); } } };
  152. 152. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling • So far you've learned how to: – re-package / re-format the response from a MicroService – Process the response from a MicroService, including: • saving some or all of it to the QEWD database • Modifying the JWT before returning the response to the REST client • You should be able to adapt the examples to make the behaviour conditional on the response content
  153. 153. Copyright © 2016 M/Gateway Developments Ltd Custom Response Handling • What if you wanted to trigger a new request to another MicroService, eg: – Make the /api/login request to the Primary Server: • Invoke the call to the authentication MicroService to log us in (using the username and password credentials), as we've done already • But then, if successful, make a call to a demographics MicroService to get some basic information about the logged-in user • Add those demographic details to the response that is returned to the REST client
  154. 154. Copyright © 2016 M/Gateway Developments Ltd What we'll need to use: • The args object, passed as an argument to your onResponse() handler function contains two properties are functions: – args.handleResponse – args.send • If you want to send a request to another MicroService, you use this function
  155. 155. Copyright © 2016 M/Gateway Developments Ltd What we'll need to use: • The args object, passed as an argument to your onResponse() handler function contains two properties are functions: – args.handleResponse • We've already seen how to use this to return our customised response back to the REST client – args.send
  156. 156. Copyright © 2016 M/Gateway Developments Ltd What we'll need to use: • The args object, passed as an argument to your onResponse() handler function contains two properties are functions: – args.handleResponse – args.send • Now we'll use this function, which allows us to send a new request to a MicroService
  157. 157. Copyright © 2016 M/Gateway Developments Ltd We'll use these two routes var config = { managementPassword: 'keepThisSecret!', serverName: 'New QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' }, u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/patient/:patientId/demographics', method: 'GET', destination: 'login_service' } ] } }; This is the startup file for the Primary server
  158. 158. Copyright © 2016 M/Gateway Developments Ltd We'll use these two routes var config = { managementPassword: 'keepThisSecret!', serverName: 'New QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' }, u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/patient/:patientId/demographics', method: 'GET', destination: 'login_service' } ] } }; The demographics route was described in detail at the start of this Part of the course
  159. 159. Copyright © 2016 M/Gateway Developments Ltd We'll use these two routes var config = { managementPassword: 'keepThisSecret!', serverName: 'New QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' }, u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/patient/:patientId/demographics', method: 'GET', destination: 'login_service' } ] } }; For this example, both routes are handled by the same destination, but they could be at different destinations
  160. 160. Copyright © 2016 M/Gateway Developments Ltd We'll use these two routes var config = { managementPassword: 'keepThisSecret!', serverName: 'New QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' }, u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/patient/:patientId/demographics', method: 'GET', destination: 'login_service' } ] } }; The demographics route expects a variable value – the patientId Let's first change the login MicroService so it returns an id that we can use for the demographics route
  161. 161. Copyright © 2016 M/Gateway Developments Ltd Modify the Login MicroService • On your Login MicroService machine, find the handler module: – ~/qewd/node_modules/login-micro-service.js – We'll edit the login() function
  162. 162. Copyright © 2016 M/Gateway Developments Ltd Modify the Login MicroService function login(args, finished) { var username = args.req.body.username; var password = args.req.body.password; var session = args.session; if (username === 'rob' && password === 'secret') { session.userText = 'Welcome Rob'; session.username = username; session.userId = 123456; session.authenticated = true; session.timeout = 1200; session.makeSecret('username'); session.makeSecret('authenticated'); return finished({ok: true}); } else { return finished({error: 'Invalid login'}); } } Add this line For simplicity, in this Example, we're just hard-coding an Id In the real-world we'd probably do some kind of database lookup
  163. 163. Copyright © 2016 M/Gateway Developments Ltd Modify the Login MicroService • What this modification will do is add the userId to the JWT that is returned to the Primary server • Stop and restart the Login MicroService and try logging in again
  164. 164. Copyright © 2016 M/Gateway Developments Ltd Modified Login MicroService Response So the Login MicroService returns 2 properties: ok and token Let's copy and paste the token and inspect it using jwt.io
  165. 165. Copyright © 2016 M/Gateway Developments Ltd Modified Login MicroService Response So here's the new userId field that we added
  166. 166. Copyright © 2016 M/Gateway Developments Ltd Custom Login Response Handler • We now need to go back to the startup file on your Primary Server, and add an onResponse() handler function to the Login route definition
  167. 167. Copyright © 2016 M/Gateway Developments Ltd Primary Server Startup File var config = { managementPassword: 'keepThisSecret!', serverName: 'New QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' }, u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service', onResponse: function(args) { } } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service' }, { path: '/api/patient/:patientId/demographics', method: 'GET', destination: 'login_service' } ] } }; We'll zoom in here on the next slides..
  168. 168. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } },
  169. 169. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, We'll only make a call to the demographics service if the login was successful
  170. 170. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, Use this.jwt.handlers.getProperty() to extract a public claim from the JWT – in this case the userId This doesn't require the secret, so is lightweight enough to perform in the master process
  171. 171. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, The first argument specifies the Property or Claim name we want
  172. 172. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, The 2nd argument is a JWT. We'll use the JWT returned in the Login MicroService response
  173. 173. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, Next, we construct a minimal message object to send a demographics MicroService request
  174. 174. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, The path will match that of the demographics route We add into the path the userId extracted from the Login JWT
  175. 175. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, The HTTP method we want to use is GET
  176. 176. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, Finally, we must add the authorization header. The format must be the same as if we'd submitted the request from a REST client We'll use the JWT returned by the Login MicroService
  177. 177. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, If the MicroService we want to use had a POST method, we'd need to specify a body object in the message. If the URL needed additional name/value pairs, we'd add a query object to the method
  178. 178. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, The message object should be sufficient for the demographics MicroService, so now we send it using the args.send() function
  179. 179. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, We want to combine the response from the Login MicroService with what comes back from the Demographics MicroService, so we'll do that in the arg,send callback function
  180. 180. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, We'll get the ok property from the Login response In other situations you might have many more properties to merge into the final response
  181. 181. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, And now we're ready to return the composite response back to the REST client. We use args.handleResponse() for this, as before
  182. 182. Copyright © 2016 M/Gateway Developments Ltd Modified Login Response Handler routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', onResponse: function(args) { if (!args.responseObj.message.error) { var userId = this.jwt.handlers.getProperty('userId', args.responseObj.message.token); var message = { path: '/api/patient/' + userId + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.responseObj.message.token } }; args.send(message, function(responseObj) { responseObj.message.ok = args.responseObj.message.ok; args.handleResponse(responseObj); }); return true; } } }, One last very important step – we must tell QEWD that we're handling the response
  183. 183. Copyright © 2016 M/Gateway Developments Ltd Try It • Restart QEWD on the Primary and Login MicroService servers • Send: – GET /api/login • With the appropriate body payload containing the username and password
  184. 184. Copyright © 2016 M/Gateway Developments Ltd Try It
  185. 185. Copyright © 2016 M/Gateway Developments Ltd Try It Success!
  186. 186. Copyright © 2016 M/Gateway Developments Ltd Try It These properties came from the Demographics service
  187. 187. Copyright © 2016 M/Gateway Developments Ltd Try It And this came from the Login service
  188. 188. Copyright © 2016 M/Gateway Developments Ltd Dynamic Routing • We've already seen how you can use dynamic destinations by using the special path variable :destination • What about situations where the client can't determine the destination, but the Primary Server needs to determine it, based on the incoming message structure/payload?
  189. 189. Copyright © 2016 M/Gateway Developments Ltd onRequest() Function • A Route can specify an onRequest() function. – onRequest() is invoked on the Primary Server's Master Process, on receipt of the incoming requests that match the route path (or path template) – QEWD hands control and responsibility for routing the request to your onRequest() function
  190. 190. Copyright © 2016 M/Gateway Developments Ltd onRequest() Function • If a route specifies an onRequest() function, then the destination property and onResponse() function do not apply to the route, and are ignored if specified
  191. 191. Copyright © 2016 M/Gateway Developments Ltd onRequest() Function • An onRequest() function can have one of 3 outcomes: – It can re-route the incoming message (or a derivative of it) to another MicroService route – It can return an error immediately to the REST client – It can return a locally-generated response that is returned to the REST client
  192. 192. Copyright © 2016 M/Gateway Developments Ltd onRequest() Function Example routes: [ { path: '/api/rerouteTest/:index', method: 'GET', onRequest: function(args, send, handleResponse) { var message = { path: '/api/patient/' + args.index + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.jwt } }; send(message, handleResponse); } } ];
  193. 193. Copyright © 2016 M/Gateway Developments Ltd onRequest() Function Example routes: [ { path: '/api/rerouteTest/:index', method: 'GET', onRequest: function(args, send, handleResponse) { var message = { path: '/api/patient/' + args.index + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.jwt } }; send(message, handleResponse); } } ]; The onRequest() function has 3 arguments: -args -send -handleResponse
  194. 194. Copyright © 2016 M/Gateway Developments Ltd onRequest() Function Arguments • The onRequest() function arguments are: – args – send – handleResponse
  195. 195. Copyright © 2016 M/Gateway Developments Ltd onRequest() Function Arguments • The onRequest() function arguments are: – args: • args.req: the incoming REST request message, as broken down by QEWD • args.jwt: the JWT extracted from the Authorization header (assumes format Bearer {{jwt}} ) • Any variable components of the URL path are made available as properties of args, eg: – If the path template was /api/patient/:patientId/:heading – And the incoming request was for /api/123456/allergies – Then: » args.patientId = 123456 » args.heading = 'allergies' – send – handleResponse
  196. 196. Copyright © 2016 M/Gateway Developments Ltd onRequest() Function Arguments • The onRequest() function arguments are: – args – send • This is the function you will use for re-directing a message to another MicroService route • It's arguments are: – messageObj: the message object to send to the MicroService – handleResponse: see below – handleResponse
  197. 197. Copyright © 2016 M/Gateway Developments Ltd onRequest() Function Arguments • The onRequest() function arguments are: – args – send – handleResponse • The function you should use to return the response object to the REST client • It has a single argument: responseObject
  198. 198. Copyright © 2016 M/Gateway Developments Ltd onRequest() Function Example routes: [ { path: '/api/rerouteTest/:index', method: 'GET', onRequest: function(args, send, handleResponse) { var message = { path: '/api/patient/' + args.index + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.jwt } }; send(message, handleResponse); } } ]; Construct the message to be routed to another MicroService. This is just the same as you saw earlier for the onResponse() function
  199. 199. Copyright © 2016 M/Gateway Developments Ltd onRequest() Function Example routes: [ { path: '/api/rerouteTest/:index', method: 'GET', onRequest: function(args, send, handleResponse) { var message = { path: '/api/patient/' + args.index + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.jwt } }; send(message, handleResponse); } } ]; I'm constructing the redirected route path using the variable part of the incoming route
  200. 200. Copyright © 2016 M/Gateway Developments Ltd onRequest() Function Example routes: [ { path: '/api/rerouteTest/:index', method: 'GET', onRequest: function(args, send, handleResponse) { var message = { path: '/api/patient/' + args.index + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.jwt } }; send(message, handleResponse); } } ]; And adding a correctly-structured Authorization header using the incoming message's JWT
  201. 201. Copyright © 2016 M/Gateway Developments Ltd onRequest() Function Example routes: [ { path: '/api/rerouteTest/:index', method: 'GET', onRequest: function(args, send, handleResponse) { var message = { path: '/api/patient/' + args.index + '/demographics', method: 'GET', headers: { authorization: 'Bearer ' + args.jwt } }; send(message, handleResponse); } } ]; Finally I send the new message to be re-routed to the demographics MicroService handleResponse can be used as the callback for the send() function, so it returns the MicroService Response to the REST client
  202. 202. Copyright © 2016 M/Gateway Developments Ltd onRequest() Function Errors routes: [ { path: '/api/rerouteTest/:index', method: 'GET', onRequest: function(args, send, handleResponse) { handleResponse({error: 'This is an error}); }; } ]; To return an error, just use the handleResponse() function with an error object as its argument
  203. 203. Copyright © 2016 M/Gateway Developments Ltd onRequest() Function Local Response routes: [ { path: '/api/rerouteTest/:index', method: 'GET', onRequest: function(args, send, handleResponse) { handleResponse({ hello: 'world', local: 'response' }); }; } ]; To return a locally-generated response, just use the handleResponse() function with your response object as its argument
  204. 204. Copyright © 2016 M/Gateway Developments Ltd onRequest() Function Local Response routes: [ { path: '/api/rerouteTest/:index', method: 'GET', onRequest: function(args, send, handleResponse) { handleResponse({ hello: 'world', local: 'response' }); }; } ]; Note that the onRequest() function runs In the QEWD Master Process. You should avoid CPU-intensive logic! If necessary, redirect the logic to a worker process, using this.handleMessage() This was described earlier
  205. 205. Copyright © 2016 M/Gateway Developments Ltd Recap so far • You've now learned how to: – Use templated routes with variable path components – Use group destinations – Use dynamic route destinations – Add custom response handling • And optionally perform some or all of that handling in a QEWD worker process, allowing: – use of the integrated QEWD document database – The JWT contents to be manipulated • And optionally send one or more further requests to MicroServices and construct a composite response – Dynamically re-route requests sent to the Primary server to MicroService routes
  206. 206. Copyright © 2016 M/Gateway Developments Ltd All on the Primary Server • Everything we've done so far in this Part of the Course has been on the Primary Server
  207. 207. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroService Fabric ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process User authentication & demographics ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Client Primary Server HTTPS WebSocket Connections Login Request
  208. 208. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroService Fabric ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process User authentication & demographics ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Client Primary Server HTTPS WebSocket Connections Login Request
  209. 209. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroService Fabric ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process User authentication & demographics ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Client Primary Server HTTPS WebSocket Connections Login Response
  210. 210. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroService Fabric ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process User authentication & demographics ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Client Primary Server HTTPS WebSocket Connections Demographics Request
  211. 211. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroService Fabric ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process User authentication & demographics ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Client Primary Server WebSocket Connections Demographics Response
  212. 212. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroService Fabric ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process User authentication & demographics ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Client Primary Server HTTPS WebSocket Connections Composite Response
  213. 213. Copyright © 2016 M/Gateway Developments Ltd What about the MicroService Servers? • Is it possible for a MicroService to make requests to other MicroServices? – In other words…
  214. 214. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroService Fabric ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process User authentication Demographics ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Client Primary Server HTTPS WebSocket Connections Login Request
  215. 215. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroService Fabric ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process User authentication Demographics ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Client Primary Server HTTPS WebSocket Connections Login Request
  216. 216. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroService Fabric ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process User authentication Demographics ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Client Primary Server HTTPS WebSocket Connections Demographics Request
  217. 217. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroService Fabric ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process User authentication Demographics ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Client Primary Server HTTPS WebSocket Connections Demographics Response
  218. 218. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroService Fabric ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process User authentication Demographics ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Client Primary Server HTTPS WebSocket Connections Login Response
  219. 219. Copyright © 2016 M/Gateway Developments Ltd QEWD MicroService Fabric ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process User authentication Demographics ewd-qoper8 queue Express Node.js socket.io Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Cache GT.M, YottaDB Redis Node.js Worker Process Client Primary Server HTTPS WebSocket Connections Login Response
  220. 220. Copyright © 2016 M/Gateway Developments Ltd Lets build out the 3 servers • Primary Server – Will handle the /api/login request sent from the REST client • Login Server – Will handle the /api/login request sent from the Primary Server • Demographics Server – Will handle the /api/patient/:patientId/demographics request sent from the Login Server
  221. 221. Copyright © 2016 M/Gateway Developments Ltd Primary Server • Startup File – ~/qewd/primary.js
  222. 222. Copyright © 2016 M/Gateway Developments Ltd Primary Server (1) var config = { managementPassword: 'keepThisSecret!', serverName: 'New QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' }, u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', } ] } };
  223. 223. Copyright © 2016 M/Gateway Developments Ltd Primary Server (2) var routes = [ { path: '/api', module: 'localServices' } ]; var qewd = require('qewd').master; var q = qewd.start(config, routes);
  224. 224. Copyright © 2016 M/Gateway Developments Ltd Primary Server (1) - Notes var config = { managementPassword: 'keepThisSecret!', serverName: 'New QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' }, u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', } ] } }; The Primary Server only knows the route to the Login Server
  225. 225. Copyright © 2016 M/Gateway Developments Ltd Primary Server (1) - Notes var config = { managementPassword: 'keepThisSecret!', serverName: 'Primary QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' }, u_services: { destinations: { login_service: { host: 'http://192.168.1.121:8080', application: 'login-micro-service' } }, routes: [ { path: '/api/login', method: 'POST', destination: 'login_service', } ] } }; Adjust to match your Login Server location
  226. 226. Copyright © 2016 M/Gateway Developments Ltd Primary Server (2) - Notes var routes = [ { path: '/api', module: 'localServices' } ]; var qewd = require('qewd').master; var q = qewd.start(config, routes); Our example is only going to respond to incoming requests for /api/login which it will forward to the Login MicroService server. We won't be handling any other routes locally on this server. Nevertheless, the Primary Server needs to know to recognise any paths starting /api, so we need to specify it as a notional route We'll specify a non-existent local handler module named localServices that won't actually get used
  227. 227. Copyright © 2016 M/Gateway Developments Ltd Login Server • Startup file: – ~/qewd/loginserver.js • Worker Module for handling the /api/login login requests • ~/qewd/node_modules/login-micro-service.js
  228. 228. Copyright © 2016 M/Gateway Developments Ltd Login Server: Startup File var config = { managementPassword: 'keepThisSecret!', serverName: 'QEWD Login MicroService', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' }, u_services: { destinations: { demographics: { host: 'http://192.168.1.114:8080', application: 'demographics' } }, routes: [ { path: '/api/patient/:patientId/demographics', method: 'GET', destination: 'demographics' } ] } }; var qewd = require('qewd').master; qewd.start(config);
  229. 229. Copyright © 2016 M/Gateway Developments Ltd var config = { managementPassword: 'keepThisSecret!', serverName: 'QEWD Login MicroService', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' }, u_services: { destinations: { demographics: { host: 'http://192.168.1.114:8080', application: 'demographics' } }, routes: [ { path: '/api/patient/:patientId/demographics', method: 'GET', destination: 'demographics' } ] } }; var qewd = require('qewd').master; qewd.start(config); The Login Server needs to know the route to the Demographics MicroService Login Server: Startup File
  230. 230. Copyright © 2016 M/Gateway Developments Ltd var config = { managementPassword: 'keepThisSecret!', serverName: 'QEWD Login MicroService', port: 8080, poolSize: 2, database: { type: 'gtm' }, jwt: { secret: 'someSecret123' }, u_services: { destinations: { demographics: { host: 'http://192.168.1.114:8080', application: 'demographics' } }, routes: [ { path: '/api/patient/:patientId/demographics', method: 'GET', destination: 'demographics' } ] } }; var qewd = require('qewd').master; qewd.start(config); Adjust to match your Demographics Server location Login Server: Startup File

×