Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/fixtures/runCustomFixtures.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { writeFileSync } from 'fs';
import { readFile, writeFile } from 'fs/promises';
import { readFile } from 'fs/promises';
import path from 'path';

import { HTTPSnippet, Request } from '../httpsnippet';
Expand Down
18 changes: 18 additions & 0 deletions src/helpers/headers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ValueOf } from 'type-fest';

type Headers<T> = Record<string, T>;

/**
Expand All @@ -22,3 +24,19 @@ export const getHeader = <T>(headers: Headers<T>, name: string) => {
*/
export const hasHeader = <T>(headers: Headers<T>, name: string) =>
Boolean(getHeaderName(headers, name));

const mimeTypeJson = [
'application/json',
'application/x-json',
'text/json',
'text/x-json',
'+json',
] as const;

type MimeTypeJson = `${string}${typeof mimeTypeJson[number]}${string}`;

/**
* Determines if a given mimetype is JSON, or a variant of such.
*/
export const isMimeTypeJSON = (mimeType: string): mimeType is MimeTypeJson =>
mimeTypeJson.some(type => mimeType.includes(type));
51 changes: 51 additions & 0 deletions src/targets/shell/curl/client.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import applicationFormEncoded from '../../../fixtures/requests/application-form-encoded.json';
import applicationJson from '../../../fixtures/requests/application-json.json';
import full from '../../../fixtures/requests/full.json';
import https from '../../../fixtures/requests/https.json';
import nested from '../../../fixtures/requests/nested.json';
Expand Down Expand Up @@ -96,5 +97,55 @@ runCustomFixtures({
},
expected: 'insecure-skip-verify.sh',
},
{
it: 'should send JSON-encoded data with single quotes within a HEREDOC',
input: {
method: 'POST',
url: 'http://mockbin.com/har',
headers: [
{
name: 'content-type',
value: 'application/json',
},
],
postData: {
mimeType: 'application/json',
text: '{"number":1,"string":"f\'oo"}',
},
} as Request,
options: {
prettifyJson: true,
},
expected: 'jsonObj-with-singlequotes.sh',
},
{
it: 'should prettify simple/short JSON if prettifyJson is true',
input: {
url: 'http://mockbin.com/har',
method: 'POST',
headers: [
{
name: 'content-type',
value: 'application/json',
},
],
postData: {
text: '{"foo": "bar"}',
mimeType: 'application/json',
},
} as Request,
options: {
prettifyJson: true,
},
expected: 'prettify-short-json.sh',
},
{
it: 'should prettify complex json if prettifyJson is true',
input: applicationJson as Request,
options: {
prettifyJson: true,
},
expected: 'application-json-prettified.sh',
},
],
});
43 changes: 39 additions & 4 deletions src/targets/shell/curl/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
*/

import { CodeBuilder } from '../../../helpers/code-builder';
import { getHeaderName } from '../../../helpers/headers';
import { getHeaderName, isMimeTypeJSON } from '../../../helpers/headers';
import { quote } from '../../../helpers/shell';
import { Client } from '../../targets';

Expand All @@ -19,6 +19,7 @@ export interface CurlOptions {
globOff?: boolean;
indent?: string | false;
insecureSkipVerify?: boolean;
prettifyJson?: boolean;
short?: boolean;
}

Expand Down Expand Up @@ -61,6 +62,7 @@ export const curl: Client<CurlOptions> = {
globOff = false,
indent = ' ',
insecureSkipVerify = false,
prettifyJson = false,
short = false,
} = options;

Expand Down Expand Up @@ -148,11 +150,44 @@ export const curl: Client<CurlOptions> = {
}
break;

default:
default: {
// raw request body
if (postData.text) {
push(`${binary ? '--data-binary' : arg('data')} ${quote(postData.text)}`);
if (!postData.text) {
break;
}

const flag = binary ? '--data-binary' : arg('data');

let builtPayload = false;
// If we're dealing with a JSON variant, and our payload is JSON let's make it look a little nicer.
if (isMimeTypeJSON(postData.mimeType)) {
// If our postData is less than 20 characters, let's keep it all on one line so as to not make the snippet overly lengthy.
const couldBeJSON = postData.text.length > 2;
if (couldBeJSON && prettifyJson) {
try {
const jsonPayload = JSON.parse(postData.text);

// If the JSON object has a single quote we should prepare it inside of a HEREDOC because the single quote in something like `string's` can't be escaped when used with `--data`.
//
// Basically this boils down to `--data @- <<EOF...EOF` vs `--data '...'`.
builtPayload = true;

const payload = JSON.stringify(jsonPayload, undefined, indent as string);
if (postData.text.indexOf("'") > 0) {
push(`${flag} @- <<EOF\n${payload}\nEOF`);
} else {
push(`${flag} '\n${payload}\n'`);
}
} catch (err) {
// no-op
}
}
}

if (!builtPayload) {
push(`${flag} ${quote(postData.text)}`);
}
}
}

return join();
Expand Down
25 changes: 25 additions & 0 deletions src/targets/shell/curl/fixtures/application-json-prettified.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
curl --request POST \
--url http://mockbin.com/har \
--header 'content-type: application/json' \
--data '
{
"number": 1,
"string": "f\"oo",
"arr": [
1,
2,
3
],
"nested": {
"a": "b"
},
"arr_mix": [
1,
"a",
{
"arr_mix_nested": {}
}
],
"boolean": false
}
'
9 changes: 9 additions & 0 deletions src/targets/shell/curl/fixtures/jsonObj-with-singlequotes.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
curl --request POST \
--url http://mockbin.com/har \
--header 'content-type: application/json' \
--data @- <<EOF
{
"number": 1,
"string": "f'oo"
}
EOF
8 changes: 8 additions & 0 deletions src/targets/shell/curl/fixtures/prettify-short-json.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
curl --request POST \
--url http://mockbin.com/har \
--header 'content-type: application/json' \
--data '
{
"foo": "bar"
}
'