Skip to content

Commit

Permalink
[SDPA-3045] Added request id in all route requests (#500)
Browse files Browse the repository at this point in the history
* Added request id in all route requests
  • Loading branch information
tim-yao authored Aug 22, 2019
1 parent 364e2d3 commit 0840f07
Show file tree
Hide file tree
Showing 14 changed files with 222 additions and 152 deletions.
4 changes: 2 additions & 2 deletions examples/vic-gov-au/store/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { logger } from '@dpc-sdp/ripple-nuxt-tide/lib/core'

export const actions = {
async nuxtServerInit ({ dispatch }) {
async nuxtServerInit ({ dispatch }, { req }) {
try {
await dispatch('tide/init')
await dispatch('tide/init', { requestId: req.requestId })
} catch (error) {
if (process.server) {
logger.error('Tide API server has an error.', { error, label: 'App' })
Expand Down
4 changes: 3 additions & 1 deletion packages/ripple-nuxt-tide/lib/core/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,16 @@ if (!process.client) {

// Format for our console output.
const printFormat = format.printf(info => {
const { timestamp, message, level, label, error } = info
const { timestamp, message, level, label, error, ...rest } = info
const printLabel = label ? `[${label}] ` : ' '
let log = `${timestamp} ${printLabel}${level} ${message}`
// Only if there is an error
// Must pass error obj by using `error` meta.
if (error) {
log = error.stack ? `${log}\n${error.stack}` : log
}

log += ' ' + JSON.stringify(rest)
return log
})

Expand Down
151 changes: 77 additions & 74 deletions packages/ripple-nuxt-tide/lib/core/middleware.js

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion packages/ripple-nuxt-tide/lib/core/tide-helper.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@

import cuid from 'cuid'
import mime from 'mime-types'

// Generate a unique id
export const generateId = () => {
return cuid()
}

// Private helpers
export const mergeIncludes = (includes, includesMergedIn) => {
if (includesMergedIn) {
Expand Down
74 changes: 41 additions & 33 deletions packages/ripple-nuxt-tide/lib/core/tide.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ export const tide = (axios, site, config) => ({
* @param {String} resource Resource type e.g. <entity type>/<bundle>
* @param {Object} params Object to convert to QueryString. Passed in URL.
* @param {String} id Resource UUID
* @param {String} authToken Authentication token
* @param {Object} headersConfig Tide API request headers config object:{ authToken: '', requestId: '' }
*/
get: async function (resource, params = {}, id = '', authToken) {
get: async function (resource, params = {}, id = '', headersConfig = {}) {
// axios config
const axiosConfig = {
baseUrl: config.baseUrl,
Expand All @@ -31,26 +31,34 @@ export const tide = (axios, site, config) => ({

if (this.isModuleEnabled('authenticatedContent')) {
// Set 'X-Authorization' header if authToken present
if (authToken && !isTokenExpired(authToken)) {
_.merge(axiosConfig.headers, { 'X-Authorization': `Bearer ${authToken}` })
if (headersConfig.authToken && !isTokenExpired(headersConfig.authToken)) {
_.merge(axiosConfig.headers, { 'X-Authorization': `Bearer ${headersConfig.authToken}` })
}
}

if (headersConfig.requestId) {
axiosConfig.headers['X-Request-Id'] = headersConfig.requestId
}

const siteParam = 'site=' + site
const url = `${apiPrefix}${resource}${id ? `/${id}` : ''}?${siteParam}${Object.keys(params).length ? `&${qs.stringify(params, { indices: false })}` : ''}`
return axios.$get(url, axiosConfig)
},

post: async function (resource, data = {}, id = '') {
// axios config
const axiosConfig = {
baseUrl: config.baseUrl,
auth: config.auth,
headers: {
'Content-Type': 'application/vnd.api+json;charset=UTF-8',
'X-Request-Id': helper.generateId()
}
}
const siteParam = resource === 'user/register' ? '?site=' + site : ''
const url = `${apiPrefix}${resource}${id ? `/${id}` : ''}${siteParam}`

let headers = {
'Content-Type': 'application/vnd.api+json;charset=UTF-8'
}
_.merge(config, { headers: headers })

return axios.$post(url, data, config)
return axios.$post(url, data, axiosConfig)
},

getMenuFields: function () {
Expand All @@ -60,17 +68,17 @@ export const tide = (axios, site, config) => ({
}
},

async getSitesData (params = {}) {
const sites = await this.get('taxonomy_term/sites', params)
async getSitesData (params = {}, headersConfig = {}) {
const sites = await this.get('taxonomy_term/sites', params, '', headersConfig)
if (typeof sites === 'undefined' || typeof sites.data === 'undefined') {
return new Error('Failed to get sites data. It can be a operation error or configuration error if it\'s the first time to setup this app.')
} else {
return this.getAllPaginatedData(sites)
}
},

async getSitesDomainMap () {
const sites = await this.getSitesData()
async getSitesDomainMap (headersConfig = {}) {
const sites = await this.getSitesData({}, headersConfig)
let sitesDomainMap = {}
let domain = ''

Expand Down Expand Up @@ -98,10 +106,10 @@ export const tide = (axios, site, config) => ({
return config && config.modules && config.modules[checkForModule] === 1
},

getSiteData: async function (tid = null) {
// TODO: this method need to be reviewed when we do SDPA-585.
// So it can support without tide_site enabled.
const siteId = tid || site
// TODO: this method need to be reviewed when we do SDPA-585.
// So it can support without tide_site enabled.
getSiteData: async function (headersConfig = {}, siteId = null) {
siteId = siteId || site
const include = [
'field_site_logo',
'field_site_footer_logos',
Expand Down Expand Up @@ -129,15 +137,15 @@ export const tide = (axios, site, config) => ({
value: siteId
}
}
const response = await this.get(`taxonomy_term/sites`, params)
const response = await this.get(`taxonomy_term/sites`, params, '', headersConfig)
if (!response || response.error) {
return new Error('Could not get site data. Please check your site id and Tide site setting.')
}
siteData = jsonapiParse.parse(response).data[0]
}

try {
siteData.menus = await this.getSiteMenus(siteData)
siteData.menus = await this.getSiteMenus(siteData, headersConfig)
} catch (error) {
if (process.server) {
logger.error('Get menus from Tide failed:', { error })
Expand All @@ -163,13 +171,13 @@ export const tide = (axios, site, config) => ({
return siteData
},

getSiteMenus: async function (siteData) {
getSiteMenus: async function (siteData, headersConfig) {
const siteMenus = {}
const menuFields = this.getMenuFields()
for (let menu in menuFields) {
if (siteData[menuFields[menu]] !== undefined) {
try {
siteMenus[menu] = await this.getMenu(siteData[menuFields[menu]].drupal_internal__id)
siteMenus[menu] = await this.getMenu(siteData[menuFields[menu]].drupal_internal__id, headersConfig)
} catch (error) {
if (process.server) {
logger.error('Get site menus error: ', { error })
Expand All @@ -186,7 +194,7 @@ export const tide = (axios, site, config) => ({
return siteMenus
},

getMenu: async function (menuName) {
getMenu: async function (menuName, headersConfig = {}) {
if (!menuName) {
throw new Error('no menu id provided.')
}
Expand All @@ -199,7 +207,7 @@ export const tide = (axios, site, config) => ({
}
}
}
const menu = await this.get('menu_link_content/menu_link_content', params)
const menu = await this.get('menu_link_content/menu_link_content', params, '', headersConfig)

return this.getAllPaginatedData(menu, false)
},
Expand Down Expand Up @@ -232,17 +240,17 @@ export const tide = (axios, site, config) => ({
}
},

getPathData: async function (path, params, authToken) {
getPathData: async function (path, params, headersConfig) {
let routeParams = { path: path }
if (!_.isEmpty(params)) {
_.merge(routeParams, params)
}

const response = await this.get('route', routeParams, '', authToken)
const response = await this.get('route', routeParams, '', headersConfig)
return response
},

getEntityByPathData: async function (pathData, query, authToken) {
getEntityByPathData: async function (pathData, query, headersConfig) {
const endpoint = `${pathData.entity_type}/${pathData.bundle}/${pathData.uuid}`

let include
Expand Down Expand Up @@ -305,13 +313,13 @@ export const tide = (axios, site, config) => ({
if (!_.isEmpty(query)) {
params = _.merge(query, params)
}
const entity = await this.get(endpoint, params, '', authToken)
const entity = await this.get(endpoint, params, '', headersConfig)
return entity
},

getPageByPath: async function (path, params, authToken) {
getPageByPath: async function (path, params, headersConfig) {
let pageData = null
const response = await this.getPathData(path, params, authToken)
const response = await this.getPathData(path, params, headersConfig)

const pathData = jsonapiParse.parse(response).data

Expand All @@ -320,7 +328,7 @@ export const tide = (axios, site, config) => ({
return pathData
}

const entity = await this.getEntityByPathData(pathData, params, authToken)
const entity = await this.getEntityByPathData(pathData, params, headersConfig)
if (!entity) {
throw new Error('Something wrong. Could not get any entity data from Tide based on API route response.')
}
Expand All @@ -331,7 +339,7 @@ export const tide = (axios, site, config) => ({
return pageData
},

getPreviewPage: async function (contentType, uuid, revisionId, section, params, authToken) {
getPreviewPage: async function (contentType, uuid, revisionId, section, params, headersConfig) {
if (revisionId === 'latest') {
params.resourceVersion = 'rel:working-copy'
} else {
Expand All @@ -343,7 +351,7 @@ export const tide = (axios, site, config) => ({
bundle: contentType,
uuid: uuid
}
const entity = await this.getEntityByPathData(pathData, params, authToken)
const entity = await this.getEntityByPathData(pathData, params, headersConfig)
const pageData = jsonapiParse.parse(entity).data

// Append the site section to page data
Expand Down
12 changes: 10 additions & 2 deletions packages/ripple-nuxt-tide/lib/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,18 @@ const nuxtTide = function (moduleOptions) {
this.addServerMiddleware(basicAuth)
}

this.addServerMiddleware(require('./server-middleware/logger'))

this.addModule('@dpc-sdp/ripple-nuxt-ui', true)

// Add request id
this.addPlugin({
src: path.resolve(__dirname, 'templates/request-id.js'),
fileName: './request-id.js'
})
this.options.router.middleware.push('request-id')
this.addServerMiddleware(require('./server-middleware/request-id'))
// Log all server side requests
this.addServerMiddleware(require('./server-middleware/request-log'))

this.options.head.htmlAttrs = this.options.head.hasOwnProperty('htmlAttrs') ? this.options.head.htmlAttrs : this.options.head.htmlAttrs = { lang: 'en' }

this.addModule('@nuxtjs/proxy', true)
Expand Down
14 changes: 14 additions & 0 deletions packages/ripple-nuxt-tide/lib/server-middleware/request-id.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Log server connection
import { generateId } from './../core/tide-helper'
const url = require('url')

module.exports = function (req, res, next) {
// req is the Node.js http request object
const reqUrl = decodeURI((url.parse(req.url)).pathname)
if (!reqUrl.includes('/api/v')) {
req.requestId = generateId()
}
// next is a function to call to invoke the next middleware
// Don't forget to call next at the end if your middleware is not an endpoint!
next()
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ module.exports = function (req, res, next) {
// req is the Node.js http request object
const status = res.statusCode
const method = req.method.toUpperCase()
const reqUrl = decodeURI((url.parse(req.url)).pathname)
if (!reqUrl.includes('api/v1/')) {
logger.info('Server got request: %s %s %s', status, method, reqUrl, { label: 'Connect' })
const reqUrl = url.parse(req.url)
const reqPath = decodeURI(reqUrl.pathname)
if (reqPath.includes('/api/v')) {
logger.info('Proxy %s %s to backend, res status code: %s.', method, reqPath, status, { label: 'Connect', requestQuery: reqUrl.query, requestId: req.headers['x-request-id'] })
} else {
logger.info('Proxy %s %s to backend, %s.', method, reqUrl, status, { label: 'Connect' })
logger.info('Server got request: %s %s %s', status, method, reqPath, { label: 'Connect', requestQuery: reqUrl.query, requestId: req.requestId })
}
// next is a function to call to invoke the next middleware
// Don't forget to call next at the end if your middleware is not an endpoint!
Expand Down
13 changes: 7 additions & 6 deletions packages/ripple-nuxt-tide/lib/templates/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default ({ env, app, req, res, store , route}, inject) => {
app.$axios.onRequest(config => {
// Log all axios' requests
if (process.server) {
logger.info('Making %s request to %s', config.method.toUpperCase(), config.url, {label: 'Axios'})
logger.info('Making %s request to %s', config.method.toUpperCase(), config.url, {label: 'Axios', requestId: config.headers['X-Request-Id']})
logger.debug('Headers %O', config.headers, {label: 'Axios'})
}
})
Expand Down Expand Up @@ -92,9 +92,9 @@ export default ({ env, app, req, res, store , route}, inject) => {
}
},
actions: {
async init ({ commit, dispatch }) {
async init ({ commit, dispatch }, { requestId = null } = {}) {
if (process.server) {
await dispatch('setSiteData')
await dispatch('setSiteData', { requestId })
commit('setHost', req.headers.host)

// Set protocol
Expand All @@ -103,16 +103,17 @@ export default ({ env, app, req, res, store , route}, inject) => {

// Load site module store.
if (config.modules.site === 1) {
await store.dispatch('tideSite/init')
await store.dispatch('tideSite/init', { requestId })
}
// Load authenticated content store.
if (config.modules.authenticatedContent === 1) {
serverSetProperties(req.headers.cookie, route.path, store)
}
}
},
async setSiteData ({ commit }) {
const siteData = await app.$tide.getSiteData()
async setSiteData ({ commit }, { requestId = null } = {}) {
const headersConfig = { requestId }
const siteData = await app.$tide.getSiteData(headersConfig)
if (siteData instanceof Error) {
throw siteData
}
Expand Down
19 changes: 19 additions & 0 deletions packages/ripple-nuxt-tide/lib/templates/request-id.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { generateId } from '@dpc-sdp/ripple-nuxt-tide/lib/core/tide-helper'
import middleware from './middleware'

middleware['request-id'] = async (context) => {
const { req, route, isHMR } = context;

if (isHMR) {
return
}

// Set request id for all Tide requests within the same route
if (req && req.requestId) {
// Nuxt req is server only variable, we pass the server request id to here.
route.requestId = req.requestId
} else {
// Generate a request id if it's a client side navigation.
route.requestId = generateId()
}
}
Loading

0 comments on commit 0840f07

Please sign in to comment.