1- import * as _ from 'lodash' ;
21import React from "react" ;
32import { action , computed } from "mobx" ;
43import { inject , observer } from "mobx-react" ;
5- import * as HarFormat from 'har-format' ;
6- import * as HTTPSnippet from "@httptoolkit/httpsnippet" ;
74import dedent from 'dedent' ;
85
9- import { Omit , HttpExchange } from '../../../types' ;
6+ import { HttpExchange } from '../../../types' ;
107import { styled } from '../../../styles' ;
118import { Icon } from '../../../icons' ;
12- import { saveFile } from '../../../util/ui' ;
139import { logError } from '../../../errors' ;
1410
1511import { 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' ;
1823
1924import { ProHeaderPill , CardSalesPitch } from '../../account/pro-placeholders' ;
2025import {
@@ -27,39 +32,6 @@ import { CopyButtonPill } from '../../common/copy-button';
2732import { DocsLink } from '../../common/docs-link' ;
2833import { ThemedSelfSizedEditor } from '../../editor/base-editor' ;
2934
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-
6335interface ExportCardProps extends CollapsibleCardProps {
6436 exchange : HttpExchange ;
6537 accountStore ?: AccountStore ;
@@ -84,62 +56,15 @@ const snippetEditorOptions = {
8456 hover : { enabled : false }
8557} ;
8658
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-
13059const ExportSnippetEditor = observer ( ( p : {
13160 exchange : HttpExchange
13261 exportOption : SnippetOption
13362} ) => {
13463 const { target, client, link, description } = p . exportOption ;
135- const harRequest = generateHarRequest ( p . exchange . request , false , {
136- bodySizeLimit : Infinity
137- } ) ;
138- const harSnippetBase = simplifyHarForSnippetExport ( harRequest ) ;
13964
14065 let snippet : string ;
14166 try {
142- snippet = new HTTPSnippet ( harSnippetBase ) . convert ( target , client ) ;
67+ snippet = generateCodeSnippet ( p . exchange , p . exportOption ) ;
14368 } catch ( e ) {
14469 console . log ( `Failed to export request for ${ target } --${ client } ` ) ;
14570 logError ( e ) ;
@@ -154,7 +79,7 @@ const ExportSnippetEditor = observer((p: {
15479 < SnippetDescriptionContainer >
15580 < p >
15681 < strong > {
157- getExportOptionName ( p . exportOption )
82+ getCodeSnippetFormatName ( p . exportOption )
15883 } </ strong > : { description }
15984 </ p >
16085 < p >
@@ -174,29 +99,14 @@ const ExportSnippetEditor = observer((p: {
17499 'javascript' : 'javascript' ,
175100 'node' : 'javascript' ,
176101 'shell' : 'shell' ,
177- } as _ . Dictionary < string > ) [ target ] || 'text'
102+ } as Record < string , string > ) [ target ] || 'text'
178103 }
179104 options = { snippetEditorOptions }
180105 />
181106 </ SnippetEditorContainer >
182107 </ > ;
183108} ) ;
184109
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-
200110const ExportHarPill = styled ( observer ( ( p : {
201111 className ?: string ,
202112 exchange : HttpExchange
@@ -232,8 +142,8 @@ export class HttpExportCard extends React.Component<ExportCardProps> {
232142 onChange = { this . setSnippetOption }
233143 value = { this . snippetOption }
234144 optGroups = { snippetExportOptions }
235- keyFormatter = { getExportOptionKey }
236- nameFormatter = { getExportOptionName }
145+ keyFormatter = { getCodeSnippetFormatKey }
146+ nameFormatter = { getCodeSnippetFormatName }
237147 />
238148
239149 < CollapsibleCardHeading onCollapseToggled = { this . props . onCollapseToggled } >
@@ -268,15 +178,8 @@ export class HttpExportCard extends React.Component<ExportCardProps> {
268178 @computed
269179 private get snippetOption ( ) : SnippetOption {
270180 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 ) ;
280183 }
281184
282185 @action . bound
0 commit comments