@mbtest/mountebank 2.9.2-beta.9050

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +94 -0
  3. package/bin/mb +136 -0
  4. package/package.json +71 -0
  5. package/releases.json +52 -0
  6. package/src/cli/api.js +112 -0
  7. package/src/cli/cli.js +420 -0
  8. package/src/controllers/configController.js +64 -0
  9. package/src/controllers/feedController.js +115 -0
  10. package/src/controllers/homeController.js +58 -0
  11. package/src/controllers/imposterController.js +328 -0
  12. package/src/controllers/impostersController.js +215 -0
  13. package/src/controllers/logsController.js +52 -0
  14. package/src/models/behaviors.js +553 -0
  15. package/src/models/behaviorsValidator.js +186 -0
  16. package/src/models/compatibility.js +133 -0
  17. package/src/models/dryRunValidator.js +261 -0
  18. package/src/models/filesystemBackedImpostersRepository.js +908 -0
  19. package/src/models/http/baseHttpServer.js +207 -0
  20. package/src/models/http/headersMap.js +87 -0
  21. package/src/models/http/httpProxy.js +230 -0
  22. package/src/models/http/httpRequest.js +82 -0
  23. package/src/models/http/httpServer.js +18 -0
  24. package/src/models/http/index.js +18 -0
  25. package/src/models/https/cert/mb-cert.pem +20 -0
  26. package/src/models/https/cert/mb-csr.pem +16 -0
  27. package/src/models/https/cert/mb-key.pem +27 -0
  28. package/src/models/https/httpsServer.js +42 -0
  29. package/src/models/https/index.js +18 -0
  30. package/src/models/imposter.js +243 -0
  31. package/src/models/imposterPrinter.js +120 -0
  32. package/src/models/impostersRepository.js +49 -0
  33. package/src/models/inMemoryImpostersRepository.js +418 -0
  34. package/src/models/jsonpath.js +44 -0
  35. package/src/models/mbConnection.js +107 -0
  36. package/src/models/predicates.js +438 -0
  37. package/src/models/protocols.js +242 -0
  38. package/src/models/responseResolver.js +398 -0
  39. package/src/models/smtp/index.js +16 -0
  40. package/src/models/smtp/smtpRequest.js +60 -0
  41. package/src/models/smtp/smtpServer.js +109 -0
  42. package/src/models/tcp/index.js +18 -0
  43. package/src/models/tcp/tcpProxy.js +110 -0
  44. package/src/models/tcp/tcpRequest.js +23 -0
  45. package/src/models/tcp/tcpServer.js +156 -0
  46. package/src/models/tcp/tcpValidator.js +19 -0
  47. package/src/models/xpath.js +95 -0
  48. package/src/mountebank.js +245 -0
  49. package/src/public/images/arrow_down.png +0 -0
  50. package/src/public/images/arrow_up.png +0 -0
  51. package/src/public/images/book.jpg +0 -0
  52. package/src/public/images/dataflow.png +0 -0
  53. package/src/public/images/favicon.ico +0 -0
  54. package/src/public/images/forkme_right_orange_ff7600.png +0 -0
  55. package/src/public/images/mountebank.png +0 -0
  56. package/src/public/images/overview.gif +0 -0
  57. package/src/public/images/quote.png +0 -0
  58. package/src/public/images/tw-logo.png +0 -0
  59. package/src/public/scripts/jquery/jquery-3.6.1.min.js +2 -0
  60. package/src/public/scripts/urlHashHandler.js +31 -0
  61. package/src/public/stylesheets/application.css +424 -0
  62. package/src/public/stylesheets/ie.css +14 -0
  63. package/src/public/stylesheets/imposters.css +121 -0
  64. package/src/public/stylesheets/jqueryui/1.10.4/themes/smoothness/jquery-ui.css +1178 -0
  65. package/src/util/combinators.js +68 -0
  66. package/src/util/date.js +51 -0
  67. package/src/util/errors.js +55 -0
  68. package/src/util/helpers.js +131 -0
  69. package/src/util/inherit.js +28 -0
  70. package/src/util/ip.js +54 -0
  71. package/src/util/logger.js +83 -0
  72. package/src/util/middleware.js +256 -0
  73. package/src/util/scopedLogger.js +47 -0
  74. package/src/views/_footer.ejs +20 -0
  75. package/src/views/_header.ejs +113 -0
  76. package/src/views/_imposter.ejs +8 -0
  77. package/src/views/config.ejs +71 -0
  78. package/src/views/docs/api/behaviors/copy.ejs +427 -0
  79. package/src/views/docs/api/behaviors/decorate.ejs +182 -0
  80. package/src/views/docs/api/behaviors/lookup.ejs +220 -0
  81. package/src/views/docs/api/behaviors/shellTransform.ejs +153 -0
  82. package/src/views/docs/api/behaviors/wait.ejs +121 -0
  83. package/src/views/docs/api/behaviors.ejs +141 -0
  84. package/src/views/docs/api/contracts/addStub-description.ejs +10 -0
  85. package/src/views/docs/api/contracts/addStub.ejs +10 -0
  86. package/src/views/docs/api/contracts/config-description.ejs +32 -0
  87. package/src/views/docs/api/contracts/config.ejs +23 -0
  88. package/src/views/docs/api/contracts/home-description.ejs +18 -0
  89. package/src/views/docs/api/contracts/home.ejs +13 -0
  90. package/src/views/docs/api/contracts/imposter-description.ejs +439 -0
  91. package/src/views/docs/api/contracts/imposter.ejs +182 -0
  92. package/src/views/docs/api/contracts/imposters-description.ejs +13 -0
  93. package/src/views/docs/api/contracts/imposters.ejs +13 -0
  94. package/src/views/docs/api/contracts/logs-description.ejs +3 -0
  95. package/src/views/docs/api/contracts/logs.ejs +14 -0
  96. package/src/views/docs/api/contracts/stub-description.ejs +4 -0
  97. package/src/views/docs/api/contracts/stub.ejs +7 -0
  98. package/src/views/docs/api/contracts/stubs-description.ejs +4 -0
  99. package/src/views/docs/api/contracts/stubs.ejs +11 -0
  100. package/src/views/docs/api/contracts.ejs +133 -0
  101. package/src/views/docs/api/errors.ejs +64 -0
  102. package/src/views/docs/api/fault/connectionReset.ejs +31 -0
  103. package/src/views/docs/api/fault/randomDataThenClose.ejs +31 -0
  104. package/src/views/docs/api/faults.ejs +57 -0
  105. package/src/views/docs/api/injection.ejs +426 -0
  106. package/src/views/docs/api/json.ejs +205 -0
  107. package/src/views/docs/api/jsonpath.ejs +210 -0
  108. package/src/views/docs/api/mocks.ejs +130 -0
  109. package/src/views/docs/api/overview.ejs +968 -0
  110. package/src/views/docs/api/predicates/and.ejs +62 -0
  111. package/src/views/docs/api/predicates/contains.ejs +64 -0
  112. package/src/views/docs/api/predicates/deepEquals.ejs +114 -0
  113. package/src/views/docs/api/predicates/endsWith.ejs +66 -0
  114. package/src/views/docs/api/predicates/equals.ejs +125 -0
  115. package/src/views/docs/api/predicates/exists.ejs +118 -0
  116. package/src/views/docs/api/predicates/inject.ejs +67 -0
  117. package/src/views/docs/api/predicates/matches.ejs +66 -0
  118. package/src/views/docs/api/predicates/not.ejs +52 -0
  119. package/src/views/docs/api/predicates/or.ejs +79 -0
  120. package/src/views/docs/api/predicates/startsWith.ejs +62 -0
  121. package/src/views/docs/api/predicates.ejs +382 -0
  122. package/src/views/docs/api/proxies.ejs +191 -0
  123. package/src/views/docs/api/proxy/addDecorateBehavior.ejs +115 -0
  124. package/src/views/docs/api/proxy/addWaitBehavior.ejs +96 -0
  125. package/src/views/docs/api/proxy/injectHeaders.ejs +91 -0
  126. package/src/views/docs/api/proxy/predicateGenerators.ejs +600 -0
  127. package/src/views/docs/api/proxy/proxyModes.ejs +495 -0
  128. package/src/views/docs/api/stubs.ejs +391 -0
  129. package/src/views/docs/api/xpath.ejs +281 -0
  130. package/src/views/docs/cli/configFiles.ejs +133 -0
  131. package/src/views/docs/cli/customFormatters.ejs +53 -0
  132. package/src/views/docs/cli/help.ejs +6 -0
  133. package/src/views/docs/cli/replay.ejs +42 -0
  134. package/src/views/docs/cli/restart.ejs +10 -0
  135. package/src/views/docs/cli/save.ejs +68 -0
  136. package/src/views/docs/cli/start.ejs +234 -0
  137. package/src/views/docs/cli/stop.ejs +32 -0
  138. package/src/views/docs/commandLine.ejs +93 -0
  139. package/src/views/docs/communityExtensions.ejs +233 -0
  140. package/src/views/docs/gettingStarted.ejs +146 -0
  141. package/src/views/docs/mentalModel.ejs +51 -0
  142. package/src/views/docs/protocols/custom.ejs +231 -0
  143. package/src/views/docs/protocols/http.ejs +238 -0
  144. package/src/views/docs/protocols/https.ejs +246 -0
  145. package/src/views/docs/protocols/smtp.ejs +142 -0
  146. package/src/views/docs/protocols/tcp.ejs +431 -0
  147. package/src/views/docs/security.ejs +38 -0
  148. package/src/views/faqs.ejs +65 -0
  149. package/src/views/feed.ejs +33 -0
  150. package/src/views/imposter.ejs +22 -0
  151. package/src/views/imposters.ejs +33 -0
  152. package/src/views/index.ejs +89 -0
  153. package/src/views/license.ejs +30 -0
  154. package/src/views/logs.ejs +77 -0
  155. package/src/views/releases/v1.1.0.ejs +55 -0
  156. package/src/views/releases/v1.1.36.ejs +84 -0
  157. package/src/views/releases/v1.1.72.ejs +92 -0
  158. package/src/views/releases/v1.10.0.ejs +108 -0
  159. package/src/views/releases/v1.11.0.ejs +109 -0
  160. package/src/views/releases/v1.12.0.ejs +96 -0
  161. package/src/views/releases/v1.13.0.ejs +118 -0
  162. package/src/views/releases/v1.14.0.ejs +107 -0
  163. package/src/views/releases/v1.14.1.ejs +94 -0
  164. package/src/views/releases/v1.15.0.ejs +113 -0
  165. package/src/views/releases/v1.16.0.ejs +104 -0
  166. package/src/views/releases/v1.2.0.ejs +78 -0
  167. package/src/views/releases/v1.2.103.ejs +86 -0
  168. package/src/views/releases/v1.2.122.ejs +86 -0
  169. package/src/views/releases/v1.2.30.ejs +84 -0
  170. package/src/views/releases/v1.2.45.ejs +84 -0
  171. package/src/views/releases/v1.2.56.ejs +79 -0
  172. package/src/views/releases/v1.3.0.ejs +86 -0
  173. package/src/views/releases/v1.3.1.ejs +100 -0
  174. package/src/views/releases/v1.4.0.ejs +96 -0
  175. package/src/views/releases/v1.4.1.ejs +103 -0
  176. package/src/views/releases/v1.4.2.ejs +100 -0
  177. package/src/views/releases/v1.4.3.ejs +113 -0
  178. package/src/views/releases/v1.5.0.ejs +104 -0
  179. package/src/views/releases/v1.5.1.ejs +91 -0
  180. package/src/views/releases/v1.6.0.ejs +109 -0
  181. package/src/views/releases/v1.7.0.ejs +113 -0
  182. package/src/views/releases/v1.7.1.ejs +90 -0
  183. package/src/views/releases/v1.7.2.ejs +96 -0
  184. package/src/views/releases/v1.8.0.ejs +121 -0
  185. package/src/views/releases/v1.9.0.ejs +111 -0
  186. package/src/views/releases/v2.0.0.ejs +159 -0
  187. package/src/views/releases/v2.1.0.ejs +121 -0
  188. package/src/views/releases/v2.1.1.ejs +106 -0
  189. package/src/views/releases/v2.1.2.ejs +84 -0
  190. package/src/views/releases/v2.2.0.ejs +115 -0
  191. package/src/views/releases/v2.2.1.ejs +102 -0
  192. package/src/views/releases/v2.3.0.ejs +121 -0
  193. package/src/views/releases/v2.3.1.ejs +100 -0
  194. package/src/views/releases/v2.3.2.ejs +102 -0
  195. package/src/views/releases/v2.3.3.ejs +97 -0
  196. package/src/views/releases/v2.4.0.ejs +114 -0
  197. package/src/views/releases/v2.5.0.ejs +51 -0
  198. package/src/views/releases/v2.6.0.ejs +35 -0
  199. package/src/views/releases/v2.7.0.ejs +32 -0
  200. package/src/views/releases/v2.8.0.ejs +36 -0
  201. package/src/views/releases/v2.8.1.ejs +7 -0
  202. package/src/views/releases/v2.8.2.ejs +26 -0
  203. package/src/views/releases/v2.9.0.ejs +32 -0
  204. package/src/views/releases/v2.9.1.ejs +10 -0
  205. package/src/views/releases.ejs +26 -0
  206. package/src/views/sitemap.ejs +36 -0
  207. package/src/views/support.ejs +14 -0
@@ -0,0 +1,220 @@
1
+ <table>
2
+ <tr>
3
+ <th>Parameter</th>
4
+ <th>Type</th>
5
+ <th>Description</th>
6
+ </tr>
7
+ <tr>
8
+ <td><code>lookup</code></td>
9
+ <td>An object</td>
10
+ <td>An object specifying the key (copied from a request field), the data source,
11
+ and the response token</td>
12
+ </tr>
13
+ <tr>
14
+ <td><code>lookup.key</code></td>
15
+ <td>An object</td>
16
+ <td>The information on how to select the key from the request.</td>
17
+ </tr>
18
+ <tr>
19
+ <td><code>lookup.key.from</code></td>
20
+ <td>A string or an object</td>
21
+ <td>The name of the request field to select from, or, if the request field is an object,
22
+ then an object specifying the path to the request field. For example,
23
+ <pre><code>{ "from": "body" }</code></pre> and <pre><code>{ "from": { "query": "q" } }</code></pre>
24
+ are both valid.</td>
25
+ </tr>
26
+ <tr>
27
+ <td><code>lookup.key.using</code></td>
28
+ <td>An object</td>
29
+ <td>The configuration needed to select the key from the response</td>
30
+ </tr>
31
+ <tr>
32
+ <td><code>lookup.key.using.method</code></td>
33
+ <td>A string</td>
34
+ <td>The method used to select the key from the request. Allowed values are <code>regex</code>,
35
+ <code>xpath</code>, and <code>jsonpath</code>.</td>
36
+ </tr>
37
+ <tr>
38
+ <td><code>lookup.key.using.selector</code></td>
39
+ <td>A string</td>
40
+ <td>The selector used to select the key from the request. For a <code>regex</code>, this would
41
+ be the pattern, and the replacement value will be the entire match by default.
42
+ <code>xpath</code> and <code>jsonpath</code> selectors work on XML and JSON documents. If the request
43
+ value does not match the selector (including through XML or JSON parsing errors), nothing is replaced.</td>
44
+ </tr>
45
+ <tr>
46
+ <td><code>lookup.key.using.ns</code></td>
47
+ <td>An object</td>
48
+ <td>For <code>xpath</code> selectors, the <code>ns</code> object maps namespace aliases to URLs</td>
49
+ </tr>
50
+ <tr>
51
+ <td><code>lookup.key.using.options</code></td>
52
+ <td>An object</td>
53
+ <td>For <code>regex</code> selectors, the <code>options</code> object describes the regular expression options</td>
54
+ </tr>
55
+ <tr>
56
+ <td><code>lookup.key.using.options.ignoreCase</code></td>
57
+ <td>A boolean</td>
58
+ <td>Uses a <a href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/ignoreCase'>case-insensitive</a>
59
+ regular expression</td>
60
+ </tr>
61
+ <tr>
62
+ <td><code>lookup.key.using.options.multiline</code></td>
63
+ <td>A boolean</td>
64
+ <td>Uses a <a href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/multiline'>multiline</a>
65
+ regular expression</td>
66
+ </tr>
67
+ <tr>
68
+ <td><code>lookup.key.index</code></td>
69
+ <td>An int (defaults to 0)</td>
70
+ <td>Each of the selection options returns an array: <code>regex</code> returns an array of parenthesized
71
+ gropus (with the entire match in the 0th index), and <code>jsonpath</code> and <code>xpath</code>
72
+ return an array of matches. This field selects the appropriate value from the array to use as
73
+ the lookup key into the data source.</td>
74
+ </tr>
75
+ <tr>
76
+ <td><code>lookup.fromDataSource</code></td>
77
+ <td>An object</td>
78
+ <td>Configuration for the external data source to lookup data based on the key. Each
79
+ <code>lookup</code> configuration may only specify one data source.</td>
80
+ </tr>
81
+ <tr>
82
+ <td><code>lookup.fromDataSource.csv</code></td>
83
+ <td>An object</td>
84
+ <td>Configuration for using a CSV file as the data source</td>
85
+ </tr>
86
+ <tr>
87
+ <td><code>lookup.fromDataSource.csv.path</code></td>
88
+ <td>A string</td>
89
+ <td>The path to the CSV file, which must be readable by the <code>mb</code> process</td>
90
+ </tr>
91
+ <tr>
92
+ <td><code>lookup.fromDataSource.csv.keyColumn</code></td>
93
+ <td>A string</td>
94
+ <td>The header of the column to scan for a match against the key. If a match is
95
+ found, the entire row will be returned.</td>
96
+ </tr>
97
+ <tr>
98
+ <td><code>lookup.fromDataSource.csv.delimiter</code></td>
99
+ <td>A string(default to comma ,)</td>
100
+ <td>The delimiter separated colums in CSV file.</td>
101
+ </tr>
102
+ <tr>
103
+ <td><code>lookup.into</code></td>
104
+ <td>A string</td>
105
+ <td>The token to replace in the response with the selected request value. There is
106
+ no need to specify which field in the response the token will be in; all response
107
+ tokens will be replaced in all response fields. A successful match will return a
108
+ hashmap or dictionary type object, with named indexes. For example, if you specify
109
+ <pre><code>{ "into": "${NAME}" }</code></pre> as your token configuration, and the
110
+ data source returned a row containing "first" and "last" fields, then
111
+ <code>${NAME}["first"]</code> and <code>${NAME}["last"]</code> will be replaced by
112
+ the appropriate data. You can quote the field name with double quotes, single quotes,
113
+ or no quotes at all.</td>
114
+ </tr>
115
+ </table>
116
+
117
+ <p>The <code>lookup</code> behavior supports dynamically replacing values in the response with something that
118
+ comes from an external data source. Looking up the values from the data source requires first selecting
119
+ the key value from the request. The key selection and replacement behavior in the response mirrors the
120
+ functionality for the <code>copy</code> behavior. We'll look at the following examples:</p>
121
+
122
+ <ul class='bullet-list'>
123
+ <li><a href='#lookup-regex-replacement'>Lookup up from a CSV file based on a key selected with a regular expression</a></li>
124
+ </ul>
125
+
126
+ <p class='info-icon'>Look at the <code>copy</code> examples to see how to do advanced key
127
+ selection using jsonpath and xpath.</p>
128
+
129
+ <testScenario name='lookup'>
130
+
131
+ <p>We'll use the following CSV file for these examples, saved as "values.csv" in the
132
+ current working directory of the <code>mb</code> process (you can always use an absolute path):</p>
133
+
134
+ <step type='file' filename='<%= process.cwd() %>/values.csv'>
135
+ <pre><code>State_ID,code,Name,price,tree,jobs
136
+ 1111111,400,liquid,235651,mango,farmer
137
+ 9856543,404,solid,54564564,orange,miner
138
+ 2222222,500,water,12564,pine,shepherd
139
+ 1234564,200,plasma,2656,guava,lumberjack
140
+ 9999999,200,lovers,9999,dogwood,steel worker</code></pre>
141
+ </step>
142
+
143
+ <h3 id='lookup-regex-replacement'>Lookup up from a CSV file based on a key selected with a regular expression</h3>
144
+
145
+ <p>The following example shows selecting the key using a regular expression to match against the path. We're
146
+ using the <code>index</code> field to select the first parenthesized group in the regular expression.</p>
147
+
148
+ <step type='http'>
149
+ <pre><code>POST /imposters HTTP/1.1
150
+ Host: localhost:<%= port %>
151
+ Accept: application/json
152
+ Content-Type: application/json
153
+
154
+ {
155
+ "port": 9595,
156
+ "protocol": "http",
157
+ "stubs": [
158
+ {
159
+ "responses": [
160
+ {
161
+ "is": {
162
+ "statusCode": "<strong class='highlight1'>${row}['code']</strong>",
163
+ "headers": {
164
+ "X-Tree": "<strong class='highlight1'>${row}['tree']</strong>"
165
+ },
166
+ "body": "Hello <strong class='highlight1'>${row}['Name']</strong>, have you done your <strong class='highlight1'>${row}['jobs']</strong> today?"
167
+ },
168
+ "behaviors": [
169
+ {
170
+ "lookup": {
171
+ "key": <strong class='highlight2'>{
172
+ "from": "path",
173
+ "using": { "method": "regex", "selector": "/(.*)$" },
174
+ "index": 1
175
+ }</strong>,
176
+ "fromDataSource": {
177
+ "csv": {
178
+ "path": "<%= process.cwd() %>/values.csv",
179
+ "keyColumn": "Name",
180
+ "delimiter": ","
181
+ }
182
+ },
183
+ "into": "<strong class='highlight1'>${row}</strong>"
184
+ }
185
+ }
186
+ ]
187
+ }
188
+ ]
189
+ }
190
+ ]
191
+ }</code></pre>
192
+ </step>
193
+
194
+ <p>As with the <code>copy</code> behavior, we can plug tokens into any of the response fields
195
+ (including the <code>statusCode</code>). Let's see what happens when we craft a request to match
196
+ all of those selectors:</p>
197
+
198
+ <step type='http'>
199
+ <pre><code>GET /<strong class='highlight2'>liquid</strong> HTTP/1.1
200
+ Host: localhost:9595</code></pre>
201
+
202
+ <assertResponse>
203
+ <pre><code>HTTP/1.1 <strong class='highlight1'>400</strong> Bad Request
204
+ X-Tree: <strong class='highlight1'>mango</strong>
205
+ Connection: close
206
+ Date: <volatile>Thu, 28 Dec 2016 11:37:31 GMT</volatile>
207
+ Transfer-Encoding: chunked
208
+
209
+ Hello <strong class='highlight1'>liquid</strong>, have you done your <strong class='highlight1'>farmer</strong> today?</code></pre>
210
+ </assertResponse>
211
+ </step>
212
+
213
+ <step type='http'>
214
+ <code class='hidden'>DELETE /imposters/9595 HTTP/1.1
215
+ Host: localhost:<%= port %></code>
216
+ </step>
217
+
218
+ <step type='file' filename='<%= process.cwd() %>/values.csv' delete='true'><code>
219
+ </code></step>
220
+ </testScenario>
@@ -0,0 +1,153 @@
1
+ <table>
2
+ <tr>
3
+ <th>Parameter</th>
4
+ <th>Type</th>
5
+ <th>Description</th>
6
+ </tr>
7
+ <tr>
8
+ <td><code>shellTransform</code></td>
9
+ <td>A string</td>
10
+ <td>Represents the path to a command line application. The application should
11
+ retrieve the JSON-encoded <code>request</code> and <code>response</code> from the environment
12
+ and print out the transformed <code>response</code> to <code>stdout</code>.
13
+
14
+ <p class='warning-icon'>The <code><a href='/docs/commandLine'>--allowInjection</a></code> command
15
+ line flag must be set to support this behavior.</p>
16
+ </td>
17
+ </tr>
18
+ </table>
19
+
20
+ <p>The <code>shellTransform</code> behavior plays a similar role as the <code>decorate</code> behavior,
21
+ enabling a programmatic transformation of the response. However, you don't have to write the transformation
22
+ logic in JavaScript -- it can be in the language of your choice.</p>
23
+
24
+ <p class='warning-icon'>Since it allows shelling out to another application,
25
+ the <a href='/docs/commandLine'>--allowInjection</a> flag must be passed in to <code>mb</code> on startup.</p>
26
+
27
+ <p>mountebank will expose the following environment variables to your shell application:</p>
28
+
29
+ <ul class='bullet-list'>
30
+ <li><code>MB_REQUEST</code> which contains the JSON request as a string, and</li>
31
+ <li><code>MB_RESPONSE</code> which contains the current JSON response as a string</li>
32
+ </ul>
33
+
34
+ <p>The application should write to stdout a JSON representation of
35
+ the transformed response.</p>
36
+
37
+ <p>We'll show a simple example of shelling out to plug in response values based on an external data source.</p>
38
+
39
+ <h3 id='external-datasource'>Using an external data source</h3>
40
+
41
+ <p>At times you may find it convenient to use an external data store to fill in dynamic values based on
42
+ data coming in from the request. You can combine an <code>is</code> canned response with a
43
+ <code>shellTransform</code> behavior to achieve this, regardless of what the external data source is.
44
+ For this example, we'll assume it's a simple pipe-delimited file mapping customer ids to names. We'll
45
+ save it as <code>names.csv</code> in the directory we run <code>mb</code> from.</p>
46
+
47
+ <p class='info-icon'>Note that, by reformatting the file, this example would more easily be satisfied
48
+ by the <code>lookup</code> behavior.</p>
49
+
50
+ <testScenario name='shellTransform'>
51
+ <step type='file' filename='<%= process.cwd() %>/names.txt'>
52
+ <pre><code>123|Frodo Baggins
53
+ 234|Samwise Gamgee
54
+ 345|Gandalf the White
55
+ 456|Smeagol</code></pre>
56
+ </step>
57
+
58
+ <p>We expect the incoming http request to specify the id in the URL, and we want to represent the name
59
+ in the response body. We'll set up our imposter like this:</p>
60
+
61
+ <step type='http'>
62
+ <pre><code>POST /imposters HTTP/1.1
63
+ Host: localhost:<%= port %>
64
+ Accept: application/json
65
+ Content-Type: application/json
66
+
67
+ {
68
+ "port": 5555,
69
+ "protocol": "http",
70
+ "stubs": [
71
+ {
72
+ "predicates": [{ "matches": { "path": "/accounts/\\d+" } }],
73
+ "responses": [
74
+ {
75
+ "is": { "body": "Hello, <strong class='highlight1'>${YOU}!</strong>" },
76
+ "behaviors": [
77
+ { "shellTransform": "node <%= process.cwd() %>/addName.js" }
78
+ ]
79
+ }
80
+ ]
81
+ }
82
+ ]
83
+ }
84
+ </code></pre>
85
+ </step>
86
+
87
+ <p>In this example, we're shelling out to a node.js application, which isn't much different than
88
+ using a <code>decorate</code> function. However, that's just to keep it simple; the application
89
+ could be written in any language.</p>
90
+
91
+ <p>Let's create the <code>addName.js</code> file..</p>
92
+
93
+ <step type='file' filename='<%= process.cwd() %>/addName.js'>
94
+ <pre><code>var request = JSON.parse(process.env.MB_REQUEST),
95
+ response = JSON.parse(process.env.MB_RESPONSE),
96
+ requestId = request.path.replace('/accounts/', ''),
97
+ fs = require('fs'),
98
+ mappings = fs.readFileSync('<%= process.cwd() %>/names.txt', { encoding: 'utf8' }),
99
+ lines = mappings.split(/\r?\n/);
100
+
101
+ for (let i = 0; i &lt; lines.length; i += 1) {
102
+ var fields = lines[i].split('|'),
103
+ id = fields[0],
104
+ name = fields[1];
105
+
106
+ if (requestId === id) {
107
+ <strong class='highlight1'>response.body = response.body.replace('${YOU}', name);</strong>
108
+ }
109
+ }
110
+
111
+ console.log(JSON.stringify(response));</code></pre>
112
+ </step>
113
+
114
+ <p>Now we can test it out:</p>
115
+
116
+ <step type='http'>
117
+ <pre><code>GET /accounts/234 HTTP/1.1
118
+ Host: localhost:5555</code></pre>
119
+
120
+ <assertResponse>
121
+ <pre><code>HTTP/1.1 200 OK
122
+ Connection: close
123
+ Date: <volatile>Wed, 07 Jan 2016 21:27:14 GMT</volatile>
124
+ Transfer-Encoding: chunked
125
+
126
+ Hello, <strong class='highlight1'>Samwise Gamgee!</strong></code></pre>
127
+ </assertResponse>
128
+ </step>
129
+
130
+ <p>And again...</p>
131
+
132
+ <step type='http'>
133
+ <pre><code>GET /accounts/456 HTTP/1.1
134
+ Host: localhost:5555</code></pre>
135
+
136
+ <assertResponse>
137
+ <pre><code>HTTP/1.1 200 OK
138
+ Connection: close
139
+ Date: <volatile>Wed, 07 Jan 2016 21:27:14 GMT</volatile>
140
+ Transfer-Encoding: chunked
141
+
142
+ Hello, <strong class='highlight1'>Smeagol!</strong></code></pre>
143
+ </assertResponse>
144
+ </step>
145
+
146
+ <step type='http'>
147
+ <code class='hidden'>DELETE /imposters/5555 HTTP/1.1
148
+ Host: localhost:<%= port %></code>
149
+ </step>
150
+
151
+ <step type='file' filename='<%= process.cwd() %>/names.txt' delete='true'><code></code></step>
152
+ <step type='file' filename='<%= process.cwd() %>/addName.js' delete='true'><code></code></step>
153
+ </testScenario>
@@ -0,0 +1,121 @@
1
+ <table>
2
+ <tr>
3
+ <th>Parameter</th>
4
+ <th>Type</th>
5
+ <th>Description</th>
6
+ </tr>
7
+ <tr>
8
+ <td><code>wait</code></td>
9
+ <td>A positive integer, or a string</td>
10
+ <td>If a number is passed in, mountebank will wait that number of milliseconds before returning.
11
+ If a string is passed in, it is expected to be a parameterless JavaScript function that returns the number
12
+ of milliseconds to wait.
13
+
14
+ <p class='warning-icon'>The <code><a href='/docs/commandLine'>--allowInjection</a></code> command
15
+ line flag must be set to support passing in a JavaScript function</p>
16
+ </td>
17
+ </tr>
18
+ </table>
19
+
20
+ <p>The <code>wait</code> behavior is conceptually quite simple. Just pass in a number of milliseconds
21
+ to wait into the behavior:</p>
22
+
23
+ <testScenario name='wait'>
24
+ <step type='http'>
25
+ <pre><code>POST /imposters HTTP/1.1
26
+ Host: localhost:<%= port %>
27
+ Accept: application/json
28
+ Content-Type: application/json
29
+
30
+ {
31
+ "port": 4545,
32
+ "protocol": "http",
33
+ "stubs": [
34
+ {
35
+ "responses": [
36
+ {
37
+ "is": { "body": "This took at least half a second to send" },
38
+ "behaviors": [
39
+ { "wait": 500 }
40
+ ]
41
+ }
42
+ ]
43
+ }
44
+ ]
45
+ }</code></pre>
46
+ </step>
47
+
48
+ <p>Now we can call the imposter. But have some patience, it'll take the better portion of a second
49
+ before you'll get your response...</p>
50
+
51
+ <step type='http'>
52
+ <pre><code>GET / HTTP/1.1
53
+ Host: localhost:4545</code></pre>
54
+
55
+ <assertResponse>
56
+ <pre><code>HTTP/1.1 200 OK
57
+ Connection: close
58
+ Date: <volatile>Thu, 01 Jan 2015 02:30:31 GMT</volatile>
59
+ Transfer-Encoding: chunked
60
+
61
+ This took at least half a second to send</code></pre>
62
+ </assertResponse>
63
+ </step>
64
+
65
+ <step type='http'>
66
+ <code class='hidden'>DELETE /imposters/4545 HTTP/1.1
67
+ Host: localhost:<%= port %></code>
68
+ </step>
69
+
70
+ <p>If a more advanced wait strategy is needed, you can also specify a function body as the wait variable.
71
+ As long as the function returns a number, mountebank can be configured to wait a variable
72
+ amount of time.</p>
73
+
74
+ <p class='warning-icon'>The <code><a href='/docs/commandLine'>--allowInjection</a></code> command
75
+ line flag must be set to support using a function to determine the wait time.</p>
76
+
77
+ <step type='http'>
78
+ <pre><code>POST /imposters HTTP/1.1
79
+ Host: localhost:<%= port %>
80
+ Accept: application/json
81
+ Content-Type: application/json
82
+
83
+ {
84
+ "port": 4545,
85
+ "protocol": "http",
86
+ "stubs": [
87
+ {
88
+ "responses": [
89
+ {
90
+ "is": { "body": "This took at least 100 to 1000 ms to send" },
91
+ "behaviors": [
92
+ { "wait": "function() { return Math.floor(Math.random() * 901) + 100; }" }
93
+ ]
94
+ }
95
+ ]
96
+ }
97
+ ]
98
+ }</code></pre>
99
+ </step>
100
+
101
+ <p>The result of the function determines the wait time:</p>
102
+
103
+ <step type='http'>
104
+ <pre><code>GET / HTTP/1.1
105
+ Host: localhost:4545</code></pre>
106
+
107
+ <assertResponse>
108
+ <pre><code>HTTP/1.1 200 OK
109
+ Connection: close
110
+ Date: <volatile>Thu, 01 Jan 2015 02:30:31 GMT</volatile>
111
+ Transfer-Encoding: chunked
112
+
113
+ This took at least 100 to 1000 ms to send</code></pre>
114
+ </assertResponse>
115
+ </step>
116
+
117
+ <step type='http'>
118
+ <code class='hidden'>DELETE /imposters/4545 HTTP/1.1
119
+ Host: localhost:<%= port %></code>
120
+ </step>
121
+ </testScenario>
@@ -0,0 +1,141 @@
1
+ <%
2
+ title = 'behaviors'
3
+ description = 'Additional behaviors supported by all mountebank responses'
4
+ %>
5
+
6
+ <%- include('../../_header') -%>
7
+
8
+ <h1>Stub Behaviors</h1>
9
+
10
+ <p>You can alter the response created by adding to the <code>behaviors</code> array, which acts as a
11
+ middleware pipeline of transformations to the response. At the moment, mountebank accepts
12
+ the following behaviors:</p>
13
+
14
+ <table id='behaviors'>
15
+ <tr>
16
+ <th>Behavior</th>
17
+ <th>Description</th>
18
+ </tr>
19
+ <tr>
20
+ <td><code>wait</code></td>
21
+ <td>Adds latency to a response by waiting a specified number of milliseconds before sending
22
+ the response.
23
+
24
+ <p class='info-icon'>Tip: Setting the <a href='/docs/api/proxies'><code>addWaitBehavior</code></a>
25
+ flag on proxies will automatically add this behavior with the actual time it took to call the
26
+ downstream service</p>
27
+ </td>
28
+ </tr>
29
+ <tr>
30
+ <td><code>copy</code></td>
31
+ <td>Copies one or more values from request fields into the response. You can tokenize the response
32
+ and select values from request fields using regular expressions, xpath, or jsonpath.</td>
33
+ </tr>
34
+ <tr>
35
+ <td><code>lookup</code></td>
36
+ <td>Queries an external data source for data based on a key selected from the request. Like the
37
+ <code>copy</code> behavior, you can tokenize the response and select the key from the request
38
+ using regular expressions, xpath, or jsonpath.</td>
39
+ </tr>
40
+ <tr>
41
+ <td><code>decorate</code></td>
42
+ <td>Post-processes the response using JavaScript injection before sending it. Post-processing opens
43
+ up a world of opportunities - you can use a <code>decorate</code> behavior to add data to a proxied
44
+ response or substitute data from the request into the response, for example. The value passed into
45
+ the <code>decorate</code> behavior is a JavaScript function that can take up to three values: the
46
+ request, the response, and a logger. You can either mutate the response passed in (and return nothing),
47
+ or return an altogether new response.
48
+
49
+ <p class='info-icon'>Tip: Setting the <a href='/docs/api/proxies'><code>addDecorateBehavior</code></a>
50
+ flag on proxies will automatically add this function as decorate behavior on the generated responses</p>
51
+
52
+ <p class='warning-icon'>The <code><a href='/docs/commandLine'>--allowInjection</a></code> command
53
+ line flag must be set to support this behavior</p>
54
+ </td>
55
+ </tr>
56
+ <tr>
57
+ <td><code>shellTransform</code></td>
58
+ <td>Like <code>decorate</code>, a <code>shellTransform</code> post-processes the response, but
59
+ instead of using JavaScript injection, it shells out to another application. That application
60
+ will get two command line parameters representing the request JSON and the response JSON, and
61
+ should print to <code>stdout</code> the transformed response JSON.
62
+
63
+ <p class='warning-icon'>The <code><a href='/docs/commandLine'>--allowInjection</a></code> command
64
+ line flag must be set to support this behavior.</p>
65
+ </td>
66
+ </tr>
67
+ </table>
68
+
69
+ <p>Multiple behaviors can be added to a response, and they will be executed in array order. While
70
+ each object in the array may contain only one type of behavior, you are free to repeat any
71
+ behavior as many times as you want. For example, take a look at the following response:</p>
72
+
73
+ <pre><code>{
74
+ "is": " { ... },
75
+ "behaviors": [
76
+ { "copy": { ... } },
77
+ { "decorate": "..." },
78
+ { "lookup": "..." },
79
+ { "shellTransform": "..." },
80
+ { "decorate": "..." },
81
+ { "wait": 500 },
82
+ { "shellTransform": "..." }
83
+ ]
84
+ }</code></pre>
85
+
86
+ <p>The ability to compose multiple behaviors together gives you complete control over the
87
+ response transformation.</p>
88
+
89
+ <h2>Examples</h2>
90
+
91
+ <p>Select the behavior below for relevant examples, which explore each type of behavior in isolation:</p>
92
+
93
+ <section class='accordion'>
94
+ <div>
95
+ <a class='section-toggler'
96
+ id='behavior-wait' name='behavior-wait' href='#behavior-wait'>
97
+ wait
98
+ </a>
99
+ <section>
100
+ <%- include('behaviors/wait') -%>
101
+ </section>
102
+ </div>
103
+ <div>
104
+ <a class='section-toggler'
105
+ id='behavior-copy' name='behavior-copy' href='#behavior-copy'>
106
+ copy
107
+ </a>
108
+ <section>
109
+ <%- include('behaviors/copy') -%>
110
+ </section>
111
+ </div>
112
+ <div>
113
+ <a class='section-toggler'
114
+ id='behavior-lookup' name='behavior-lookup' href='#behavior-lookup'>
115
+ lookup
116
+ </a>
117
+ <section>
118
+ <%- include('behaviors/lookup') -%>
119
+ </section>
120
+ </div>
121
+ <div>
122
+ <a class='section-toggler'
123
+ id='behavior-decorate' name='behavior-decorate' href='#behavior-decorate'>
124
+ decorate
125
+ </a>
126
+ <section>
127
+ <%- include('behaviors/decorate') -%>
128
+ </section>
129
+ </div>
130
+ <div>
131
+ <a class='section-toggler'
132
+ id='behavior-shellTransform' name='behavior-shellTransform' href='#behavior-shellTransform'>
133
+ shellTransform
134
+ </a>
135
+ <section>
136
+ <%- include('behaviors/shellTransform') -%>
137
+ </section>
138
+ </div>
139
+ </section>
140
+
141
+ <%- include('../../_footer') -%>
@@ -0,0 +1,10 @@
1
+ <div id='addStub-index-description'>
2
+ <p>The array index to add the stub. Stubs are always evaluated in array order, and the first stub
3
+ whose predicates match the request will be used. If you leave this value off the request, the stub
4
+ will be added to the end of the array.</p>
5
+ </div>
6
+
7
+ <div id='addStub-stub-description'>
8
+ <p>The stub to add. See the imposter contract for more information.</p>
9
+ <p class='info-icon'>More information: <a href='/docs/api/contracts?type=imposter'>imposter contract</a></p>
10
+ </div>
@@ -0,0 +1,10 @@
1
+ <pre id='addStub-specification'><code>{
2
+ <span id='addStub-index'><%- indent(2) %>&quot;index&quot;: 1,</span>
3
+ <span id='addStub-stub'><%- indent(2) %>&quot;stub&quot;: {
4
+ &quot;responses&quot;: [{
5
+ &quot;is&quot;: {
6
+ &quot;body&quot;: &quot;Hello, world!&quot;
7
+ }
8
+ }]
9
+ }</span>
10
+ }</code></pre>