-
Notifications
You must be signed in to change notification settings - Fork 108
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add very basic Send UI (feature flagged) for initial testing
Can send requests, accepts all the core inputs we'll need and persists them, has the top-level UI structure & data model sort-of in place. Does not show responses (they're just logged to the console), does not do lots of very necessary UX details, and doesn't have any kind of UI polish at all. But exciting!
- Loading branch information
Showing
13 changed files
with
597 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
import * as _ from 'lodash'; | ||
import * as React from 'react'; | ||
import { action, computed } from 'mobx'; | ||
import { inject, observer } from 'mobx-react'; | ||
import { | ||
MOCKTTP_PARAM_REF, | ||
ProxySetting, | ||
ProxySettingSource, | ||
RuleParameterReference | ||
} from 'mockttp'; | ||
|
||
import { RawHeaders } from '../../types'; | ||
import { styled } from '../../styles'; | ||
import { bufferToString, isProbablyUtf8, stringToBuffer } from '../../util'; | ||
|
||
import { sendRequest } from '../../services/server-api'; | ||
import { RulesStore } from '../../model/rules/rules-store'; | ||
import { SendStore } from '../../model/send/send-store'; | ||
import { ClientProxyConfig, RULE_PARAM_REF_KEY } from '../../model/send/send-data-model'; | ||
|
||
import { EditableRawHeaders } from '../common/editable-headers'; | ||
import { ThemedSelfSizedEditor } from '../editor/base-editor'; | ||
import { Button, TextInput } from '../common/inputs'; | ||
|
||
const RequestPaneContainer = styled.section` | ||
display: flex; | ||
flex-direction: column; | ||
`; | ||
|
||
const UrlInput = styled(TextInput)` | ||
`; | ||
|
||
export const getEffectivePort = (url: { protocol: string | null, port: string | null }) => { | ||
if (url.port) { | ||
return parseInt(url.port, 10); | ||
} else if (url.protocol === 'https:' || url.protocol === 'wss:') { | ||
return 443; | ||
} else { | ||
return 80; | ||
} | ||
} | ||
|
||
@inject('rulesStore') | ||
@inject('sendStore') | ||
@observer | ||
export class RequestPane extends React.Component<{ | ||
rulesStore?: RulesStore, | ||
sendStore?: SendStore | ||
}> { | ||
|
||
@computed | ||
get method() { | ||
return this.props.sendStore!.requestInput.method; | ||
} | ||
|
||
@computed | ||
get url() { | ||
return this.props.sendStore!.requestInput.url; | ||
} | ||
|
||
|
||
@computed | ||
get headers() { | ||
return this.props.sendStore!.requestInput.headers; | ||
} | ||
|
||
@computed | ||
get body() { | ||
return this.props.sendStore!.requestInput.rawBody; | ||
} | ||
|
||
@computed | ||
private get bodyTextEncoding() { | ||
// If we're handling text data, we want to show & edit it as UTF8. | ||
// If it's binary, that's a lossy operation, so we use binary (latin1) instead. | ||
return isProbablyUtf8(this.body) | ||
? 'utf8' | ||
: 'binary'; | ||
} | ||
|
||
render() { | ||
const bodyString = bufferToString(this.body, this.bodyTextEncoding); | ||
|
||
return <RequestPaneContainer> | ||
<UrlInput | ||
placeholder='https://example.com/hello?name=world' | ||
value={this.url} | ||
onChange={this.updateUrl} | ||
/> | ||
<EditableRawHeaders | ||
headers={this.headers} | ||
onChange={this.updateHeaders} | ||
/> | ||
<ThemedSelfSizedEditor | ||
contentId='request' | ||
language={'text'} | ||
value={bodyString} | ||
onChange={this.updateBody} | ||
/> | ||
<Button | ||
onClick={this.sendRequest} | ||
/> | ||
</RequestPaneContainer>; | ||
} | ||
|
||
@action.bound | ||
updateUrl(changeEvent: React.ChangeEvent<HTMLInputElement>) { | ||
const { requestInput } = this.props.sendStore!; | ||
requestInput.url = changeEvent.target.value; | ||
} | ||
|
||
@action.bound | ||
updateHeaders(headers: RawHeaders) { | ||
const { requestInput } = this.props.sendStore!; | ||
requestInput.headers = headers; | ||
} | ||
|
||
@action.bound | ||
updateBody(input: string) { | ||
const { requestInput } = this.props.sendStore!; | ||
requestInput.rawBody = stringToBuffer(input, this.bodyTextEncoding); | ||
} | ||
|
||
@action.bound | ||
async sendRequest() { | ||
const rulesStore = this.props.rulesStore!; | ||
const passthroughOptions = rulesStore.activePassthroughOptions; | ||
|
||
const url = new URL(this.url); | ||
const effectivePort = getEffectivePort(url); | ||
const hostWithPort = `${url.hostname}:${effectivePort}`; | ||
const clientCertificate = passthroughOptions.clientCertificateHostMap?.[hostWithPort] || | ||
passthroughOptions.clientCertificateHostMap?.[url.hostname!] || | ||
undefined; | ||
|
||
const responseStream = await sendRequest({ | ||
url: this.url, | ||
method: this.method, | ||
headers: this.headers, | ||
rawBody: this.body | ||
}, { | ||
ignoreHostHttpsErrors: passthroughOptions.ignoreHostHttpsErrors, | ||
trustAdditionalCAs: rulesStore.additionalCaCertificates.map((cert) => ({ cert: cert.rawPEM })), | ||
clientCertificate, | ||
proxyConfig: getProxyConfig(rulesStore.proxyConfig), | ||
lookupOptions: passthroughOptions.lookupOptions | ||
}); | ||
|
||
const reader = responseStream.getReader(); | ||
while(true) { | ||
const { done, value } = await reader.read(); | ||
if (done) return; | ||
else console.log(value); | ||
} | ||
} | ||
|
||
} | ||
|
||
function getProxyConfig(proxyConfig: RulesStore['proxyConfig']): ClientProxyConfig { | ||
if (!proxyConfig) return undefined; | ||
|
||
if (_.isArray(proxyConfig)) { | ||
return proxyConfig.map((config) => getProxyConfig(config)) as ClientProxyConfig; | ||
} | ||
|
||
if (MOCKTTP_PARAM_REF in proxyConfig) { | ||
return { | ||
[RULE_PARAM_REF_KEY]: (proxyConfig as RuleParameterReference<ProxySettingSource>)[MOCKTTP_PARAM_REF] | ||
}; | ||
} | ||
|
||
return proxyConfig as ProxySetting; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import * as React from "react"; | ||
import { observer } from "mobx-react"; | ||
|
||
import { styled } from '../../styles'; | ||
|
||
const ResponsePaneContainer = styled.section` | ||
`; | ||
|
||
@observer | ||
export class ResponsePane extends React.Component<{}> { | ||
|
||
render() { | ||
return <ResponsePaneContainer> | ||
|
||
</ResponsePaneContainer>; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import * as React from 'react'; | ||
import { observer } from 'mobx-react'; | ||
|
||
import { styled } from '../../styles'; | ||
|
||
import { SplitPane } from '../split-pane'; | ||
import { RequestPane } from './request-pane'; | ||
import { ResponsePane } from './response-pane'; | ||
|
||
const SendPageContainer = styled.div` | ||
height: 100vh; | ||
position: relative; | ||
`; | ||
|
||
@observer | ||
export class SendPage extends React.Component<{}> { | ||
|
||
render() { | ||
return <SendPageContainer> | ||
<SplitPane | ||
split='vertical' | ||
primary='second' | ||
defaultSize='50%' | ||
minSize={300} | ||
maxSize={-300} | ||
> | ||
<RequestPane /> | ||
<ResponsePane /> | ||
</SplitPane> | ||
</SendPageContainer>; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.