'use strict' ; import * as debug from 'debug' ; import * as express from 'express' ; import * as fs from 'fs-extra' ; import * as _ from 'lodash' ; import 'multer' ; import * as path from 'path' ; import * as YAML from 'yamljs' ; import { FileLimits , HttpMethod , ParameterConverter , ServiceAuthenticator , ServiceFactory } from './model/server-types' ; import { ServerContainer } from './server-container' ; const serverDebugger = debug ( 'typescript-rest:server:build' ) ; /**
 * The Http server main class.
 */ export class Server { /**
     * Create the routes for all classes decorated with our decorators
     */ public static buildServices ( router , ... types ) { if ( ! Server . locked ) { serverDebugger ( 'Creating typescript-rest services handlers' ) ; const serverContainer = ServerContainer . get ( ) ; serverContainer . router = router ; serverContainer . buildServices ( types ) ; } } /**
     * An alias for Server.loadServices()
     */ public static loadControllers ( router , patterns , baseDir ? ) { Server . loadServices ( router , patterns , baseDir ) ; } /**
     * Load all services from the files that matches the patterns provided
     */ public static loadServices ( router , patterns , baseDir ? ) { if ( ! Server . locked ) { serverDebugger ( 'Loading typescript-rest services %j. BaseDir: %s' , patterns , baseDir ) ; const importedTypes = [ ] ; const requireGlob = require ( 'require-glob' ) ; baseDir = baseDir || process . cwd ( ) ; const loadedModules = requireGlob . sync ( patterns , { cwd : baseDir } ) ; _ . values ( loadedModules ) . forEach ( serviceModule => { _ . values ( serviceModule ) . filter ( ( service ) => typeof service === 'function' ) . forEach ( ( service ) => { importedTypes . push ( service ) ; } ) ; } ) ; try { Server . buildServices ( router , ... importedTypes ) ; } catch ( e ) { serverDebugger ( 'Error loading services for pattern: %j. Error: %o' , patterns , e ) ; serverDebugger ( 'ImportedTypes: %o' , importedTypes ) ; throw new TypeError ( `Error loading services for pattern: ${ JSON . stringify ( patterns ) }. Error: ${ e . message }` ) ; } } } param value public static immutable ( value ) { Server . locked = value ; } /**
     * Return true if the server is immutable. Any configuration change request to the Server
     * is ignored when immutable is true
     */ public static isImmutable ( ) { return Server . locked ; } /**
     * Retrieve the express router that serves the rest endpoints
     */ public static server ( ) { return ServerContainer . get ( ) . router ; } /**
     * Return all paths accepted by the Server
     */ public static getPaths ( ) { const result = new Array < string > ( ) ; ServerContainer . get ( ) . getPaths ( ) . forEach ( value => { result . push ( value ) ; } ) ; return result ; } /**
     * Register a custom serviceFactory. It will be used to instantiate the service Objects
     * If You plan to use a custom serviceFactory, You must ensure to call this method before any typescript-rest service declaration.
     */ public static registerServiceFactory ( serviceFactory ) { if ( ! Server . locked ) { let factory ; if ( typeof serviceFactory === 'string' ) { const mod = require ( serviceFactory ) ; factory = mod . default ? mod . default : mod ; } else { factory = serviceFactory as ServiceFactory ; } serverDebugger ( 'Registering a new serviceFactory' ) ; ServerContainer . get ( ) . serviceFactory = factory ; } } /**
     * Register a service authenticator. It will be used to authenticate users before the service method
     * invocations occurs.
     */ public static registerAuthenticator ( authenticator , name = 'default' ) { if ( ! Server . locked ) { serverDebugger ( 'Registering a new authenticator with name %s' , name ) ; ServerContainer . get ( ) . authenticator . set ( name , authenticator ) ; } } param servicePath public static getHttpMethods ( servicePath ) { const result = new Array < HttpMethod > ( ) ; ServerContainer . get ( ) . getHttpMethods ( servicePath ) . forEach ( value => { result . push ( value ) ; } ) ; return result ; } param secret public static setCookiesSecret ( secret ) { if ( ! Server . locked ) { serverDebugger ( 'Setting a new secret for cookies: %s' , secret ) ; ServerContainer . get ( ) . cookiesSecret = secret ; } } param decoder public static setCookiesDecoder ( decoder ) { if ( ! Server . locked ) { serverDebugger ( 'Setting a new secret decoder' ) ; ServerContainer . get ( ) . cookiesDecoder = decoder ; } } param dest public static setFileDest ( dest ) { if ( ! Server . locked ) { serverDebugger ( 'Setting a new destination for files: %s' , dest ) ; ServerContainer . get ( ) . fileDest = dest ; } } param filter public static setFileFilter ( filter ) { if ( ! Server . locked ) { serverDebugger ( 'Setting a new filter for files' ) ; ServerContainer . get ( ) . fileFilter = filter ; } } param limit public static setFileLimits ( limit ) { if ( ! Server . locked ) { serverDebugger ( 'Setting a new fileLimits: %j' , limit ) ; ServerContainer . get ( ) . fileLimits = limit ; } } param converter param type public static addParameterConverter ( converter , type ) { if ( ! Server . locked ) { serverDebugger ( 'Adding a new parameter converter' ) ; ServerContainer . get ( ) . paramConverters . set ( type , converter ) ; } } param type public static removeParameterConverter ( type ) { if ( ! Server . locked ) { serverDebugger ( 'Removing a parameter converter' ) ; ServerContainer . get ( ) . paramConverters . delete ( type ) ; } } param value public static ignoreNextMiddlewares ( value ) { if ( ! Server . locked ) { serverDebugger ( 'Ignoring next middlewares: %b' , value ) ; ServerContainer . get ( ) . ignoreNextMiddlewares = value ; } } param router param options public static swagger ( router , options ? ) { if ( ! Server . locked ) { const swaggerUi = require ( 'swagger-ui-express' ) ; options = Server . getOptions ( options ) ; serverDebugger ( 'Configuring open api documentation endpoints for options: %j' , options ) ; const swaggerDocument = Server . loadSwaggerDocument ( options ) ; if ( options . host ) { swaggerDocument . host = options . host ; } if ( options . schemes ) { swaggerDocument . schemes = options . schemes ; } router . get ( path . posix . join ( '/' , options . endpoint , 'json' ) , ( req , res , next ) => { res . send ( swaggerDocument ) ; } ) ; router . get ( path . posix . join ( '/' , options . endpoint , 'yaml' ) , ( req , res , next ) => { res . set ( 'Content-Type' , 'text/vnd.yaml' ) ; res . send ( YAML . stringify ( swaggerDocument , 1000 ) ) ; } ) ; router . use ( path . posix . join ( '/' , options . endpoint ) , swaggerUi . serve , swaggerUi . setup ( swaggerDocument , options . swaggerUiOptions ) ) ; } } private static locked = false ; private static loadSwaggerDocument ( options ) { let swaggerDocument ; if ( _ . endsWith ( options . filePath , '.yml' ) || _ . endsWith ( options . filePath , '.yaml' ) ) { swaggerDocument = YAML . load ( options . filePath ) ; } else { swaggerDocument = fs . readJSONSync ( options . filePath ) ; } serverDebugger ( 'Loaded swagger configurations: %j' , swaggerDocument ) ; return swaggerDocument ; } private static getOptions ( options ) { options = _ . defaults ( options , { endpoint : 'api-docs' , filePath : './swagger.json' } ) ; if ( _ . startsWith ( options . filePath , '.' ) ) { options . filePath = path . join ( process . cwd ( ) , options . filePath ) ; } return options ; } } export interface SwaggerOptions { /**
     * The path to a swagger file (json or yaml)
     */ filePath ? : string ; /**
     * Where to publish the docs
     */ endpoint ? : string ; /**
     * The hostname of the service
     */ host ? : string ; /**
     * The schemes used by the server
     */ schemes ? : Array < string > ; /**
     * Options to send to swagger-ui
     */ swaggerUiOptions ? : object ; }