Skip to content

Commit d04b007

Browse files
committed
a guideline for converters
- documented guideline under CONTRIBUTING.md - added universal multipart management - added allHeaders property - updated tests with new information - update converters with new usage of allHeaders and removing some usage of cookies
1 parent 81e89bc commit d04b007

63 files changed

Lines changed: 529 additions & 285 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CONTRIBUTING.md

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,281 @@ project:
133133

134134
**IMPORTANT**: By submitting a patch, you agree to allow the project owner to
135135
license your work under the same license as that used by the project.
136+
137+
## Creating New Conversion Targets
138+
139+
Please start by browsing for [available targets](/src/targets) and inspect each implementation.
140+
141+
a target is a simple module with a constructor that accepts two parameters: `source` and `options`,
142+
where `source` is the HAR Object to process, and `options` is an optional object with any target
143+
specific flags *(used for customizing the output)*.
144+
145+
### Conversion Rules
146+
147+
1. start by reading an understanding the [HAR](http://www.softwareishard.com/blog/har-12-spec/#request) format.
148+
2. utilize utility properties created for convenience (`source.headersObj`, `source.uriObj` etc ...) *see below for mode details*
149+
3. follow the guidelines below for best practices and consistency.
150+
151+
### Guidelines
152+
153+
Using the following example of a request object, HTTP Snippet will pre-process data and create some additional properties:
154+
155+
| `source.fullUrl` | the full & final url, including all query string values |
156+
| `source.uriObj` | the url parsed with `url.parse()`. compatible with `url.format` |
157+
| `source.queryObj` | a key => value pair, "normalized" version of `source.queryString`, adds additional query string values from the `source.url` |
158+
| `source.headersObj` | a key => value pair, "normalized" version of `source.headers`, header names are lowercased |
159+
| `source.allHeaders` | same as `source.headersObj` but with `cookies` header and populated from `source.cookies` array |
160+
| `source.postData.paramsObj` | a key => value pair, "normalized" version of `source.postData.params`, only for `source.postData.mimeType` = `application/x-www-form-urlencoded` |
161+
| `source.postData.jsonObj` | the parsed value of `source.postData.text`, only for `source.postData.mimeType` = `application/json` *(or equivalent mimeTypes)* |
162+
163+
###### Sample Incoming Request Object
164+
165+
```js
166+
{
167+
method: 'POST',
168+
url: 'http://mockbin.com/har?key=value',
169+
httpVersion: 'HTTP/1.1',
170+
queryString: [
171+
{ name: 'foo', value: 'bar' },
172+
{ name: 'foo', value: 'baz' },
173+
{ name: 'baz', value: 'abc' }
174+
],
175+
headers: [
176+
{ name: 'Accept', value: 'application/json' },
177+
{ name: 'Content-Type', value: 'application/x-www-form-urlencoded' }
178+
],
179+
180+
cookies: [
181+
{ name: 'foo', value: 'bar' },
182+
{ name: 'bar', value: 'baz' }
183+
],
184+
185+
postData: {
186+
mimeType: 'application/x-www-form-urlencoded',
187+
params: [
188+
{ name: 'foo', value: 'bar' },
189+
{ name: 'foo', value: 'baz' },
190+
{ name: 'baz', value: 'abc' }
191+
]
192+
}
193+
}
194+
```
195+
196+
###### Processed Source Object
197+
198+
```js
199+
{
200+
method: 'POST',
201+
202+
// the base url value stripped of any the query string
203+
url: 'http://mockbin.com/har',
204+
205+
// the full & final url, including all query string values
206+
fullUrl: 'http://mockbin.com/har?foo=bar&foo=baz&baz=abc&key=value',
207+
208+
// the url parsed with url.parse()
209+
// compatible with url.format
210+
uriObj: {
211+
protocol: 'http:',
212+
slashes: true,
213+
auth: null,
214+
host: 'mockbin.com',
215+
port: null,
216+
hostname: 'mockbin.com',
217+
hash: null,
218+
search: 'key=value&baz=abc&foo=bar&foo=baz',
219+
query: { key: 'value', baz: 'abc', foo: [Object] },
220+
pathname: '/har',
221+
path: '/har?key=value&baz=abc&foo=bar&foo=baz',
222+
href: 'http://mockbin.com/har'
223+
},
224+
225+
httpVersion: 'HTTP/1.1',
226+
227+
// added to pass har-validator
228+
bodySize: 0,
229+
230+
// added to pass har-validator
231+
headersSize: 0,
232+
233+
queryString: [
234+
{ name: 'foo', value: 'bar' },
235+
{ name: 'foo', value: 'baz' },
236+
{ name: 'baz', value: 'abc' }
237+
],
238+
239+
// "normalized" version of `queryString`
240+
// adds any additional query string values from the url
241+
// compatible with "querystring" node module
242+
queryObj: {
243+
key: 'value',
244+
baz: 'abc',
245+
foo: [ 'bar', 'baz' ]
246+
},
247+
248+
headers: [
249+
{ name: 'Accept', value: 'application/json' },
250+
{ name: 'Content-Type', value: 'application/x-www-form-urlencoded' }
251+
],
252+
253+
// normalized headers array into a key => value object pair
254+
// header names are lowercased
255+
headersObj: {
256+
'accept': 'application/json',
257+
'content-type': 'application/x-www-form-urlencoded'
258+
},
259+
260+
// same as headersObj but with Cookies added (if any exist in cookies array)
261+
allHeaders: {
262+
'accept': 'application/json',
263+
'content-type': 'application/x-www-form-urlencoded',
264+
'cookie': 'foo=bar; bar=baz'
265+
},
266+
267+
cookies: [
268+
{ name: 'foo', value: 'bar' },
269+
{ name: 'bar', value: 'baz' }
270+
],
271+
272+
// see below for different scenarios
273+
postData: [Object]
274+
}
275+
```
276+
277+
###### application/x-www-form-urlencoded
278+
279+
```js
280+
postData: {
281+
// added to pass har-validator
282+
size: 0,
283+
284+
// original value
285+
mimeType: 'application/x-www-form-urlencoded',
286+
287+
// original value
288+
params: [
289+
{ name: 'foo', value: 'bar' },
290+
{ name: 'foo', value: 'baz' },
291+
{ name: 'baz', value: 'abc' }
292+
],
293+
294+
// "normalized" version of `params`
295+
// compatible with "querystring" node module
296+
paramsObj: {
297+
key: 'value',
298+
baz: 'abc',
299+
foo: [ 'bar', 'baz' ]
300+
}
301+
302+
// the raw body in plain text
303+
// this value will be always overwritten in this scenario
304+
text: 'baz=abc&foo=bar&foo=baz'
305+
306+
// see below
307+
jsonObj: false
308+
}
309+
```
310+
311+
###### application/json
312+
313+
*when postData.mimeType is one of: `application/json`, `text/json`, `text/x-json`, `application/x-json`*
314+
315+
```js
316+
postData: {
317+
// added to pass har-validator
318+
size: 0,
319+
320+
// original value
321+
mimeType: 'application/json',
322+
323+
// ignored
324+
params: [],
325+
326+
// default value
327+
paramsObj: false
328+
329+
// the raw body in plain text
330+
text: '"{\"foo\": \"bar\"}"'
331+
332+
// the parsed value of postData.text
333+
jsonObj: {
334+
foo: 'bar'
335+
}
336+
}
337+
```
338+
339+
Notes:
340+
341+
- In case of failure to parse `postData.text` as a JSON object, `postData.mimeType` is set to `text/plain`. this is done so that the implementing target, would still attempt to post the raw body as is.
342+
- This also emphasizes not to rely on `postData.mimeType` for the `Content-Type` header!
343+
344+
###### multipart/form-data
345+
346+
- will create/overwrite the `Content-Type` header if it does not exist, with the appropriate boundary flag.
347+
- will create/overwrite the `Content-Length` header
348+
349+
```js
350+
postData: {
351+
// added to pass har-validator
352+
size: 0,
353+
354+
// original value
355+
mimeType: 'multipart/form-data',
356+
357+
// parsed into text values
358+
params: [
359+
{
360+
name: 'foo',
361+
value: 'bar'
362+
}
363+
]
364+
365+
// ignored
366+
paramsObj: false
367+
368+
// the raw body in plain text
369+
// generated based on appropriately parsing the `params` into a multi-boundary content string
370+
// this value will be always overwritten in this scenario
371+
text: '----------------------------591447866569479977899212\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n----------------------------591447866569479977899212--'
372+
373+
// ignored
374+
jsonObj: false
375+
}
376+
```
377+
378+
###### multipart/form-data (File Uploads)
379+
380+
- will create/overwrite the `Content-Type` header if it does not exist, with the appropriate boundary flag.
381+
- will create/overwrite the `Content-Length` header
382+
- when no params[].value is present, will default to empty content
383+
384+
```js
385+
postData: {
386+
// added to pass har-validator
387+
size: 0,
388+
389+
// original value
390+
mimeType: 'multipart/form-data',
391+
392+
// parsed into text values
393+
params: [
394+
{
395+
name: 'foo',
396+
value: 'Hello World',
397+
fileName: 'test/fixtures/files/hello.txt',
398+
contentType: 'text/plain'
399+
}
400+
]
401+
402+
// ignored
403+
paramsObj: false
404+
405+
// the raw body in plain text
406+
// generated based on appropriately parsing the `params` into a multi-boundary content string
407+
// this value will be always overwritten in this scenario
408+
text: '----------------------------771333709043252625002993\r\nContent-Disposition: form-data; name=\"foo\"; filename=\"hello.txt\"\r\nContent-Type: text/plain\r\n\r\nHello World\r\n----------------------------771333709043252625002993--'
409+
410+
// ignored
411+
jsonObj: false
412+
}
413+
```

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"chalk": "^1.0.0",
4141
"commander": "^2.6.0",
4242
"debug": "^2.1.1",
43+
"form-data": "^0.2.0",
4344
"har-validator": "^1.1.2",
4445
"require-directory": "^2.1.0"
4546
}

0 commit comments

Comments
 (0)