From 64d7338470ab23eb16893999e3bec24dd024595c Mon Sep 17 00:00:00 2001 From: Kelvin Jin Date: Wed, 15 Nov 2017 18:46:07 -0800 Subject: [PATCH 1/5] refactor: add web framework plugin types and script to fetch plugin types --- .gitignore | 8 +- package-lock.json | 6 ++ package.json | 4 +- scripts/get-plugin-types.ts | 40 +++++++++ scripts/index.ts | 4 + scripts/utils.ts | 6 +- src/plugins/plugin-connect.ts | 92 ++++++++++++-------- src/plugins/plugin-express.ts | 78 ++++++++++------- src/plugins/plugin-hapi.ts | 115 ++++++++++++++----------- src/plugins/plugin-koa.ts | 157 +++++++++++++++++++++------------- src/plugins/plugin-restify.ts | 75 +++++++++------- src/plugins/types/index.d.ts | 36 ++++++++ tsconfig.full.json | 3 +- tsconfig.json | 6 ++ 14 files changed, 412 insertions(+), 218 deletions(-) create mode 100644 scripts/get-plugin-types.ts create mode 100644 src/plugins/types/index.d.ts diff --git a/.gitignore b/.gitignore index 2f5f1336e..553a5cc56 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ -*.tgz +node_modules/ +npm-debug.log .DS_Store .nyc_output/ .vscode/ build/ -node_modules/ -npm-debug.log +*.tgz +src/plugins/types/* +!src/plugins/types/index.d.ts diff --git a/package-lock.json b/package-lock.json index 1a981aeb1..783a2a5af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -333,6 +333,12 @@ "integrity": "sha512-ZXyOOm83p7X8p3s0IYM3VeueNmHpkk/yMlP8CLeOnEcu6hIwPH7YjZBvhQkR0ZFS2DqZAxKtJ/M5fcuv3OU5BA==", "dev": true }, + "@types/methods": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.0.tgz", + "integrity": "sha512-ROomEm+QHlUmcQoDr3CBo3GRm0w4PVoFYjVT9YcfyBha/Per4deb1IpvHU7KTK7YBZCIvOYbSADoEyDnFgaWLA==", + "dev": true + }, "@types/minimatch": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.1.tgz", diff --git a/package.json b/package.json index 89c82ed42..1778024d5 100644 --- a/package.json +++ b/package.json @@ -13,13 +13,14 @@ "system-test": "npm run script decrypt-service-account-credentials run-system-tests", "changelog": "./bin/run-changelog.sh", "check-install": "npm run script check-install", + "get-plugin-types": "npm run script get-plugin-types", "coverage": "npm run script run-unit-tests-with-coverage report-coverage", "bump": "./bin/run-bump.sh", "check": "gts check", "clean": "rimraf build/src", "compile-all": "tsc -p ./tsconfig.full.json", "compile-strict": "tsc -p .", - "compile": "npm run script npm-compile-all npm-compile-strict", + "compile": "npm run script get-plugin-types npm-compile-all npm-compile-strict", "fix": "gts fix", "prepare": "npm run script npm-clean npm-compile", "script": "ts-node -P ./scripts ./scripts", @@ -54,6 +55,7 @@ "@types/extend": "^3.0.0", "@types/glob": "^5.0.32", "@types/is": "0.0.18", + "@types/methods": "^1.1.0", "@types/mocha": "^2.2.44", "@types/ncp": "^2.0.1", "@types/node": "^9.3.0", diff --git a/scripts/get-plugin-types.ts b/scripts/get-plugin-types.ts new file mode 100644 index 000000000..a17aaf6d2 --- /dev/null +++ b/scripts/get-plugin-types.ts @@ -0,0 +1,40 @@ +import { flatten, globP, mkdirP, ncpP, readFileP, spawnP, tmpDirP, writeFileP } from './utils'; + +const TYPES_DIRECTORY = 'src/plugins/types'; + +async function mkdirSafeP(dir: string) { + try { + await mkdirP(dir); + return true; + } catch (e) { + if (e.code !== 'EEXIST') { + throw new Error(`Error creating directory ${dir}`); + } + return false; + } +} + +export async function getPluginTypes() { + await mkdirSafeP(TYPES_DIRECTORY); + + const indexTs = (await readFileP(`${TYPES_DIRECTORY}/index.d.ts`, 'utf8') as string) + .split('\n'); + for (const line of indexTs) { + const matches = line.match(/^import \* as .* from '\.\/(.*)';\s*\/\/\s*(.*)@(.*)$/); + if (!matches) { + continue; + } + const [_0, packageName, name, version] = matches; + const installDir = `${TYPES_DIRECTORY}/${packageName}`; + if (await mkdirSafeP(installDir)) { + await spawnP('npm', ['init', '-y'], { + cwd: installDir + }); + await spawnP('npm', ['install', `@types/${name}@${version}`], { + cwd: installDir + }); + await writeFileP(`${installDir}/index.ts`, + `import * as _ from '${name}'; export = _;\n`, 'utf8'); + } + } +} diff --git a/scripts/index.ts b/scripts/index.ts index 4c5a6aeba..799d29bf7 100644 --- a/scripts/index.ts +++ b/scripts/index.ts @@ -10,6 +10,7 @@ const [bin, script, ...steps] = process.argv; import { checkInstall } from './check-install'; import { encryptCredentials, decryptCredentials } from './credentials'; +import { getPluginTypes } from './get-plugin-types'; import { initTestFixtures } from './init-test-fixtures'; import { reportCoverage } from './report-coverage'; import { runTests } from './run-tests'; @@ -63,6 +64,9 @@ async function run(steps: string[]) { await decryptCredentials({ key, iv }, `node-team-test-${keyID}.json`); break; + case 'get-plugin-types': + await getPluginTypes(); + break; case 'init-test-fixtures': await initTestFixtures(); break; diff --git a/scripts/utils.ts b/scripts/utils.ts index c20defa5e..39432c41a 100644 --- a/scripts/utils.ts +++ b/scripts/utils.ts @@ -1,4 +1,4 @@ -import { Stats, stat, readFile, writeFile, mkdir } from 'fs'; +import { mkdir, Stats, stat, readFile, writeFile } from 'fs'; import * as glob from 'glob'; import { ncp } from 'ncp'; import * as path from 'path'; @@ -21,6 +21,10 @@ export function nodule(nodule: string) { return path.relative(BUILD_DIRECTORY, `node_modules/${nodule}`); } +export function flatten(arr: Array>): Array { + return arr.reduce((acc, e) => acc.concat(e), []); +} + export function existsP(path: string): Promise { return statP(path).then( () => Promise.resolve(true), diff --git a/src/plugins/plugin-connect.ts b/src/plugins/plugin-connect.ts index 19471a61a..45d860383 100644 --- a/src/plugins/plugin-connect.ts +++ b/src/plugins/plugin-connect.ts @@ -13,26 +13,50 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -'use strict'; -var urlParse = require('url').parse; +// tslint:disable-next-line:no-reference +/// -var SUPPORTED_VERSIONS = '3.x'; +import {IncomingMessage, ServerResponse} from 'http'; +import * as shimmer from 'shimmer'; +import {parse as urlParse} from 'url'; -function createMiddleware(api) { - return function middleware(req, res, next) { - var options = { - name: urlParse(req.originalUrl).pathname, +import {PluginTypes} from '..'; + +import {connect_3} from './types'; + +type Connect3 = typeof connect_3; +// Connect docs note that routed requests have an originalUrl property. +// https://github.com/senchalabs/connect/tree/3.6.5#appuseroute-fn +type Request = IncomingMessage&{originalUrl?: string}; + +const SUPPORTED_VERSIONS = '3.x'; + +function getFirstHeader(req: IncomingMessage, key: string) { + let headerValue = req.headers[key]; + if (headerValue && typeof headerValue !== 'string') { + headerValue = headerValue[0]; + } + return headerValue; +} + +function createMiddleware(api: PluginTypes.TraceAgent): + connect_3.NextHandleFunction { + return function middleware(req: Request, res, next) { + const options = { + name: req.originalUrl ? (urlParse(req.originalUrl).pathname || '') : '', url: req.originalUrl, - traceContext: req.headers[api.constants.TRACE_CONTEXT_HEADER_NAME.toLowerCase()], + traceContext: + getFirstHeader(req, api.constants.TRACE_CONTEXT_HEADER_NAME), skipFrames: 3 }; - api.runInRootSpan(options, function(root) { + api.runInRootSpan(options, (root) => { // Set response trace context. - var responseTraceContext = - api.getResponseTraceContext(options.traceContext, !!root); + const responseTraceContext = + api.getResponseTraceContext(options.traceContext || null, !!root); if (responseTraceContext) { - res.setHeader(api.constants.TRACE_CONTEXT_HEADER_NAME, responseTraceContext); + res.setHeader( + api.constants.TRACE_CONTEXT_HEADER_NAME, responseTraceContext); } if (!root) { @@ -42,8 +66,8 @@ function createMiddleware(api) { api.wrapEmitter(req); api.wrapEmitter(res); - var url = (req.headers['X-Forwarded-Proto'] || 'http') + - '://' + req.headers.host + req.originalUrl; + const url = (req.headers['X-Forwarded-Proto'] || 'http') + '://' + + req.headers.host + req.originalUrl; // we use the path part of the url as the span name and add the full // url as a label @@ -52,18 +76,13 @@ function createMiddleware(api) { root.addLabel(api.labels.HTTP_SOURCE_IP, req.connection.remoteAddress); // wrap end - var originalEnd = res.end; - res.end = function() { + const originalEnd = res.end; + res.end = function(this: ServerResponse) { res.end = originalEnd; - var returned = res.end.apply(this, arguments); - - if (req.route && req.route.path) { - root.addLabel( - 'connect/request.route.path', req.route.path); - } + const returned = res.end.apply(this, arguments); - root.addLabel( - api.labels.HTTP_RESPONSE_CODE_LABEL_KEY, res.statusCode); + root.addLabel('connect/request.route.path', req.originalUrl); + root.addLabel(api.labels.HTTP_RESPONSE_CODE_LABEL_KEY, res.statusCode); root.endSpan(); return returned; @@ -74,19 +93,16 @@ function createMiddleware(api) { }; } -module.exports = [ - { - file: '', - versions: SUPPORTED_VERSIONS, - intercept: function(connect, api) { - return function() { - var app = connect(); - app.use(createMiddleware(api)); - return app; - }; - } +const plugin: PluginTypes.Plugin = [{ + file: '', + versions: SUPPORTED_VERSIONS, + intercept: (connect, api) => { + return function(this: {}) { + const app = connect(); + app.use(createMiddleware(api)); + return app; + }; } -]; - +} as PluginTypes.Intercept]; -export default {}; +export = plugin; diff --git a/src/plugins/plugin-express.ts b/src/plugins/plugin-express.ts index c4199bf03..3bc673060 100644 --- a/src/plugins/plugin-express.ts +++ b/src/plugins/plugin-express.ts @@ -14,35 +14,36 @@ * limitations under the License. */ -'use strict'; -var shimmer = require('shimmer'); -var methods = require('methods').concat('use', 'route', 'param', 'all'); +// tslint:disable-next-line:no-reference +/// -var SUPPORTED_VERSIONS = '4.x'; +import * as httpMethods from 'methods'; +import * as shimmer from 'shimmer'; -function patchModuleRoot(express, api) { - var labels = api.labels; - function applicationActionWrap(method) { - return function expressActionTrace() { - if (!this._google_trace_patched && !this._router) { - this._google_trace_patched = true; - this.use(middleware); - } - return method.apply(this, arguments); - }; - } +import {PluginTypes} from '..'; + +import {express_4} from './types'; + +// application is an undocumented member of the express object. +type Express4Module = typeof express_4&{application: express_4.Application}; - function middleware(req, res, next) { - var options = { +const methods = httpMethods.concat('use', 'route', 'param', 'all'); + +const SUPPORTED_VERSIONS = '4.x'; + +function patchModuleRoot(express: Express4Module, api: PluginTypes.TraceAgent) { + const labels = api.labels; + const middleware: express_4.RequestHandler = (req, res, next) => { + const options: PluginTypes.RootSpanOptions = { name: req.path, traceContext: req.get(api.constants.TRACE_CONTEXT_HEADER_NAME), url: req.originalUrl, skipFrames: 3 }; - api.runInRootSpan(options, function(rootSpan) { + api.runInRootSpan(options, (rootSpan) => { // Set response trace context. - var responseTraceContext = - api.getResponseTraceContext(options.traceContext, !!rootSpan); + const responseTraceContext = + api.getResponseTraceContext(options.traceContext || null, !!rootSpan); if (responseTraceContext) { res.set(api.constants.TRACE_CONTEXT_HEADER_NAME, responseTraceContext); } @@ -55,16 +56,16 @@ function patchModuleRoot(express, api) { api.wrapEmitter(req); api.wrapEmitter(res); - var url = req.protocol + '://' + req.hostname + req.originalUrl; + const url = req.protocol + '://' + req.hostname + req.originalUrl; rootSpan.addLabel(labels.HTTP_METHOD_LABEL_KEY, req.method); rootSpan.addLabel(labels.HTTP_URL_LABEL_KEY, url); rootSpan.addLabel(labels.HTTP_SOURCE_IP, req.connection.remoteAddress); // wrap end - var originalEnd = res.end; - res.end = function() { + const originalEnd = res.end; + res.end = function(this: express_4.Response) { res.end = originalEnd; - var returned = res.end.apply(this, arguments); + const returned = res.end.apply(this, arguments); if (req.route && req.route.path) { rootSpan.addLabel('express/request.route.path', req.route.path); @@ -76,23 +77,34 @@ function patchModuleRoot(express, api) { next(); }); + }; + + function applicationActionWrap(method: T): () => T { + return function expressActionTrace(this: express_4.Application& + PluginTypes.TraceAgentExtension) { + if (!this._google_trace_patched && !this._router) { + this._google_trace_patched = true; + this.use(middleware); + } + return method.apply(this, arguments); + }; } - methods.forEach(function(method) { + methods.forEach((method) => { shimmer.wrap(express.application, method, applicationActionWrap); }); } -function unpatchModuleRoot(express) { - methods.forEach(function(method) { +function unpatchModuleRoot(express: Express4Module) { + methods.forEach((method) => { shimmer.unwrap(express.application, method); }); } -module.exports = [{ - versions: SUPPORTED_VERSIONS, - patch: patchModuleRoot, - unpatch: unpatchModuleRoot -}]; +const plugin: PluginTypes.Plugin = [{ + versions: SUPPORTED_VERSIONS, + patch: patchModuleRoot, + unpatch: unpatchModuleRoot +} as PluginTypes.Patch]; -export default {}; +export = plugin; diff --git a/src/plugins/plugin-hapi.ts b/src/plugins/plugin-hapi.ts index 95c7ff05e..1e1a29651 100644 --- a/src/plugins/plugin-hapi.ts +++ b/src/plugins/plugin-hapi.ts @@ -13,40 +13,51 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -'use strict'; -var shimmer = require('shimmer'); -var urlParse = require('url').parse; +// TODO(kjin): Remove this when @types/shimmer are published. +// tslint:disable-next-line:no-reference +/// -var SUPPORTED_VERSIONS = '8 - 16'; +import {IncomingMessage, ServerResponse} from 'http'; +import * as shimmer from 'shimmer'; +import {parse as urlParse} from 'url'; -function createConnectionWrap(api) { - return function connectionWrap(connection) { - return function connectionTrace() { - var server = connection.apply(this, arguments); - server.ext('onRequest', createMiddleware(api)); - return server; - }; - }; +import {PluginTypes} from '..'; + +import {hapi_16} from './types'; + +type Hapi16Module = typeof hapi_16; + +const SUPPORTED_VERSIONS = '8 - 16'; + +function getFirstHeader(req: IncomingMessage, key: string) { + let headerValue = req.headers[key]; + if (headerValue && typeof headerValue !== 'string') { + headerValue = headerValue[0]; + } + return headerValue; } -function createMiddleware(api) { +function createMiddleware(api: PluginTypes.TraceAgent): + hapi_16.ServerExtRequestHandler { return function middleware(request, reply) { - var req = request.raw.req; - var res = request.raw.res; - var originalEnd = res.end; - var options = { - name: urlParse(req.url).pathname, + const req = request.raw.req; + const res = request.raw.res; + const originalEnd = res.end; + const options: PluginTypes.RootSpanOptions = { + name: req.url ? (urlParse(req.url).pathname || '') : '', url: req.url, - traceContext: req.headers[api.constants.TRACE_CONTEXT_HEADER_NAME], + traceContext: + getFirstHeader(req, api.constants.TRACE_CONTEXT_HEADER_NAME), skipFrames: 3 }; - api.runInRootSpan(options, function(root) { + api.runInRootSpan(options, (root) => { // Set response trace context. - var responseTraceContext = - api.getResponseTraceContext(options.traceContext, !!root); + const responseTraceContext = + api.getResponseTraceContext(options.traceContext || null, !!root); if (responseTraceContext) { - res.setHeader(api.constants.TRACE_CONTEXT_HEADER_NAME, responseTraceContext); + res.setHeader( + api.constants.TRACE_CONTEXT_HEADER_NAME, responseTraceContext); } if (!root) { @@ -56,36 +67,36 @@ function createMiddleware(api) { api.wrapEmitter(req); api.wrapEmitter(res); - var url = (req.headers['X-Forwarded-Proto'] || 'http') + - '://' + req.headers.host + req.url; - + const url = (req.headers['X-Forwarded-Proto'] || 'http') + '://' + + req.headers.host + req.url; + // we use the path part of the url as the span name and add the full // url as a label - // req.path would be more desirable but is not set at the time our middleware runs. + // req.path would be more desirable but is not set at the time our + // middleware runs. root.addLabel(api.labels.HTTP_METHOD_LABEL_KEY, req.method); root.addLabel(api.labels.HTTP_URL_LABEL_KEY, url); root.addLabel(api.labels.HTTP_SOURCE_IP, req.connection.remoteAddress); // wrap end - res.end = function() { + res.end = function(this: ServerResponse) { res.end = originalEnd; - var returned = res.end.apply(this, arguments); + const returned = res.end.apply(this, arguments); - if (req.route && req.route.path) { - root.addLabel( - 'hapi/request.route.path', req.route.path); + if (request.route && request.route.path) { + root.addLabel('hapi/request.route.path', request.route.path); } - root.addLabel( - api.labels.HTTP_RESPONSE_CODE_LABEL_KEY, res.statusCode); + root.addLabel(api.labels.HTTP_RESPONSE_CODE_LABEL_KEY, res.statusCode); root.endSpan(); return returned; }; // if the event is aborted, end the span (as res.end will not be called) - req.once('aborted', function() { + req.once('aborted', () => { root.addLabel(api.labels.ERROR_DETAILS_NAME, 'aborted'); - root.addLabel(api.labels.ERROR_DETAILS_MESSAGE, 'client aborted the request'); + root.addLabel( + api.labels.ERROR_DETAILS_MESSAGE, 'client aborted the request'); root.endSpan(); }); @@ -94,19 +105,21 @@ function createMiddleware(api) { }; } -module.exports = [ - { - file: '', - versions: SUPPORTED_VERSIONS, - patch: function(hapi, api) { - shimmer.wrap(hapi.Server.prototype, - 'connection', - createConnectionWrap(api)); - }, - unpatch: function(hapi) { - shimmer.unwrap(hapi.Server.prototype, 'connection'); - } +const plugin: PluginTypes.Plugin = [{ + file: '', + versions: SUPPORTED_VERSIONS, + patch: (hapi, api) => { + shimmer.wrap( + hapi.Server.prototype, 'connection', (connection) => { + return function connectionTrace(this: {}) { + const server: hapi_16.Server = connection.apply(this, arguments); + server.ext('onRequest', createMiddleware(api)); + return server; + }; + }); + }, + unpatch: (hapi) => { + shimmer.unwrap(hapi.Server.prototype, 'connection'); } -]; - -export default {}; +} as PluginTypes.Patch]; +export = plugin; diff --git a/src/plugins/plugin-koa.ts b/src/plugins/plugin-koa.ts index b95aad7e7..3184d2539 100644 --- a/src/plugins/plugin-koa.ts +++ b/src/plugins/plugin-koa.ts @@ -13,134 +13,169 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -'use strict'; -const shimmer = require('shimmer'); -const urlParse = require('url').parse; +// TODO(kjin): Remove this when @types/shimmer are published. +// tslint:disable-next-line:no-reference +/// -function startSpanForRequest(api, req, res, next) { +import {IncomingMessage, ServerResponse} from 'http'; +import * as shimmer from 'shimmer'; +import {parse as urlParse} from 'url'; + +import {PluginTypes} from '..'; + +import {koa_1, koa_2} from './types'; + +type Koa1Module = typeof koa_1; +type Koa2Module = typeof koa_2; +// routePath is populated if the user uses the koa-route module. +type KoaContext = (koa_1.Context|koa_2.Context)&{routePath?: string | RegExp}; + +interface KoaModule { + // TypeScript isn't expressive enough, but KoaModule#use should return `this`. + // tslint:disable-next-line:no-any + readonly prototype: {use: (m: T) => any}; +} + +// Function signature for createMiddleware[2x] +type CreateMiddlewareFn = (api: PluginTypes.TraceAgent) => T; +// Function signature for a function that returns the value of the "next" +// middleware function parameter, wrapped to propagate context based on the +// propagateContext flag. The type of "next" differs between Koa 1 and 2. +type GetNextFn = (propagateContext: boolean) => T; + +function getFirstHeader(req: IncomingMessage, key: string) { + let headerValue = req.headers[key]; + if (headerValue && typeof headerValue !== 'string') { + headerValue = headerValue[0]; + } + return headerValue; +} + +function startSpanForRequest( + api: PluginTypes.TraceAgent, ctx: KoaContext, getNext: GetNextFn): T { + const {req, res} = ctx; const originalEnd = res.end; const options = { - name: urlParse(req.url).pathname, + name: req.url ? (urlParse(req.url).pathname || '') : '', url: req.url, - traceContext: req.headers[api.constants.TRACE_CONTEXT_HEADER_NAME], + traceContext: getFirstHeader(req, api.constants.TRACE_CONTEXT_HEADER_NAME), skipFrames: 4 }; - return api.runInRootSpan(options, function(root) { + return api.runInRootSpan(options, root => { // Set response trace context. const responseTraceContext = - api.getResponseTraceContext(options.traceContext, !!root); + api.getResponseTraceContext(options.traceContext || null, !!root); if (responseTraceContext) { - res.setHeader(api.constants.TRACE_CONTEXT_HEADER_NAME, responseTraceContext); + res.setHeader( + api.constants.TRACE_CONTEXT_HEADER_NAME, responseTraceContext); } - + if (!root) { - return next; + return getNext(false); } api.wrapEmitter(req); api.wrapEmitter(res); - const url = (req.headers['X-Forwarded-Proto'] || 'http') + - '://' + req.headers.host + req.url; + const url = (req.headers['X-Forwarded-Proto'] || 'http') + '://' + + req.headers.host + req.url; // we use the path part of the url as the span name and add the full // url as a label - // req.path would be more desirable but is not set at the time our middlewear runs. + // req.path would be more desirable but is not set at the time our + // middlewear runs. root.addLabel(api.labels.HTTP_METHOD_LABEL_KEY, req.method); root.addLabel(api.labels.HTTP_URL_LABEL_KEY, url); root.addLabel(api.labels.HTTP_SOURCE_IP, req.connection.remoteAddress); // wrap end - res.end = function() { + res.end = function(this: ServerResponse) { res.end = originalEnd; const returned = res.end.apply(this, arguments); - if (req.route && req.route.path) { - root.addLabel( - 'koa/request.route.path', req.route.path); + if (ctx.routePath) { + root.addLabel('koa/request.route.path', ctx.routePath); } - root.addLabel( - api.labels.HTTP_RESPONSE_CODE_LABEL_KEY, res.statusCode); + root.addLabel(api.labels.HTTP_RESPONSE_CODE_LABEL_KEY, res.statusCode); root.endSpan(); return returned; }; // if the event is aborted, end the span (as res.end will not be called) - req.once('aborted', function() { + req.once('aborted', () => { root.addLabel(api.labels.ERROR_DETAILS_NAME, 'aborted'); - root.addLabel(api.labels.ERROR_DETAILS_MESSAGE, 'client aborted the request'); + root.addLabel( + api.labels.ERROR_DETAILS_MESSAGE, 'client aborted the request'); root.endSpan(); }); - // In Koa 1, next is a Generator object. - // We wrap Generator#next here. - if (!next.apply && next.next) { - next.next = api.wrap(next.next); - return next; - } else { - return api.wrap(next); - } + return getNext(true); }); } -function createMiddleware(api) { - return function* middleware(next) { - /* jshint validthis:true */ - const req = this.req; - const res = this.res; - - next = startSpanForRequest(api, req, res, next); - +function createMiddleware(api: PluginTypes.TraceAgent): koa_1.Middleware { + // Koa 1 type definitions use any here. + // tslint:disable-next-line:no-any + return function* middleware(this: koa_1.Context, next: IterableIterator<{}>) { + next = startSpanForRequest(api, this, (propagateContext: boolean) => { + if (propagateContext) { + next.next = api.wrap(next.next); + } + return next; + }); yield next; }; } -function createMiddleware2x(api) { +function createMiddleware2x(api: PluginTypes.TraceAgent): koa_2.Middleware { return function middleware(ctx, next) { - const req = ctx.req; - const res = ctx.res; - - next = startSpanForRequest(api, req, res, next); - + next = startSpanForRequest( + api, ctx, + (propagateContext: boolean) => + propagateContext ? api.wrap(next) : next); return next(); }; } -function patchUse(koa, api, createMiddlewareFunction) { - shimmer.wrap(koa.prototype, 'use', function useWrap(use) { - return function useTrace() { - if (!this._google_trace_patched) { - this._google_trace_patched = true; - this.use(createMiddlewareFunction(api)); - } - return use.apply(this, arguments); - }; +function patchUse( + koa: KoaModule, api: PluginTypes.TraceAgent, + createMiddlewareFunction: CreateMiddlewareFn) { + shimmer.wrap(koa.prototype, 'use', (use) => { + return function useTrace(this: typeof koa.prototype& + PluginTypes.TraceAgentExtension): + typeof koa.prototype { + if (!this._google_trace_patched) { + this._google_trace_patched = true; + this.use(createMiddlewareFunction(api)); + } + return use.apply(this, arguments); + }; }); } -module.exports = [ +const plugin: PluginTypes.Plugin = [ { file: '', versions: '1.x', - patch: function(koa, api) { + patch: (koa, api) => { patchUse(koa, api, createMiddleware); }, - unpatch: function(koa) { + unpatch: (koa) => { shimmer.unwrap(koa.prototype, 'use'); } - }, + } as PluginTypes.Patch, { file: '', versions: '2.x', - patch: function(koa, api) { + patch: (koa, api) => { patchUse(koa, api, createMiddleware2x); }, - unpatch: function(koa) { + unpatch: (koa) => { shimmer.unwrap(koa.prototype, 'use'); } - } + } as PluginTypes.Patch ]; -export default {}; +export = plugin; diff --git a/src/plugins/plugin-restify.ts b/src/plugins/plugin-restify.ts index adc552c63..a4e5c3898 100644 --- a/src/plugins/plugin-restify.ts +++ b/src/plugins/plugin-restify.ts @@ -14,43 +14,58 @@ * limitations under the License. */ -'use strict'; +// TODO(kjin): Remove this when @types/shimmer are published. +// tslint:disable-next-line:no-reference +/// -var shimmer = require('shimmer'); +import {ServerResponse} from 'http'; +import * as shimmer from 'shimmer'; +import {parse as urlParse} from 'url'; -var SUPPORTED_VERSIONS = '<=6.x'; +import {PluginTypes} from '..'; -function unpatchRestify(restify) { +import {restify_5} from './types'; + +type Restify5 = typeof restify_5; +type Request = restify_5.Request&{route?: {path: string | RegExp}}; +type Response = restify_5.Response; +type Next = restify_5.Next; +type CreateServerFn = (options?: restify_5.ServerOptions) => restify_5.Server; + +const SUPPORTED_VERSIONS = '<=6.x'; + +function unpatchRestify(restify: Restify5) { shimmer.unwrap(restify, 'createServer'); } -function patchRestify(restify, api) { - shimmer.wrap(restify, 'createServer', createServerWrap); +function patchRestify(restify: Restify5, api: PluginTypes.TraceAgent) { + shimmer.wrap(restify, 'createServer', createServerWrap); - function createServerWrap(createServer) { - return function createServerTrace() { - var server = createServer.apply(this, arguments); + function createServerWrap(createServer: CreateServerFn): CreateServerFn { + return function createServerTrace(this: {}) { + const server = createServer.apply(this, arguments) as restify_5.Server; server.use(middleware); return server; }; } - function middleware(req, res, next) { - var options = { + function middleware(req: Request, res: Response, next: Next): void { + const options = { // we use the path part of the url as the span name and add the full url // as a label later. name: req.path(), url: req.url, - traceContext: req.header(api.constants.TRACE_CONTEXT_HEADER_NAME, null), + traceContext: req.header(api.constants.TRACE_CONTEXT_HEADER_NAME), skipFrames: 3 }; - api.runInRootSpan(options, function(rootSpan) { + api.runInRootSpan(options, rootSpan => { // Set response trace context. - var responseTraceContext = - api.getResponseTraceContext(options.traceContext, !!rootSpan); + const responseTraceContext = + api.getResponseTraceContext(options.traceContext, !!rootSpan); if (responseTraceContext) { - res.header(api.constants.TRACE_CONTEXT_HEADER_NAME, responseTraceContext); + res.header( + api.constants.TRACE_CONTEXT_HEADER_NAME, responseTraceContext); } if (!rootSpan) { @@ -60,23 +75,23 @@ function patchRestify(restify, api) { api.wrapEmitter(req); api.wrapEmitter(res); - var fullUrl = req.header('X-Forwarded-Proto', 'http') + '://' + - req.header('host') + req.url; + const fullUrl = req.header('X-Forwarded-Proto', 'http') + '://' + + req.header('host') + req.url; rootSpan.addLabel(api.labels.HTTP_METHOD_LABEL_KEY, req.method); rootSpan.addLabel(api.labels.HTTP_URL_LABEL_KEY, fullUrl); - rootSpan.addLabel(api.labels.HTTP_SOURCE_IP, - req.connection.remoteAddress); + rootSpan.addLabel( + api.labels.HTTP_SOURCE_IP, req.connection.remoteAddress); - var originalEnd = res.end; - res.end = function() { + const originalEnd = res.end; + res.end = function(this: ServerResponse) { res.end = originalEnd; - var returned = res.end.apply(this, arguments); + const returned = res.end.apply(this, arguments); if (req.route && req.route.path) { rootSpan.addLabel('restify/request.route.path', req.route.path); } - rootSpan.addLabel(api.labels.HTTP_RESPONSE_CODE_LABEL_KEY, - res.statusCode); + rootSpan.addLabel( + api.labels.HTTP_RESPONSE_CODE_LABEL_KEY, res.statusCode); rootSpan.endSpan(); return returned; }; @@ -86,8 +101,10 @@ function patchRestify(restify, api) { } } -module.exports = [ - {versions: SUPPORTED_VERSIONS, patch: patchRestify, unpatch: unpatchRestify} -]; +const plugin: PluginTypes.Plugin = [{ + versions: SUPPORTED_VERSIONS, + patch: patchRestify, + unpatch: unpatchRestify +} as PluginTypes.Patch]; -export default {}; +export = plugin; diff --git a/src/plugins/types/index.d.ts b/src/plugins/types/index.d.ts new file mode 100644 index 000000000..95587b8ff --- /dev/null +++ b/src/plugins/types/index.d.ts @@ -0,0 +1,36 @@ +import * as connect_3 from './connect_3'; // connect@3 +import * as express_4 from './express_4'; // express@4 +import * as hapi_16 from './hapi_16'; // hapi@16 +import * as koa_2 from './koa_2'; // koa@2 +import * as restify_5 from './restify_5'; // restify@5 + +//---koa@1---// + +declare class koa_1 { + use(middleware: koa_1.Middleware): this; +} + +declare namespace koa_1 { + interface Middleware { + (this: Context, next: IterableIterator): any; + } + + // Koa 1 and 2 differ primarily in the middleware passed to Koa#use. + // For our purposes we can borrow type definitions from Koa 2. + // + // References: + // https://github.com/koajs/koa/issues/533 + // https://github.com/koajs/koa/blob/master/History.md#200-alpha1--2015-10-22 + interface Context extends koa_2.Context {} +} + +//---exports---// + +export { + connect_3, + express_4, + hapi_16, + koa_1, + koa_2, + restify_5 +}; diff --git a/tsconfig.full.json b/tsconfig.full.json index 81c95cfcf..336e867a2 100644 --- a/tsconfig.full.json +++ b/tsconfig.full.json @@ -11,7 +11,8 @@ "noEmitOnError": false }, "include": [ - "src/**/*.ts", + "src/*.ts", + "src/plugins/*.ts", "test/*.ts", "test/plugins/*.ts" ] diff --git a/tsconfig.json b/tsconfig.json index 380d5db49..4f2d7e998 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,12 @@ "src/plugins/plugin-http.ts", "src/plugins/plugin-http2.ts", "src/plugins/plugin-https.ts", + "src/plugins/plugin-connect.ts", + "src/plugins/plugin-express.ts", + "src/plugins/plugin-hapi.ts", + "src/plugins/plugin-https.ts", + "src/plugins/plugin-koa.ts", + "src/plugins/plugin-restify.ts", "test/plugins/test-trace-http2.ts" ] } From a2ba7cc1d7e4623c90ff05b7634667a3ba3dfc89 Mon Sep 17 00:00:00 2001 From: Kelvin Jin Date: Tue, 5 Dec 2017 17:11:41 -0800 Subject: [PATCH 2/5] ci: fix ci failure --- scripts/get-plugin-types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/get-plugin-types.ts b/scripts/get-plugin-types.ts index a17aaf6d2..539219969 100644 --- a/scripts/get-plugin-types.ts +++ b/scripts/get-plugin-types.ts @@ -34,7 +34,7 @@ export async function getPluginTypes() { cwd: installDir }); await writeFileP(`${installDir}/index.ts`, - `import * as _ from '${name}'; export = _;\n`, 'utf8'); + `import * as _ from '${name}';\nexport = _;\n`, 'utf8'); } } } From 004ed70cbab0f57a853ae44723864e99c6cc0170 Mon Sep 17 00:00:00 2001 From: Kelvin Jin Date: Tue, 12 Dec 2017 12:02:27 -0800 Subject: [PATCH 3/5] fix koa plugin in node 4 and express function shenanigans --- src/plugins/plugin-express.ts | 6 ++++-- src/plugins/plugin-koa.ts | 3 ++- tsconfig.json | 5 ++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/plugins/plugin-express.ts b/src/plugins/plugin-express.ts index 3bc673060..fdb0ad23f 100644 --- a/src/plugins/plugin-express.ts +++ b/src/plugins/plugin-express.ts @@ -33,7 +33,9 @@ const SUPPORTED_VERSIONS = '4.x'; function patchModuleRoot(express: Express4Module, api: PluginTypes.TraceAgent) { const labels = api.labels; - const middleware: express_4.RequestHandler = (req, res, next) => { + function middleware( + req: express_4.Request, res: express_4.Response, + next: express_4.NextFunction) { const options: PluginTypes.RootSpanOptions = { name: req.path, traceContext: req.get(api.constants.TRACE_CONTEXT_HEADER_NAME), @@ -77,7 +79,7 @@ function patchModuleRoot(express: Express4Module, api: PluginTypes.TraceAgent) { next(); }); - }; + } function applicationActionWrap(method: T): () => T { return function expressActionTrace(this: express_4.Application& diff --git a/src/plugins/plugin-koa.ts b/src/plugins/plugin-koa.ts index 3184d2539..b0abd0856 100644 --- a/src/plugins/plugin-koa.ts +++ b/src/plugins/plugin-koa.ts @@ -54,7 +54,8 @@ function getFirstHeader(req: IncomingMessage, key: string) { function startSpanForRequest( api: PluginTypes.TraceAgent, ctx: KoaContext, getNext: GetNextFn): T { - const {req, res} = ctx; + const req = ctx.req; + const res = ctx.res; const originalEnd = res.end; const options = { name: req.url ? (urlParse(req.url).pathname || '') : '', diff --git a/tsconfig.json b/tsconfig.json index 4f2d7e998..2724163aa 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,12 +9,11 @@ }, "include": [ "src/*.ts", - "src/plugins/plugin-http.ts", - "src/plugins/plugin-http2.ts", - "src/plugins/plugin-https.ts", "src/plugins/plugin-connect.ts", "src/plugins/plugin-express.ts", "src/plugins/plugin-hapi.ts", + "src/plugins/plugin-http.ts", + "src/plugins/plugin-http2.ts", "src/plugins/plugin-https.ts", "src/plugins/plugin-koa.ts", "src/plugins/plugin-restify.ts", From b04a4fad00206bf1879eeb342e87a161b06ba9c9 Mon Sep 17 00:00:00 2001 From: Kelvin Jin Date: Fri, 19 Jan 2018 11:55:23 -0800 Subject: [PATCH 4/5] more conservative approach to plugin types --- .gitignore | 2 -- package.json | 8 ++++++-- scripts/get-plugin-types.ts | 40 ------------------------------------ scripts/index.ts | 4 ---- src/plugins/types/index.d.ts | 10 ++++----- 5 files changed, 11 insertions(+), 53 deletions(-) delete mode 100644 scripts/get-plugin-types.ts diff --git a/.gitignore b/.gitignore index 553a5cc56..384b0c916 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,3 @@ npm-debug.log .vscode/ build/ *.tgz -src/plugins/types/* -!src/plugins/types/index.d.ts diff --git a/package.json b/package.json index 1778024d5..94ca28571 100644 --- a/package.json +++ b/package.json @@ -13,14 +13,13 @@ "system-test": "npm run script decrypt-service-account-credentials run-system-tests", "changelog": "./bin/run-changelog.sh", "check-install": "npm run script check-install", - "get-plugin-types": "npm run script get-plugin-types", "coverage": "npm run script run-unit-tests-with-coverage report-coverage", "bump": "./bin/run-bump.sh", "check": "gts check", "clean": "rimraf build/src", "compile-all": "tsc -p ./tsconfig.full.json", "compile-strict": "tsc -p .", - "compile": "npm run script get-plugin-types npm-compile-all npm-compile-strict", + "compile": "npm run script npm-compile-all npm-compile-strict", "fix": "gts fix", "prepare": "npm run script npm-clean npm-compile", "script": "ts-node -P ./scripts ./scripts", @@ -51,10 +50,14 @@ }, "devDependencies": { "@google-cloud/datastore": "^1.0.2", + "@types/connect": "^3.4.31", "@types/continuation-local-storage": "^3.2.1", + "@types/express": "^4.11.0", "@types/extend": "^3.0.0", "@types/glob": "^5.0.32", + "@types/hapi": "^16.1.12", "@types/is": "0.0.18", + "@types/koa": "^2.0.43", "@types/methods": "^1.1.0", "@types/mocha": "^2.2.44", "@types/ncp": "^2.0.1", @@ -63,6 +66,7 @@ "@types/pify": "^3.0.0", "@types/proxyquire": "^1.3.28", "@types/request": "^2.0.8", + "@types/restify": "^5.0.7", "@types/semver": "^5.4.0", "@types/shimmer": "^1.0.0", "@types/tmp": "0.0.33", diff --git a/scripts/get-plugin-types.ts b/scripts/get-plugin-types.ts deleted file mode 100644 index 539219969..000000000 --- a/scripts/get-plugin-types.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { flatten, globP, mkdirP, ncpP, readFileP, spawnP, tmpDirP, writeFileP } from './utils'; - -const TYPES_DIRECTORY = 'src/plugins/types'; - -async function mkdirSafeP(dir: string) { - try { - await mkdirP(dir); - return true; - } catch (e) { - if (e.code !== 'EEXIST') { - throw new Error(`Error creating directory ${dir}`); - } - return false; - } -} - -export async function getPluginTypes() { - await mkdirSafeP(TYPES_DIRECTORY); - - const indexTs = (await readFileP(`${TYPES_DIRECTORY}/index.d.ts`, 'utf8') as string) - .split('\n'); - for (const line of indexTs) { - const matches = line.match(/^import \* as .* from '\.\/(.*)';\s*\/\/\s*(.*)@(.*)$/); - if (!matches) { - continue; - } - const [_0, packageName, name, version] = matches; - const installDir = `${TYPES_DIRECTORY}/${packageName}`; - if (await mkdirSafeP(installDir)) { - await spawnP('npm', ['init', '-y'], { - cwd: installDir - }); - await spawnP('npm', ['install', `@types/${name}@${version}`], { - cwd: installDir - }); - await writeFileP(`${installDir}/index.ts`, - `import * as _ from '${name}';\nexport = _;\n`, 'utf8'); - } - } -} diff --git a/scripts/index.ts b/scripts/index.ts index 799d29bf7..4c5a6aeba 100644 --- a/scripts/index.ts +++ b/scripts/index.ts @@ -10,7 +10,6 @@ const [bin, script, ...steps] = process.argv; import { checkInstall } from './check-install'; import { encryptCredentials, decryptCredentials } from './credentials'; -import { getPluginTypes } from './get-plugin-types'; import { initTestFixtures } from './init-test-fixtures'; import { reportCoverage } from './report-coverage'; import { runTests } from './run-tests'; @@ -64,9 +63,6 @@ async function run(steps: string[]) { await decryptCredentials({ key, iv }, `node-team-test-${keyID}.json`); break; - case 'get-plugin-types': - await getPluginTypes(); - break; case 'init-test-fixtures': await initTestFixtures(); break; diff --git a/src/plugins/types/index.d.ts b/src/plugins/types/index.d.ts index 95587b8ff..80b8082db 100644 --- a/src/plugins/types/index.d.ts +++ b/src/plugins/types/index.d.ts @@ -1,8 +1,8 @@ -import * as connect_3 from './connect_3'; // connect@3 -import * as express_4 from './express_4'; // express@4 -import * as hapi_16 from './hapi_16'; // hapi@16 -import * as koa_2 from './koa_2'; // koa@2 -import * as restify_5 from './restify_5'; // restify@5 +import * as connect_3 from 'connect'; // connect@3 +import * as express_4 from 'express'; // express@4 +import * as hapi_16 from 'hapi'; // hapi@16 +import * as koa_2 from 'koa'; // koa@2 +import * as restify_5 from 'restify'; // restify@5 //---koa@1---// From 139399496ffc9c11ed5e2da8ebcfa6b68f430196 Mon Sep 17 00:00:00 2001 From: Kelvin Jin Date: Fri, 19 Jan 2018 17:43:42 -0800 Subject: [PATCH 5/5] address comments --- src/plugin-types.ts | 4 ++-- src/plugins/plugin-connect.ts | 11 ++++------- src/plugins/plugin-express.ts | 5 +---- src/plugins/plugin-hapi.ts | 12 ++++-------- src/plugins/plugin-koa.ts | 15 +++++---------- src/plugins/plugin-restify.ts | 9 +++------ 6 files changed, 19 insertions(+), 37 deletions(-) diff --git a/src/plugin-types.ts b/src/plugin-types.ts index 12bc09813..8edc774c3 100644 --- a/src/plugin-types.ts +++ b/src/plugin-types.ts @@ -72,9 +72,9 @@ export interface RootSpanOptions extends SpanOptions { url?: string; /** * The serialized form of an object that contains information about an - * existing trace context. + * existing trace context, if it exists. */ - traceContext?: string; + traceContext?: string|null; } export interface TraceAgent { diff --git a/src/plugins/plugin-connect.ts b/src/plugins/plugin-connect.ts index 45d860383..47cea9716 100644 --- a/src/plugins/plugin-connect.ts +++ b/src/plugins/plugin-connect.ts @@ -14,9 +14,6 @@ * limitations under the License. */ -// tslint:disable-next-line:no-reference -/// - import {IncomingMessage, ServerResponse} from 'http'; import * as shimmer from 'shimmer'; import {parse as urlParse} from 'url'; @@ -32,8 +29,8 @@ type Request = IncomingMessage&{originalUrl?: string}; const SUPPORTED_VERSIONS = '3.x'; -function getFirstHeader(req: IncomingMessage, key: string) { - let headerValue = req.headers[key]; +function getFirstHeader(req: IncomingMessage, key: string): string|null { + let headerValue = req.headers[key] || null; if (headerValue && typeof headerValue !== 'string') { headerValue = headerValue[0]; } @@ -66,8 +63,8 @@ function createMiddleware(api: PluginTypes.TraceAgent): api.wrapEmitter(req); api.wrapEmitter(res); - const url = (req.headers['X-Forwarded-Proto'] || 'http') + '://' + - req.headers.host + req.originalUrl; + const url = `${req.headers['X-Forwarded-Proto'] || 'http'}://${ + req.headers.host}${req.originalUrl}`; // we use the path part of the url as the span name and add the full // url as a label diff --git a/src/plugins/plugin-express.ts b/src/plugins/plugin-express.ts index fdb0ad23f..1715d2833 100644 --- a/src/plugins/plugin-express.ts +++ b/src/plugins/plugin-express.ts @@ -14,9 +14,6 @@ * limitations under the License. */ -// tslint:disable-next-line:no-reference -/// - import * as httpMethods from 'methods'; import * as shimmer from 'shimmer'; @@ -58,7 +55,7 @@ function patchModuleRoot(express: Express4Module, api: PluginTypes.TraceAgent) { api.wrapEmitter(req); api.wrapEmitter(res); - const url = req.protocol + '://' + req.hostname + req.originalUrl; + const url = `${req.protocol}://${req.hostname}${req.originalUrl}`; rootSpan.addLabel(labels.HTTP_METHOD_LABEL_KEY, req.method); rootSpan.addLabel(labels.HTTP_URL_LABEL_KEY, url); rootSpan.addLabel(labels.HTTP_SOURCE_IP, req.connection.remoteAddress); diff --git a/src/plugins/plugin-hapi.ts b/src/plugins/plugin-hapi.ts index 1e1a29651..4f2f6ab7c 100644 --- a/src/plugins/plugin-hapi.ts +++ b/src/plugins/plugin-hapi.ts @@ -14,10 +14,6 @@ * limitations under the License. */ -// TODO(kjin): Remove this when @types/shimmer are published. -// tslint:disable-next-line:no-reference -/// - import {IncomingMessage, ServerResponse} from 'http'; import * as shimmer from 'shimmer'; import {parse as urlParse} from 'url'; @@ -30,8 +26,8 @@ type Hapi16Module = typeof hapi_16; const SUPPORTED_VERSIONS = '8 - 16'; -function getFirstHeader(req: IncomingMessage, key: string) { - let headerValue = req.headers[key]; +function getFirstHeader(req: IncomingMessage, key: string): string|null { + let headerValue = req.headers[key] || null; if (headerValue && typeof headerValue !== 'string') { headerValue = headerValue[0]; } @@ -67,8 +63,8 @@ function createMiddleware(api: PluginTypes.TraceAgent): api.wrapEmitter(req); api.wrapEmitter(res); - const url = (req.headers['X-Forwarded-Proto'] || 'http') + '://' + - req.headers.host + req.url; + const url = `${req.headers['X-Forwarded-Proto'] || 'http'}://${ + req.headers.host}${req.url}`; // we use the path part of the url as the span name and add the full // url as a label diff --git a/src/plugins/plugin-koa.ts b/src/plugins/plugin-koa.ts index b0abd0856..a785ea775 100644 --- a/src/plugins/plugin-koa.ts +++ b/src/plugins/plugin-koa.ts @@ -14,10 +14,6 @@ * limitations under the License. */ -// TODO(kjin): Remove this when @types/shimmer are published. -// tslint:disable-next-line:no-reference -/// - import {IncomingMessage, ServerResponse} from 'http'; import * as shimmer from 'shimmer'; import {parse as urlParse} from 'url'; @@ -44,8 +40,8 @@ type CreateMiddlewareFn = (api: PluginTypes.TraceAgent) => T; // propagateContext flag. The type of "next" differs between Koa 1 and 2. type GetNextFn = (propagateContext: boolean) => T; -function getFirstHeader(req: IncomingMessage, key: string) { - let headerValue = req.headers[key]; +function getFirstHeader(req: IncomingMessage, key: string): string|null { + let headerValue = req.headers[key] || null; if (headerValue && typeof headerValue !== 'string') { headerValue = headerValue[0]; } @@ -79,8 +75,9 @@ function startSpanForRequest( api.wrapEmitter(req); api.wrapEmitter(res); - const url = (req.headers['X-Forwarded-Proto'] || 'http') + '://' + - req.headers.host + req.url; + + const url = `${req.headers['X-Forwarded-Proto'] || 'http'}://${ + req.headers.host}${req.url}`; // we use the path part of the url as the span name and add the full // url as a label @@ -117,8 +114,6 @@ function startSpanForRequest( } function createMiddleware(api: PluginTypes.TraceAgent): koa_1.Middleware { - // Koa 1 type definitions use any here. - // tslint:disable-next-line:no-any return function* middleware(this: koa_1.Context, next: IterableIterator<{}>) { next = startSpanForRequest(api, this, (propagateContext: boolean) => { if (propagateContext) { diff --git a/src/plugins/plugin-restify.ts b/src/plugins/plugin-restify.ts index a4e5c3898..4bc7a36e3 100644 --- a/src/plugins/plugin-restify.ts +++ b/src/plugins/plugin-restify.ts @@ -14,10 +14,6 @@ * limitations under the License. */ -// TODO(kjin): Remove this when @types/shimmer are published. -// tslint:disable-next-line:no-reference -/// - import {ServerResponse} from 'http'; import * as shimmer from 'shimmer'; import {parse as urlParse} from 'url'; @@ -75,8 +71,9 @@ function patchRestify(restify: Restify5, api: PluginTypes.TraceAgent) { api.wrapEmitter(req); api.wrapEmitter(res); - const fullUrl = req.header('X-Forwarded-Proto', 'http') + '://' + - req.header('host') + req.url; + + const fullUrl = `${req.header('X-Forwarded-Proto', 'http')}://${ + req.header('host')}${req.url}`; rootSpan.addLabel(api.labels.HTTP_METHOD_LABEL_KEY, req.method); rootSpan.addLabel(api.labels.HTTP_URL_LABEL_KEY, fullUrl); rootSpan.addLabel(