From 5de7fb22740c2a0e320239a5adfc93cc65a8f0d6 Mon Sep 17 00:00:00 2001 From: Mitchell Bundy Date: Tue, 29 May 2018 17:43:51 -0400 Subject: [PATCH] update types for restify 7.2 --- types/restify/index.d.ts | 474 +++++++++++++++++++++++++-------- types/restify/restify-tests.ts | 18 +- 2 files changed, 373 insertions(+), 119 deletions(-) diff --git a/types/restify/index.d.ts b/types/restify/index.d.ts index bacea67cab..aa6cc218aa 100644 --- a/types/restify/index.d.ts +++ b/types/restify/index.d.ts @@ -1,6 +1,6 @@ -// Type definitions for restify 5.0 +// Type definitions for restify 7.2 // Project: https://github.com/restify/node-restify -// Definitions by: Bret Little , Steve Hipwell , Leandro Almeida +// Definitions by: Bret Little , Steve Hipwell , Leandro Almeida , Mitchell Bundy // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.2 @@ -8,16 +8,21 @@ import http = require('http'); import https = require('https'); +import http2 = require('http2'); import Logger = require('bunyan'); import url = require('url'); import spdy = require('spdy'); import stream = require('stream'); +import { Certificate } from 'crypto'; +import { ZlibOptions } from 'zlib'; export interface ServerOptions { ca?: string | Buffer | ReadonlyArray; certificate?: string | Buffer | ReadonlyArray; + cert?: string | Buffer | ReadonlyArray; + key?: string | Buffer | ReadonlyArray; passphrase?: string; @@ -51,6 +56,18 @@ export interface ServerOptions { noWriteContinue?: boolean; rejectUnauthorized?: boolean; + + secureOptions?: number; + + http2?: http2.SecureServerOptions; + + dtrace?: boolean; + + onceNext?: boolean; + + strictNext?: boolean; + + ignoreTrailingSlash?: boolean; } export interface AddressInterface { @@ -85,7 +102,7 @@ export interface Server extends http.Server { * Wraps node's close(). * @param callback optional callback to invoke when done. */ - close(...args: any[]): any; + close(callback?: Function): any; /** * Returns the number of currently inflight requests. @@ -175,26 +192,6 @@ export interface Server extends http.Server { */ param(name: string, fn: RequestHandler): Server; - /** - * Piggy-backs on the `server.use` method. It attaches a new middleware - * function that only fires if the specified version matches the request. - * - * Note that if the client does not request a specific version, the middleware - * function always fires. If you don't want this set a default version with a - * pre handler on requests where the client omits one. - * - * Exposes an API: - * server.versionedUse("version", function (req, res, next, ver) { - * // do stuff that only applies to routes of this API version - * }); - * - * @param versions the version(s) the URL to respond to - * @param fn the middleware function to execute, the - * fourth parameter will be the selected - * version - */ - versionedUse(versions: string | string[], fn: RequestHandler): Server; - /** * Removes a route from the server. * You pass in the route 'blob' you got from a mount call. @@ -247,39 +244,130 @@ export interface Server extends http.Server { url: string; /** Node server instance */ - server: http.Server; + server: http.Server | https.Server | spdy.Server | http2.Http2SecureServer; /** Router instance */ router: Router; + + /** Handle uncaught exceptions */ + handleUncaughtExceptions: boolean; + + /** enable DTrace support */ + dtrace: boolean; + + /** Custom response formatters */ + formatters: Formatters; + + /** Prevents calling next multiple times */ + onceNext: boolean; + + /** Throws error when next() is called more than once, enabled onceNext option */ + strictNext: boolean; + + /** Pre handlers */ + preChain: Chain; + + useChain: Chain; + + spdy?: boolean; + + http2?: boolean; + + ca: ServerOptions['ca'] + + certificate: ServerOptions['certificate'] + + key: ServerOptions['key']; + + passphrase: ServerOptions['passphrase'] | null; + + secure?: boolean; +} + +export interface ChainOptions { + onceNext?: boolean; + + strictNext?: boolean; +} + +export interface Chain { + /** Get handlers of a chain instance */ + getHandlers(): Function[]; + + /** Utilize the given middleware `handler` */ + add(handler: Function): void; + + /** Returns the number of handlers */ + count(): number; + + /** Handle server requests, punting them down the middleware stack. */ + run(req: Request, res: Response, done: Function): void; + + /** Prevents calling next multiple times */ + onceNext: boolean; + + /** Throws error when next() is called more than once, enables onceNext option */ + strictNext: boolean; +} + +export interface RouterRegistryRadix { + /** + * Adds a route. + */ + add(route: Route): boolean; + + /** + * Removes a route. + */ + remove(name: string): Route | undefined; + + /** + * Registry for route. + */ + lookup(method: string, pathname: string): Chain | undefined; + + /** + * Get registry. + */ + get(): Route[]; + + /** + * toString() serialization. + */ + toString(): string; } export interface RouterOptions { - contentType?: string | string[]; - - strictRouting?: boolean; - log?: Logger; - version?: string; + onceNext?: boolean; - versions?: string[]; + strictNext?: boolean; + + ignoreTrailingSlash?: boolean; + + registry?: RouterRegistryRadix; } export interface Router { + constructor(options: RouterOptions): void; + /** - * takes an object of route params and query params, and 'renders' a URL. - * @param routeName the route name - * @param params an object of route params - * @param query an object of query params + * Lookup for route */ - render(routeName: string, params: any, query?: any): string; + lookup(req: Request, res: Response): Chain | undefined; + + /** + * Lookup by name + */ + lookupByName(name: string, req: Request, res: Response): Chain | undefined; /** * adds a route. * @param options an options object * @returns returns the route name if creation is successful. */ - mount(options: RouteOptions): string | boolean; + mount(options: RouteOptions, ...handlers: RequestHandlerType[]): string; /** * unmounts a route. @@ -289,30 +377,14 @@ export interface Router { unmount(name: string): string; /** - * get a route from the router. - * @param name the name of the route to retrieve - * @param req the request object - * @param cb callback function + * Return mounted routes. */ - get(name: string, req: Request, cb: FindRouteCallback): void; + getRoutes(): Route[]; /** - * find a route from inside the router, handles versioned routes. - * @param req the request object - * @param res the response object - * @param callback callback function + * Default route, when no route found */ - find(req: Request, res: Response, callback: FindRouteCallback): void; - - /** - * Find a route by path. Scans the route list for a route with the same RegEx. - * i.e. /foo/:param1/:param2 would match an existing route with different - * parameter names /foo/:id/:name since the compiled RegExs match. - * @param path a path to find a route for. - * @param options an options object - * @returns returns the route if a match is found - */ - findByPath(path: string | RegExp): Route; + defaultRoute(req: Request, res: Response, next: Function): void; /** * toString() serialization. @@ -327,23 +399,11 @@ export interface Router { name: string; - mounts: { [routeName: string]: Route }; - - versions: string[]; - - contentType: string[]; - - routes: { - DELETE: Route[]; - GET: Route[]; - HEAD: Route[]; - OPTIONS: Route[]; - PATCH: Route[]; - POST: Route[]; - PUT: Route[]; - }; - log?: Logger; + + onceNext: boolean; + + strictNext: boolean; } export interface RequestFileInterface { @@ -361,6 +421,11 @@ export interface RequestAuthorization { } export interface Request extends http.IncomingMessage { + /** + * Builds an absolute URI for the request. + */ + absoluteUri(path: string): string; + /** * checks if the accept header is present and has the value requested. * e.g., req.accepts('html'); @@ -464,20 +529,22 @@ export interface Request extends http.IncomingMessage { matchedVersion(): string; /** - * returns any header off the request. also, 'correct' any + * Get the case-insensitive request header key, + * and optionally provide a default value (express-compliant). + * Returns any header off the request. also, 'correct' any * correctly spelled 'referrer' header to the actual spelling used. - * @param name the name of the header - * @param value default value if header isn't found on the req + * @param key - the key of the header + * @param defaultValue - default value if header isn't found on the req */ - header(name: string, value?: string): string; + header(key: string, defaultValue?: string): string; /** * returns any trailer header off the request. also, 'correct' any * correctly spelled 'referrer' header to the actual spelling used. * @param name the name of the header - * @param value default value if header isn't found on the req + * @param defaultValue default value if header isn't found on the req */ - trailer(name: string, value?: string): string; + trailer(name: string, defaultValue?: string): string; /** * Check if the incoming request contains the Content-Type header field, and @@ -629,37 +696,37 @@ export interface Response extends http.ServerResponse { /** * sets headers on the response. - * @param name the name of the header + * @param key the name of the header * @param value the value of the header */ - header(name: string, value?: any): any; + header(key: string, value?: any): any; /** * short hand method for: * res.contentType = 'json'; * res.send({hello: 'world'}); * @param code http status code - * @param object value to json.stringify + * @param body value to json.stringify * @param [headers] headers to set on the response */ - json(code: number, object: any, headers?: { [header: string]: string }): any; + json(code: number, body: any, headers?: { [header: string]: string }): any; /** * short hand method for: * res.contentType = 'json'; * res.send({hello: 'world'}); - * @param object value to json.stringify + * @param body value to json.stringify * @param [headers] headers to set on the response */ - json(object: any, headers?: { [header: string]: string }): any; + json(body: any, headers?: { [header: string]: string }): any; /** * sets the link heaader. - * @param l the link key - * @param rel the link value + * @param key the link key + * @param value the link value * @returns the header value set to res */ - link(l: string, rel: string): string; + link(key: string, value: string): string; /** * sends the response object. pass through to internal __send that uses a @@ -669,7 +736,16 @@ export interface Response extends http.ServerResponse { * @param [headers] any add'l headers to set * @returns the response object */ - send(code?: any, body?: any, headers?: { [header: string]: string }): any; + send(code?: number, body?: any, headers?: { [header: string]: string }): any; + + /** + * sends the response object. pass through to internal __send that uses a + * formatter based on the content-type header. + * @param [body] the content to send + * @param [headers] any add'l headers to set + * @returns the response object + */ + send(body?: any, headers?: { [header: string]: string }): any; /** * sends the response object. pass through to internal __send that skips @@ -679,7 +755,16 @@ export interface Response extends http.ServerResponse { * @param [headers] any add'l headers to set * @returns the response object */ - sendRaw(code?: any, body?: any, headers?: { [header: string]: string }): any; + sendRaw(code?: number, body?: any, headers?: { [header: string]: string }): any; + + /** + * sends the response object. pass through to internal __send that skips + * formatters entirely and sends the content as is. + * @param [body] the content to send + * @param [headers] any add'l headers to set + * @returns the response object + */ + sendRaw(body?: any, headers?: { [header: string]: string }): any; /** * sets a header on the response. @@ -689,6 +774,13 @@ export interface Response extends http.ServerResponse { */ set(name: string, val: string): Response; + /** + * sets a header on the response. + * @param val object of headers + * @returns self, the response object + */ + set(headers?: { [header: string]: string }): Response; + /** * sets the http status code on the response. * @param code http status code @@ -705,9 +797,9 @@ export interface Response extends http.ServerResponse { * redirect is sugar method for redirecting. * res.redirect(301, 'www.foo.com', next); * `next` is mandatory, to complete the response and trigger audit logger. - * @param code the status code - * @param url to redirect to - * @param next fn + * @param code the status code + * @param url to redirect to + * @param next - mandatory, to complete the response and trigger audit logger * @emits redirect */ redirect(code: number, url: string, next: Next): void; @@ -716,11 +808,21 @@ export interface Response extends http.ServerResponse { * redirect is sugar method for redirecting. * res.redirect({...}, next); * `next` is mandatory, to complete the response and trigger audit logger. - * @param options the options or url to redirect to - * @param next fn + * @param url to redirect to + * @param next - mandatory, to complete the response and trigger audit logger * @emits redirect */ - redirect(options: object | string, next: Next): void; + redirect(url: string, next: Next): void; + + /** + * redirect is sugar method for redirecting. + * res.redirect({...}, next); + * `next` is mandatory, to complete the response and trigger audit logger. + * @param opts - options object to configure a redirect + * @param next - mandatory, to complete the response and trigger audit logger + * @emits redirect + */ + redirect(opts: RedirectOptions, next: Next): void; /** HTTP status code. */ code: number; @@ -735,30 +837,65 @@ export interface Response extends http.ServerResponse { id: string; } +export interface RedirectOptions { + /** + * whether to redirect to http or https + */ + secure?: boolean; + + /** + * redirect location's hostname + */ + hostname?: string; + + /** + * redirect location's pathname + */ + pathname?: string; + + /** + * redirect location's port number + */ + port?: string; + + /** + * redirect location's query string parameters + */ + query?: string; + + /** + * if true, `options.query` + * stomps over any existing query + * parameters on current URL. + * by default, will merge the two. + */ + overrideQuery?: boolean; + + /** + * if true, sets 301. defaults to 302. + */ + permanent?: boolean; +} + export interface Next { (err?: any): void; ifError(err?: any): void; } -export interface RoutePathRegex extends RegExp { - restifyParams: string[]; -} - export interface RouteSpec { method: string; - name: string; + name?: string; path: string | RegExp; - versions: string[]; + versions?: string[]; } export interface Route { name: string; method: string; - path: RoutePathRegex; + path: string | RegExp; spec: RouteSpec; - types: string[]; - versions: string[]; + chain: Chain; } export interface RouteOptions { @@ -800,6 +937,49 @@ export type FindRouteCallback = (err: Error, route?: Route, params?: any) => voi export type RequestHandler = (req: Request, res: Response, next: Next) => any; export type RequestHandlerType = RequestHandler | RequestHandler[]; +export interface ServerUpgradeResponse { + /** + * Set the status code of the response. + * @param code - the http status code + */ + status(code: number): number; + + /** + * Sends the response. + * @param code - the http status code + * @param body - the response to send out + */ + send(code: number, body: any): any; + + /** + * Sends the response. + * @param body - the response to send out + */ + send(body: any): boolean; + + /** + * Ends the response + */ + end(): boolean; + + /** + * Write to the response. + */ + write(): boolean; + + /** + * Write to the head of the response. + * @param statusCode - the http status code + * @param reason - a message + */ + writeHead(statusCode: number, reason?: string): void; + + /** + * Attempt to upgrade. + */ + claimUpgrade(): any; +} + export namespace bunyan { interface RequestCaptureOptions { /** The stream to which to write when dumping captured records. */ @@ -857,7 +1037,7 @@ export namespace bunyan { export function createServer(options?: ServerOptions): Server; -export type Formatter = (req: Request, res: Response, body: any) => string | null; +export type Formatter = (req: Request, res: Response, body: any) => string | Buffer | null; export interface Formatters { [contentType: string]: Formatter; @@ -907,6 +1087,8 @@ export namespace plugins { */ function acceptParser(accepts: string[]): RequestHandler; + type AuditLoggerContext = (req: Request, res: Response, route: any, error: any) => any; + interface AuditLoggerOptions { /** * Bunyan logger @@ -918,11 +1100,21 @@ export namespace plugins { * log, one of 'pre', 'routed', or 'after' */ event: 'pre' | 'routed' | 'after'; + /** * Restify server. If passed in, causes server to emit 'auditlog' event after audit logs are flushed */ server?: Server; + /** + * The optional context function of signature + * f(req, res, route, err). Invoked each time an audit log is generated. This + * function can return an object that customizes the format of anything off the + * req, res, route, and err objects. The output of this function will be + * available on the `context` key in the audit object. + */ + context?: AuditLoggerContext; + /** * Ringbuffer which is written to if passed in */ @@ -962,6 +1154,18 @@ export namespace plugins { */ function conditionalRequest(): RequestHandler[]; + interface CpuUsageThrottleOptions { + limit?: number; + max?: number; + interval?: number; + halfLife?: number; + } + + /** + * Cpu Throttle middleware + */ + function cpuUsageThrottle(opts?: CpuUsageThrottleOptions): RequestHandler; + /** * Handles disappeared CORS headers */ @@ -1048,9 +1252,10 @@ export namespace plugins { */ function bodyReader(options?: { maxBodySize?: number }): RequestHandler; - interface UrlEncodedBodyParser { + interface UrlEncodedBodyParserOptions { mapParams?: boolean; overrideParams?: boolean; + bodyReader?: boolean; } /** @@ -1059,12 +1264,19 @@ export namespace plugins { * If req.params already contains a given key, that key is skipped and an * error is logged. */ - function urlEncodedBodyParser(options?: UrlEncodedBodyParser): RequestHandler[]; + function urlEncodedBodyParser(options?: UrlEncodedBodyParserOptions): RequestHandler[]; + + interface JsonBodyParserOptions { + mapParams?: boolean; + overrideParams?: boolean; + reviver?: (key: any, value: any) => any; + bodyReader?: boolean; + } /** * Parses JSON POST bodies */ - function jsonBodyParser(options?: { mapParams?: boolean, reviver?: any, overrideParams?: boolean }): RequestHandler[]; + function jsonBodyParser(options?: JsonBodyParserOptions): RequestHandler[]; /** * Parses JSONP callback @@ -1168,7 +1380,15 @@ export namespace plugins { * gzips the response if client send `accept-encoding: gzip` * @param options options to pass to gzlib */ - function gzipResponse(options?: any): RequestHandler; + function gzipResponse(options?: ZlibOptions): RequestHandler; + + interface InflightRequestThrottleOptions { + limit: number; + server: Server; + err: any; + } + + function inflightRequestThrottle(opts: InflightRequestThrottleOptions): RequestHandler; interface ServeStatic { appendRequestPath?: boolean; @@ -1190,6 +1410,7 @@ export namespace plugins { interface ThrottleOptions { burst?: number; rate?: number; + setHeaders?: boolean; ip?: boolean; username?: boolean; xff?: boolean; @@ -1198,6 +1419,11 @@ export namespace plugins { overrides?: any; // any } + /** + * throttles responses + */ + function throttle(options?: ThrottleOptions): RequestHandler; + interface MetricsCallback { /** * An error if the request had an error @@ -1229,16 +1455,41 @@ export namespace plugins { */ method: string; + /** + * latency includes both request is flushed and all handlers finished + */ + totalLatency: number; + /** * Request latency */ latency: number; + /** + * pre handlers latency + */ + preLatency: number | null; + + /** + * use handlers latency + */ + useLatency: number | null; + /** * req.path() value */ path: string; + /** + * Number of inflight requests pending in restify + */ + inflightRequests: number; + + /** + * Same as `inflightRequests` + */ + unfinishedRequests: number; + /** * If this value is set, err will be a corresponding `RequestCloseError` or `RequestAbortedError`. * @@ -1271,11 +1522,6 @@ export namespace plugins { */ function oauth2TokenParser(): RequestHandler; - /** - * throttles responses - */ - function throttle(options?: ThrottleOptions): RequestHandler; - interface RequestExpiryOptions { /** * Header name of the absolute time for request expiration diff --git a/types/restify/restify-tests.ts b/types/restify/restify-tests.ts index 8601d912ac..9b2736b98b 100644 --- a/types/restify/restify-tests.ts +++ b/types/restify/restify-tests.ts @@ -3,6 +3,7 @@ import * as url from "url"; import * as Logger from "bunyan"; import * as http from "http"; import * as stream from "stream"; +import { resolveSoa } from "dns"; let server: restify.Server; @@ -50,6 +51,7 @@ function send(req: restify.Request, res: restify.Response, next: restify.Next) { req.userAgent() === 'test'; req.startHandlerTimer('test'); req.endHandlerTimer('test'); + req.absoluteUri('test'); const log = req.log; log.debug({ params: req.params }, 'Hello there %s', 'foo'); @@ -85,6 +87,11 @@ function send(req: restify.Request, res: restify.Response, next: restify.Next) { res.send(201, { hello: 'world' }); res.send(new Error('meh')); + res.set('header', 'value'); + res.set({ + headerName: 'value' + }); + res.json(201, { hello: 'world' }); res.json({ hello: 'world' }); @@ -162,11 +169,10 @@ const logger = Logger.createLogger({ name: "test" }); server.on('after', restify.plugins.auditLogger({ event: 'after', log: logger })); server.on('after', (req: restify.Request, res: restify.Response, route: restify.Route, err: any) => { - route.spec.method === 'GET'; - route.spec.name === 'routeName'; - route.spec.path === '/some/path'; - route.spec.path === /\/some\/path\/.*/; - route.spec.versions === ['v1']; + route.method === 'GET'; + route.name === 'routeName'; + route.path === '/some/path'; + route.path === /\/some\/path\/.*/; restify.plugins.auditLogger({ event: 'after', log: logger })(req, res, route, err); }); @@ -186,6 +192,8 @@ requestCaptureOptions.maxRequestIds = 500; requestCaptureOptions.dumpDefault = true; const requestCaptureStream = new restify.bunyan.RequestCaptureStream(requestCaptureOptions); +requestCaptureStream.write(loggerStream); +requestCaptureStream.toString(); const asStream: stream.Stream = requestCaptureStream; const logger2: Logger = restify.bunyan.createLogger("horse");