@@ -5,125 +5,145 @@ import 'source-map-support/register';
55
66import * as errorHandler from 'errorhandler' ;
77import * as express from 'express' ;
8- import { Request , Response } from 'express' ;
8+ import { Request , Response } from 'express' ;
99import * as refParser from 'json-schema-ref-parser' ;
1010import * as morgan from 'morgan' ;
11- import OpenAPIBackend , { Request as OpenAPIRequest } from 'openapi-backend' ;
12- import { OpenAPIV3 } from 'openapi-types' ;
11+ import OpenAPIBackend , { Request as OpenAPIRequest } from 'openapi-backend' ;
12+ import { OpenAPIV3 } from 'openapi-types' ;
1313import * as path from 'path' ;
1414import * as http from 'http' ;
1515
16- import { readJsonOrYamlSync } from './util' ;
16+ import { readJsonOrYamlSync } from './util' ;
1717
18- export async function buildApp ( apiDocFile : string ) :
19- Promise < express . Application > {
20- const apiDocRaw = readJsonOrYamlSync ( apiDocFile ) ;
21- const apiDoc = await refParser . dereference ( apiDocFile , apiDocRaw , { } ) ;
18+ export async function buildApp (
19+ apiDocFile : string ,
20+ ) : Promise < express . Application > {
21+ const apiDocRaw = readJsonOrYamlSync ( apiDocFile ) ;
22+ const apiDoc = await refParser . dereference ( apiDocFile , apiDocRaw , { } ) ;
2223
23- const api = buildApi ( apiDocFile , apiDoc ) ;
24- await api . init ( ) ;
25- debug ( `Loaded API definition for ${ path . basename ( apiDocFile ) } ("${
26- api . document . info . title } ", version: ${ api . document . info . version } )`) ;
24+ const api = buildApi ( apiDocFile , apiDoc ) ;
25+ await api . init ( ) ;
26+ debug (
27+ `Loaded API definition for ${ path . basename ( apiDocFile ) } ("${
28+ api . document . info . title
29+ } ", version: ${ api . document . info . version } )`,
30+ ) ;
2731
28- return buildExpressApp ( api ) ;
32+ return buildExpressApp ( api ) ;
2933}
3034
3135/** Configures and build the OpenAPIBackend express middleware. */
3236export function buildApi ( apiDocFile : string , apiDoc : any ) : OpenAPIBackend {
33- return new OpenAPIBackend ( {
34- definition : apiDoc as OpenAPIV3 . Document ,
35- strict : true ,
36- validate : false ,
37- ajvOpts : { unknownFormats : [ 'int32' , 'int64' , 'float' , 'double' ] } ,
38- handlers : {
39- validationFail : async ( c , _req : Request , res : Response ) => {
40- if ( ! c ) return ;
41- res . setHeader ( 'openapi-cop-openapi-file' , apiDocFile ) ;
42- res . status ( 400 ) . json ( { validation : c . validation } ) ;
43- } ,
44- notFound : async ( c , _req : Request , res : Response ) => {
45- if ( ! c ) return ;
46- // c.operationId is not defined, but c.operation is
47- if ( c . operation ) {
48- debug (
49- 'Cannot mock operation without an "operationId". Responding with 404.' ) ;
50- }
51- res . setHeader ( 'openapi-cop-openapi-file' , apiDocFile ) ;
52- res . status ( 404 ) . json ( { error : 'not found' } ) ;
53- } ,
54- notImplemented : async ( c , _req : Request , res : Response ) => {
55- if ( ! c ) return ;
56- res . setHeader ( 'openapi-cop-openapi-file' , apiDocFile ) ;
57-
58- if ( ! c . operation || ! c . operation . operationId ) {
59- debug ( 'Cannot mock operation without an "operationId"' ) ;
60- return res . status ( 404 ) . json ( { error : 'not found' } ) ;
61- }
62- const { status, mock} =
63- c . api . mockResponseForOperation ( c . operation . operationId ) ;
64-
65- return res . status ( status ) . json ( mock ) ;
66- }
67- }
68- } ) ;
37+ return new OpenAPIBackend ( {
38+ definition : apiDoc as OpenAPIV3 . Document ,
39+ strict : true ,
40+ validate : false ,
41+ ajvOpts : { unknownFormats : [ 'int32' , 'int64' , 'float' , 'double' ] } ,
42+ handlers : {
43+ validationFail : async ( c , _req : Request , res : Response ) => {
44+ if ( ! c ) return ;
45+ res . setHeader ( 'openapi-cop-openapi-file' , apiDocFile ) ;
46+ res . status ( 400 ) . json ( { validation : c . validation } ) ;
47+ } ,
48+ notFound : async ( c , _req : Request , res : Response ) => {
49+ if ( ! c ) return ;
50+ // c.operationId is not defined, but c.operation is
51+ if ( c . operation ) {
52+ debug (
53+ 'Cannot mock operation without an "operationId". Responding with 404.' ,
54+ ) ;
55+ }
56+ res . setHeader ( 'openapi-cop-openapi-file' , apiDocFile ) ;
57+ res . status ( 404 ) . json ( { error : 'not found' } ) ;
58+ } ,
59+ notImplemented : async ( c , _req : Request , res : Response ) => {
60+ if ( ! c ) return ;
61+ res . setHeader ( 'openapi-cop-openapi-file' , apiDocFile ) ;
62+
63+ if ( ! c . operation || ! c . operation . operationId ) {
64+ debug ( 'Cannot mock operation without an "operationId"' ) ;
65+ return res . status ( 404 ) . json ( { error : 'not found' } ) ;
66+ }
67+ const { status, mock } = c . api . mockResponseForOperation (
68+ c . operation . operationId ,
69+ ) ;
70+
71+ return res . status ( status ) . json ( mock ) ;
72+ } ,
73+ } ,
74+ } ) ;
6975}
7076
7177/**
7278 * Creates an express app and attaches a OpenAPIBackend middleware instance to
7379 * it.
7480 */
75- export async function buildExpressApp ( api : OpenAPIBackend ) :
76- Promise < express . Application > {
77- const app : express . Application = express ( ) ;
78- app . use ( express . json ( ) ) ;
79-
80- if ( debugMod . enabled ( 'openapi-cop:mock' ) ) {
81- // Logging of the form "openapi-cop:mock METHOD /url 123B (50ms)"
82- app . use ( morgan ( ( tokens , req , res ) => {
83- return [
84- chalk . bold ( ' openapi-cop:mock' ) , tokens . method ( req , res ) ,
85- tokens . url ( req , res ) , tokens . status ( req , res ) ,
86- tokens . res ( req , res , 'content-length' ) + 'B' ,
87- '(' + tokens [ 'response-time' ] ( req , res ) , 'ms)'
88- ] . join ( ' ' ) ;
89- } ) ) ;
90- }
91-
92- // Attach OpenAPI backend
93- app . use (
94- ( req , res ) => api . handleRequest ( req as OpenAPIRequest , req , res ) ) ;
95-
96- app . use (
97- // tslint:disable-next-line:no-any
98- ( err : any , _req : Request , res : Response ) => {
99- console . error ( err . stack ) ;
100- res . status ( 500 ) . send ( 'Server error' ) ;
101- } ) ;
102-
103- // Display full error stack traces
104- if ( process . env . NODE_ENV === 'development' ) {
105- app . use ( errorHandler ( ) ) ;
106- }
107-
108- return app ;
81+ export async function buildExpressApp (
82+ api : OpenAPIBackend ,
83+ ) : Promise < express . Application > {
84+ const app : express . Application = express ( ) ;
85+ app . use ( express . json ( ) ) ;
86+
87+ if ( debugMod . enabled ( 'openapi-cop:mock' ) ) {
88+ // Logging of the form "openapi-cop:mock METHOD /url 123B (50ms)"
89+ app . use (
90+ morgan ( ( tokens , req , res ) => {
91+ return [
92+ chalk . bold ( ' openapi-cop:mock' ) ,
93+ tokens . method ( req , res ) ,
94+ tokens . url ( req , res ) ,
95+ tokens . status ( req , res ) ,
96+ tokens . res ( req , res , 'content-length' ) + 'B' ,
97+ '(' + tokens [ 'response-time' ] ( req , res ) ,
98+ 'ms)' ,
99+ ] . join ( ' ' ) ;
100+ } ) ,
101+ ) ;
102+ }
103+
104+ // Attach OpenAPI backend
105+ app . use ( ( req , res ) => api . handleRequest ( req as OpenAPIRequest , req , res ) ) ;
106+
107+ app . use (
108+ // tslint:disable-next-line:no-any
109+ ( err : any , _req : Request , res : Response ) => {
110+ console . error ( err . stack ) ;
111+ res . status ( 500 ) . send ( 'Server error' ) ;
112+ } ,
113+ ) ;
114+
115+ // Display full error stack traces
116+ if ( process . env . NODE_ENV === 'development' ) {
117+ app . use ( errorHandler ( ) ) ;
118+ }
119+
120+ return app ;
121+ }
122+
123+ export type MockOptions = BaseMockOptions & ExtendedMockOptions ;
124+
125+ export interface BaseMockOptions {
126+ port : string | number ;
127+ }
128+
129+ export interface ExtendedMockOptions {
130+ apiDocFile : string ;
109131}
110132
111133/** Builds the app and runs it on the given port. */
112- export async function runApp (
113- port : string | number , apiDocFile : string ) : Promise < http . Server > {
114- try {
115- const app = await buildApp ( apiDocFile ) ;
116- let server : http . Server ;
117- return new Promise < http . Server > ( resolve => {
118- server = app . listen ( port , ( ) => {
119- resolve ( ) ;
120- } ) ;
121- } )
122- . then ( ( ) => {
123- return server ;
124- } ) ;
125- } catch ( e ) {
126- console . error ( `Failed to run mock server:\n${ e . message } ` ) ;
127- return Promise . reject ( ) ;
128- }
134+ export async function runApp ( { port, apiDocFile } : MockOptions ) : Promise < http . Server > {
135+ try {
136+ const app = await buildApp ( apiDocFile ) ;
137+ let server : http . Server ;
138+ return new Promise < http . Server > ( ( resolve ) => {
139+ server = app . listen ( port , ( ) => {
140+ resolve ( server ) ;
141+ } ) ;
142+ } ) . then ( ( ) => {
143+ return server ;
144+ } ) ;
145+ } catch ( error ) {
146+ console . error ( 'Failed to run mock server' , error ) ;
147+ return Promise . reject ( ) ;
148+ }
129149}
0 commit comments