1
- import * as _ from 'lodash' ;
2
1
import React from "react" ;
3
2
import { action , computed } from "mobx" ;
4
3
import { inject , observer } from "mobx-react" ;
5
- import * as HarFormat from 'har-format' ;
6
- import * as HTTPSnippet from "@httptoolkit/httpsnippet" ;
7
4
import dedent from 'dedent' ;
8
5
9
- import { Omit , HttpExchange } from '../../../types' ;
6
+ import { HttpExchange } from '../../../types' ;
10
7
import { styled } from '../../../styles' ;
11
8
import { Icon } from '../../../icons' ;
12
- import { saveFile } from '../../../util/ui' ;
13
9
import { logError } from '../../../errors' ;
14
10
15
11
import { AccountStore } from '../../../model/account/account-store' ;
16
- import { UiStore } from '../../../model/ui-store' ;
17
- import { generateHarRequest , generateHar , ExtendedHarRequest } from '../../../model/http/har' ;
12
+ import { UiStore } from '../../../model/ui/ui-store' ;
13
+ import {
14
+ exportHar ,
15
+ generateCodeSnippet ,
16
+ getCodeSnippetFormatKey ,
17
+ getCodeSnippetFormatName ,
18
+ getCodeSnippetOptionFromKey ,
19
+ DEFAULT_SNIPPET_FORMAT_KEY ,
20
+ snippetExportOptions ,
21
+ SnippetOption
22
+ } from '../../../model/ui/export' ;
18
23
19
24
import { ProHeaderPill , CardSalesPitch } from '../../account/pro-placeholders' ;
20
25
import {
@@ -27,39 +32,6 @@ import { CopyButtonPill } from '../../common/copy-button';
27
32
import { DocsLink } from '../../common/docs-link' ;
28
33
import { ThemedSelfSizedEditor } from '../../editor/base-editor' ;
29
34
30
- interface SnippetOption {
31
- target : HTTPSnippet . Target ,
32
- client : HTTPSnippet . Client ,
33
- name : string ,
34
- description : string ,
35
- link : string
36
- }
37
-
38
- const snippetExportOptions : _ . Dictionary < SnippetOption [ ] > = _ ( HTTPSnippet . availableTargets ( ) )
39
- . keyBy ( target => target . title )
40
- . mapValues ( target =>
41
- target . clients . map ( ( client ) => ( {
42
- target : target . key ,
43
- client : client . key ,
44
- name : client . title ,
45
- description : client . description ,
46
- link : client . link
47
- } ) )
48
- ) . value ( ) ;
49
-
50
- const KEY_SEPARATOR = '~~' ;
51
-
52
- const getExportOptionKey = ( option : SnippetOption ) =>
53
- option . target + KEY_SEPARATOR + option . client ;
54
-
55
- // Show the client name, or an overridden name in some ambiguous cases
56
- const getExportOptionName = ( option : SnippetOption ) => ( {
57
- 'php~~curl' : 'PHP ext-cURL' ,
58
- 'php~~http1' : 'PHP HTTP v1' ,
59
- 'php~~http2' : 'PHP HTTP v2' ,
60
- 'node~~native' : 'Node.js HTTP'
61
- } as _ . Dictionary < string > ) [ getExportOptionKey ( option ) ] || option . name ;
62
-
63
35
interface ExportCardProps extends CollapsibleCardProps {
64
36
exchange : HttpExchange ;
65
37
accountStore ?: AccountStore ;
@@ -84,62 +56,15 @@ const snippetEditorOptions = {
84
56
hover : { enabled : false }
85
57
} ;
86
58
87
- const simplifyHarForSnippetExport = ( harRequest : ExtendedHarRequest ) => {
88
- const postData = ! ! harRequest . postData
89
- ? harRequest . postData
90
- : harRequest . _requestBodyStatus === 'discarded:not-representable'
91
- ? {
92
- mimeType : 'text/plain' ,
93
- text : "!!! UNREPRESENTABLE BINARY REQUEST BODY - BODY MUST BE EXPORTED SEPARATELY !!!"
94
- }
95
- : harRequest . _requestBodyStatus === 'discarded:too-large'
96
- ? {
97
- mimeType : 'text/plain' ,
98
- text : "!!! VERY LARGE REQUEST BODY - BODY MUST BE EXPORTED & INCLUDED SEPARATELY !!!"
99
- }
100
- : harRequest . _requestBodyStatus === 'discarded:not-decodable'
101
- ? {
102
- mimeType : 'text/plain' ,
103
- text : "!!! REQUEST BODY COULD NOT BE DECODED !!!"
104
- }
105
- : undefined ;
106
-
107
- // When exporting code snippets the primary goal is to generate convenient code to send the
108
- // request that's *sematantically* equivalent to the original request, not to force every
109
- // tool to produce byte-for-byte identical requests (that's effectively impossible). To do
110
- // this, we drop headers that tools can produce automatically for themselves:
111
- return {
112
- ...harRequest ,
113
- postData,
114
- headers : _ . filter ( harRequest . headers , ( header ) => {
115
- // All clients should be able to automatically generate the correct content-length
116
- // headers as required for a request where it's unspecified. If we override this,
117
- // it can cause problems if tools change the body length (due to encoding/compression).
118
- if ( header . name . toLowerCase ( ) === 'content-length' ) return false ;
119
-
120
- // HTTP/2 headers should never be included in snippets - they're implicitly part of
121
- // the other request data (the method etc).
122
- // We can drop this after fixing https://github.com/Kong/httpsnippet/issues/298
123
- if ( header . name . startsWith ( ':' ) ) return false ;
124
-
125
- return true ;
126
- } )
127
- } ;
128
- } ;
129
-
130
59
const ExportSnippetEditor = observer ( ( p : {
131
60
exchange : HttpExchange
132
61
exportOption : SnippetOption
133
62
} ) => {
134
63
const { target, client, link, description } = p . exportOption ;
135
- const harRequest = generateHarRequest ( p . exchange . request , false , {
136
- bodySizeLimit : Infinity
137
- } ) ;
138
- const harSnippetBase = simplifyHarForSnippetExport ( harRequest ) ;
139
64
140
65
let snippet : string ;
141
66
try {
142
- snippet = new HTTPSnippet ( harSnippetBase ) . convert ( target , client ) ;
67
+ snippet = generateCodeSnippet ( p . exchange , p . exportOption ) ;
143
68
} catch ( e ) {
144
69
console . log ( `Failed to export request for ${ target } --${ client } ` ) ;
145
70
logError ( e ) ;
@@ -154,7 +79,7 @@ const ExportSnippetEditor = observer((p: {
154
79
< SnippetDescriptionContainer >
155
80
< p >
156
81
< strong > {
157
- getExportOptionName ( p . exportOption )
82
+ getCodeSnippetFormatName ( p . exportOption )
158
83
} </ strong > : { description }
159
84
</ p >
160
85
< p >
@@ -174,29 +99,14 @@ const ExportSnippetEditor = observer((p: {
174
99
'javascript' : 'javascript' ,
175
100
'node' : 'javascript' ,
176
101
'shell' : 'shell' ,
177
- } as _ . Dictionary < string > ) [ target ] || 'text'
102
+ } as Record < string , string > ) [ target ] || 'text'
178
103
}
179
104
options = { snippetEditorOptions }
180
105
/>
181
106
</ SnippetEditorContainer >
182
107
</ > ;
183
108
} ) ;
184
109
185
- const exportHar = async ( exchange : HttpExchange ) => {
186
- const harContent = JSON . stringify (
187
- await generateHar ( [ exchange ] , {
188
- bodySizeLimit : Infinity
189
- } )
190
- ) ;
191
- const filename = `${
192
- exchange . request . method
193
- } ${
194
- exchange . request . parsedUrl . hostname
195
- } .har`;
196
-
197
- saveFile ( filename , 'application/har+json;charset=utf-8' , harContent ) ;
198
- } ;
199
-
200
110
const ExportHarPill = styled ( observer ( ( p : {
201
111
className ?: string ,
202
112
exchange : HttpExchange
@@ -232,8 +142,8 @@ export class HttpExportCard extends React.Component<ExportCardProps> {
232
142
onChange = { this . setSnippetOption }
233
143
value = { this . snippetOption }
234
144
optGroups = { snippetExportOptions }
235
- keyFormatter = { getExportOptionKey }
236
- nameFormatter = { getExportOptionName }
145
+ keyFormatter = { getCodeSnippetFormatKey }
146
+ nameFormatter = { getCodeSnippetFormatName }
237
147
/>
238
148
239
149
< CollapsibleCardHeading onCollapseToggled = { this . props . onCollapseToggled } >
@@ -268,15 +178,8 @@ export class HttpExportCard extends React.Component<ExportCardProps> {
268
178
@computed
269
179
private get snippetOption ( ) : SnippetOption {
270
180
let exportSnippetFormat = this . props . uiStore ! . exportSnippetFormat ||
271
- `shell${ KEY_SEPARATOR } curl` ;
272
-
273
- const [ target , client ] = exportSnippetFormat . split ( KEY_SEPARATOR ) as
274
- [ HTTPSnippet . Target , HTTPSnippet . Client ] ;
275
-
276
- return _ ( snippetExportOptions )
277
- . values ( )
278
- . flatten ( )
279
- . find ( { target, client } ) as SnippetOption ;
181
+ DEFAULT_SNIPPET_FORMAT_KEY ;
182
+ return getCodeSnippetOptionFromKey ( exportSnippetFormat ) ;
280
183
}
281
184
282
185
@action . bound
0 commit comments