@@ -5,125 +5,145 @@ import 'source-map-support/register';
5
5
6
6
import * as errorHandler from 'errorhandler' ;
7
7
import * as express from 'express' ;
8
- import { Request , Response } from 'express' ;
8
+ import { Request , Response } from 'express' ;
9
9
import * as refParser from 'json-schema-ref-parser' ;
10
10
import * 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' ;
13
13
import * as path from 'path' ;
14
14
import * as http from 'http' ;
15
15
16
- import { readJsonOrYamlSync } from './util' ;
16
+ import { readJsonOrYamlSync } from './util' ;
17
17
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 , { } ) ;
22
23
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
+ ) ;
27
31
28
- return buildExpressApp ( api ) ;
32
+ return buildExpressApp ( api ) ;
29
33
}
30
34
31
35
/** Configures and build the OpenAPIBackend express middleware. */
32
36
export 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
+ } ) ;
69
75
}
70
76
71
77
/**
72
78
* Creates an express app and attaches a OpenAPIBackend middleware instance to
73
79
* it.
74
80
*/
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 ;
109
131
}
110
132
111
133
/** 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
+ }
129
149
}
0 commit comments