mirror of https://github.com/linkerd/linkerd2.git
Refactor Tap query form into its own component (#1446)
Tap.jsx is really large and contains a lot of logic that pertains only to the Tap Query Form. This PR tries to separate the concerns of the form and the query display from the main Tap querying and rendering logic. This will also allow us to easily reuse this form/CLI formatting for the Top page. Changes in this PR: * moves all the code for the form into its own component (TapQueryForm) * moves the code that displays the current query into its own component (TapQueryCliCmd) * formats the current tap query as the equivalent command line format that you can paste into a terminal
This commit is contained in:
parent
0750e47203
commit
e5ab124d76
|
@ -5,23 +5,12 @@ import PropTypes from 'prop-types';
|
|||
import { publicAddressToString } from './util/Utils.js';
|
||||
import React from 'react';
|
||||
import TapEventTable from './TapEventTable.jsx';
|
||||
import TapQueryCliCmd from './TapQueryCliCmd.jsx';
|
||||
import TapQueryForm from './TapQueryForm.jsx';
|
||||
import { withContext } from './util/AppContext.jsx';
|
||||
import {
|
||||
AutoComplete,
|
||||
Button,
|
||||
Col,
|
||||
Form,
|
||||
Icon,
|
||||
Input,
|
||||
Row,
|
||||
Select
|
||||
} from 'antd';
|
||||
import { defaultMaxRps, httpMethods } from './util/TapUtils.js';
|
||||
import './../../css/tap.css';
|
||||
|
||||
const colSpan = 5;
|
||||
const rowGutter = 16;
|
||||
const httpMethods = ["GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH"];
|
||||
const defaultMaxRps = 1.0;
|
||||
const maxNumFilterOptions = 12;
|
||||
class Tap extends React.Component {
|
||||
static propTypes = {
|
||||
|
@ -53,17 +42,9 @@ class Tap extends React.Component {
|
|||
authority: "",
|
||||
maxRps: defaultMaxRps
|
||||
},
|
||||
autocomplete: {
|
||||
namespace: [],
|
||||
resource: [],
|
||||
toNamespace: [],
|
||||
toResource: [],
|
||||
authority: []
|
||||
},
|
||||
maxLinesToDisplay: 40,
|
||||
awaitingWebSocketConnection: false,
|
||||
tapRequestInProgress: false,
|
||||
showAdvancedForm: false,
|
||||
pollingInterval: 10000,
|
||||
pendingRequests: false
|
||||
};
|
||||
|
@ -82,8 +63,8 @@ class Tap extends React.Component {
|
|||
}
|
||||
|
||||
onWebsocketOpen = () => {
|
||||
let query = this.state.query;
|
||||
query.maxRps = parseFloat(query.maxRps) || defaultMaxRps;
|
||||
let query = _.cloneDeep(this.state.query);
|
||||
query.maxRps = parseFloat(query.maxRps);
|
||||
|
||||
this.ws.send(JSON.stringify({
|
||||
id: "tap-web",
|
||||
|
@ -160,10 +141,6 @@ class Tap extends React.Component {
|
|||
};
|
||||
}
|
||||
|
||||
getResourceList(resourcesByNs, ns) {
|
||||
return resourcesByNs[ns] || _.uniq(_.flatten(_.values(resourcesByNs)));
|
||||
}
|
||||
|
||||
parseTapResult = data => {
|
||||
let d = JSON.parse(data);
|
||||
let filters = this.state.tapResultFilterOptions;
|
||||
|
@ -311,12 +288,6 @@ class Tap extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
toggleAdvancedForm = show => {
|
||||
this.setState({
|
||||
showAdvancedForm: show
|
||||
});
|
||||
}
|
||||
|
||||
handleTapStart = e => {
|
||||
e.preventDefault();
|
||||
this.startTapSteaming();
|
||||
|
@ -326,44 +297,6 @@ class Tap extends React.Component {
|
|||
this.ws.close(1000);
|
||||
}
|
||||
|
||||
handleFormChange = (name, scopeResource, shouldScopeAuthority) => {
|
||||
let state = {
|
||||
query: this.state.query,
|
||||
autocomplete: this.state.autocomplete
|
||||
};
|
||||
|
||||
return formVal => {
|
||||
state.query[name] = formVal;
|
||||
if (!_.isNil(scopeResource)) {
|
||||
// scope the available typeahead resources to the selected namespace
|
||||
state.autocomplete[scopeResource] = this.state.resourcesByNs[formVal];
|
||||
}
|
||||
if (shouldScopeAuthority) {
|
||||
state.autocomplete.authority = this.state.authoritiesByNs[formVal];
|
||||
}
|
||||
|
||||
this.setState(state);
|
||||
};
|
||||
}
|
||||
|
||||
handleFormEvent = name => {
|
||||
let state = {
|
||||
query: this.state.query
|
||||
};
|
||||
|
||||
return event => {
|
||||
state.query[name] = event.target.value;
|
||||
this.setState(state);
|
||||
};
|
||||
}
|
||||
|
||||
autoCompleteData = name => {
|
||||
return _(this.state.autocomplete[name])
|
||||
.filter(d => d.indexOf(this.state.query[name]) !== -1)
|
||||
.sortBy()
|
||||
.value();
|
||||
}
|
||||
|
||||
loadFromServer() {
|
||||
if (this.state.pendingRequests) {
|
||||
return; // don't make more requests if the ones we sent haven't completed
|
||||
|
@ -377,21 +310,10 @@ class Tap extends React.Component {
|
|||
this.serverPromise = Promise.all(this.api.getCurrentPromises())
|
||||
.then(rsp => {
|
||||
let { resourcesByNs, authoritiesByNs } = this.getResourcesByNs(rsp);
|
||||
let namespaces = _.sortBy(_.keys(resourcesByNs));
|
||||
let resourceNames = this.getResourceList(resourcesByNs, this.state.query.namespace);
|
||||
let toResourceNames = this.getResourceList(resourcesByNs, this.state.query.toNamespace);
|
||||
let authorities = this.getResourceList(authoritiesByNs, this.state.query.namespace);
|
||||
|
||||
this.setState({
|
||||
resourcesByNs,
|
||||
authoritiesByNs,
|
||||
autocomplete: {
|
||||
namespace: namespaces,
|
||||
resource: resourceNames,
|
||||
toNamespace: namespaces,
|
||||
toResource: toResourceNames,
|
||||
authority: authorities
|
||||
},
|
||||
pendingRequests: false
|
||||
});
|
||||
})
|
||||
|
@ -409,178 +331,10 @@ class Tap extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
renderTapForm = () => {
|
||||
return (
|
||||
<Form className="tap-form">
|
||||
<Row gutter={rowGutter}>
|
||||
<Col span={colSpan}>
|
||||
<Form.Item>
|
||||
<Select
|
||||
showSearch
|
||||
allowClear
|
||||
placeholder="Namespace"
|
||||
optionFilterProp="children"
|
||||
onChange={this.handleFormChange("namespace", "resource", true)}>
|
||||
{
|
||||
_.map(this.state.autocomplete.namespace, (n, i) => (
|
||||
<Select.Option key={`ns-dr-${i}`} value={n}>{n}</Select.Option>
|
||||
))
|
||||
}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
<Col span={colSpan}>
|
||||
<Form.Item>
|
||||
<AutoComplete
|
||||
dataSource={this.autoCompleteData("resource")}
|
||||
onSelect={this.handleFormChange("resource")}
|
||||
onSearch={this.handleFormChange("resource")}
|
||||
placeholder="Resource" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
<Col span={colSpan}>
|
||||
<Form.Item>
|
||||
{
|
||||
this.state.tapRequestInProgress ?
|
||||
<Button type="primary" className="tap-stop" onClick={this.handleTapStop}>Stop</Button> :
|
||||
<Button type="primary" className="tap-start" onClick={this.handleTapStart}>Start</Button>
|
||||
}
|
||||
{
|
||||
this.state.awaitingWebSocketConnection ?
|
||||
<Icon type="loading" style={{ paddingLeft: rowGutter, fontSize: 20, color: '#08c' }} /> : null
|
||||
}
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Button
|
||||
className="tap-form-toggle"
|
||||
onClick={() => this.toggleAdvancedForm(!this.state.showAdvancedForm)}>
|
||||
{ this.state.showAdvancedForm ?
|
||||
"Hide filters" : "Show more request filters" } <Icon type={this.state.showAdvancedForm ? 'up' : 'down'} />
|
||||
</Button>
|
||||
|
||||
{ !this.state.showAdvancedForm ? null : this.renderAdvancedTapForm() }
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
renderAdvancedTapForm = () => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Row gutter={rowGutter}>
|
||||
<Col span={colSpan}>
|
||||
<Form.Item>
|
||||
<Select
|
||||
showSearch
|
||||
allowClear
|
||||
placeholder="To Namespace"
|
||||
optionFilterProp="children"
|
||||
onChange={this.handleFormChange("toNamespace", "toResource")}>
|
||||
{
|
||||
_.map(this.state.autocomplete.toNamespace, (n, i) => (
|
||||
<Select.Option key={`ns-dr-${i}`} value={n}>{n}</Select.Option>
|
||||
))
|
||||
}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
<Col span={colSpan}>
|
||||
<Form.Item>
|
||||
<AutoComplete
|
||||
dataSource={this.autoCompleteData("toResource")}
|
||||
onSelect={this.handleFormChange("toResource")}
|
||||
onSearch={this.handleFormChange("toResource")}
|
||||
placeholder="To Resource" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row gutter={rowGutter}>
|
||||
<Col span={2 * colSpan}>
|
||||
<Form.Item
|
||||
extra="Display requests with this :authority">
|
||||
|
||||
<AutoComplete
|
||||
dataSource={this.autoCompleteData("authority")}
|
||||
onSelect={this.handleFormChange("authority")}
|
||||
onSearch={this.handleFormChange("authority")}
|
||||
placeholder="Authority" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
<Col span={2 * colSpan}>
|
||||
<Form.Item
|
||||
extra="Display requests with paths that start with this prefix">
|
||||
<Input placeholder="Path" onChange={this.handleFormEvent("path")} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
</Row>
|
||||
|
||||
<Row gutter={rowGutter}>
|
||||
<Col span={colSpan}>
|
||||
<Form.Item
|
||||
extra="Display requests with this scheme">
|
||||
<Input placeholder="Scheme" onChange={this.handleFormEvent("scheme")} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
<Col span={colSpan}>
|
||||
<Form.Item
|
||||
extra="Maximum requests per second to tap">
|
||||
<Input
|
||||
defaultValue={defaultMaxRps}
|
||||
placeholder="Max RPS"
|
||||
onChange={this.handleFormEvent("maxRps")} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
<Col span={colSpan}>
|
||||
<Form.Item
|
||||
extra="Display requests with this HTTP method">
|
||||
<Select
|
||||
allowClear
|
||||
placeholder="HTTP method"
|
||||
onChange={this.handleFormChange("method")}>
|
||||
{
|
||||
_.map(httpMethods, m =>
|
||||
<Select.Option key={`method-select-${m}`} value={m}>{m}</Select.Option>
|
||||
)
|
||||
}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
renderCurrentQuery = () => {
|
||||
let emptyVals = _.countBy(_.values(this.state.query), v => _.isEmpty(v));
|
||||
|
||||
return (
|
||||
<div className="tap-query">
|
||||
<code>
|
||||
{ !emptyVals.false || emptyVals.false === 1 ? null :
|
||||
<div>
|
||||
Current query:
|
||||
{
|
||||
_.map(this.state.query, (query, queryName) => {
|
||||
if (query === "") {
|
||||
return null;
|
||||
}
|
||||
return <div key={queryName}>{queryName}: {query}</div>;
|
||||
})
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</code>
|
||||
</div>
|
||||
);
|
||||
updateQuery = query => {
|
||||
this.setState({
|
||||
query
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -593,8 +347,17 @@ class Tap extends React.Component {
|
|||
<ErrorBanner message={this.state.error} onHideMessage={() => this.setState({ error: null })} />}
|
||||
|
||||
<PageHeader header="Tap" />
|
||||
{this.renderTapForm()}
|
||||
{this.renderCurrentQuery()}
|
||||
<TapQueryForm
|
||||
tapRequestInProgress={this.state.tapRequestInProgress}
|
||||
awaitingWebSocketConnection={this.state.awaitingWebSocketConnection}
|
||||
handleTapStart={this.handleTapStart}
|
||||
handleTapStop={this.handleTapStop}
|
||||
resourcesByNs={this.state.resourcesByNs}
|
||||
authoritiesByNs={this.state.authoritiesByNs}
|
||||
updateQuery={this.updateQuery}
|
||||
query={this.state.query} />
|
||||
|
||||
<TapQueryCliCmd query={this.state.query} />
|
||||
|
||||
<TapEventTable
|
||||
tableRows={tableRows}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
import _ from 'lodash';
|
||||
import React from 'react';
|
||||
import { tapQueryPropType } from './util/TapUtils.js';
|
||||
|
||||
/*
|
||||
prints a given tap query in an equivalent CLI format, such that it
|
||||
could be pasted into a terminal
|
||||
*/
|
||||
export default class TapQueryCliCmd extends React.Component {
|
||||
static propTypes = {
|
||||
query: tapQueryPropType
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
query: {
|
||||
resource: "",
|
||||
namespace: "",
|
||||
toResource: "",
|
||||
toNamespace: "",
|
||||
method: "",
|
||||
path: "",
|
||||
scheme: "",
|
||||
authority: "",
|
||||
maxRps: ""
|
||||
}
|
||||
}
|
||||
|
||||
renderCliItem = (queryLabel, queryVal) => {
|
||||
return _.isEmpty(queryVal) ? null : ` ${queryLabel} ${queryVal}`;
|
||||
}
|
||||
|
||||
render = () => {
|
||||
let {
|
||||
resource,
|
||||
namespace,
|
||||
toResource,
|
||||
toNamespace,
|
||||
method,
|
||||
path,
|
||||
scheme,
|
||||
authority,
|
||||
maxRps
|
||||
} = this.props.query;
|
||||
|
||||
return (
|
||||
<div className="tap-query">
|
||||
{
|
||||
_.isEmpty(resource) ? null :
|
||||
<React.Fragment>
|
||||
<div>Current Tap query:</div>
|
||||
<code>
|
||||
linkerd tap {resource}
|
||||
{ this.renderCliItem("--namespace", namespace) }
|
||||
{ this.renderCliItem("--to", toResource) }
|
||||
{ this.renderCliItem("--to-namespace", toNamespace) }
|
||||
{ this.renderCliItem("--method", method) }
|
||||
{ this.renderCliItem("--scheme", scheme) }
|
||||
{ this.renderCliItem("--authority", authority) }
|
||||
{ this.renderCliItem("--path", path) }
|
||||
{ this.renderCliItem("--max-rps", maxRps) }
|
||||
</code>
|
||||
</React.Fragment>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,270 @@
|
|||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import {
|
||||
AutoComplete,
|
||||
Button,
|
||||
Col,
|
||||
Form,
|
||||
Icon,
|
||||
Input,
|
||||
Row,
|
||||
Select
|
||||
} from 'antd';
|
||||
import { defaultMaxRps, httpMethods, tapQueryPropType } from './util/TapUtils.js';
|
||||
|
||||
const colSpan = 5;
|
||||
const rowGutter = 16;
|
||||
|
||||
const getResourceList = (resourcesByNs, ns) => {
|
||||
return resourcesByNs[ns] || _.uniq(_.flatten(_.values(resourcesByNs)));
|
||||
};
|
||||
export default class TapQueryForm extends React.Component {
|
||||
static propTypes = {
|
||||
awaitingWebSocketConnection: PropTypes.bool.isRequired,
|
||||
handleTapStart: PropTypes.func.isRequired,
|
||||
handleTapStop: PropTypes.func.isRequired,
|
||||
query: tapQueryPropType.isRequired,
|
||||
tapRequestInProgress: PropTypes.bool.isRequired,
|
||||
updateQuery: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
authoritiesByNs: {},
|
||||
resourcesByNs: {},
|
||||
showAdvancedForm: false,
|
||||
query: props.query,
|
||||
autocomplete: {
|
||||
namespace: [],
|
||||
resource: [],
|
||||
toNamespace: [],
|
||||
toResource: [],
|
||||
authority: []
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props, state) {
|
||||
if (!_.isEqual(props.resourcesByNs, state.resourcesByNs)) {
|
||||
let resourcesByNs = props.resourcesByNs;
|
||||
let authoritiesByNs = props.authoritiesByNs;
|
||||
let namespaces = _.sortBy(_.keys(resourcesByNs));
|
||||
let resourceNames = getResourceList(resourcesByNs, state.query.namespace);
|
||||
let toResourceNames = getResourceList(resourcesByNs, state.query.toNamespace);
|
||||
let authorities = getResourceList(authoritiesByNs, state.query.namespace);
|
||||
|
||||
return _.merge(state, {
|
||||
resourcesByNs,
|
||||
authoritiesByNs,
|
||||
autocomplete: {
|
||||
namespace: namespaces,
|
||||
resource: resourceNames,
|
||||
toNamespace: namespaces,
|
||||
toResource: toResourceNames,
|
||||
authority: authorities
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
toggleAdvancedForm = show => {
|
||||
this.setState({
|
||||
showAdvancedForm: show
|
||||
});
|
||||
}
|
||||
|
||||
handleFormChange = (name, scopeResource, shouldScopeAuthority) => {
|
||||
let state = {
|
||||
query: this.state.query,
|
||||
autocomplete: this.state.autocomplete
|
||||
};
|
||||
|
||||
return formVal => {
|
||||
state.query[name] = formVal;
|
||||
if (!_.isNil(scopeResource)) {
|
||||
// scope the available typeahead resources to the selected namespace
|
||||
state.autocomplete[scopeResource] = this.state.resourcesByNs[formVal];
|
||||
}
|
||||
if (shouldScopeAuthority) {
|
||||
state.autocomplete.authority = this.state.authoritiesByNs[formVal];
|
||||
}
|
||||
|
||||
this.setState(state);
|
||||
this.props.updateQuery(state.query);
|
||||
};
|
||||
}
|
||||
|
||||
handleFormEvent = name => {
|
||||
let state = {
|
||||
query: this.state.query
|
||||
};
|
||||
|
||||
return event => {
|
||||
state.query[name] = event.target.value;
|
||||
this.setState(state);
|
||||
this.props.updateQuery(state.query);
|
||||
};
|
||||
}
|
||||
|
||||
autoCompleteData = name => {
|
||||
return _(this.state.autocomplete[name])
|
||||
.filter(d => d.indexOf(this.state.query[name]) !== -1)
|
||||
.sortBy()
|
||||
.value();
|
||||
}
|
||||
|
||||
renderAdvancedTapForm = () => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Row gutter={rowGutter}>
|
||||
<Col span={colSpan}>
|
||||
<Form.Item>
|
||||
<Select
|
||||
showSearch
|
||||
allowClear
|
||||
placeholder="To Namespace"
|
||||
optionFilterProp="children"
|
||||
onChange={this.handleFormChange("toNamespace", "toResource")}>
|
||||
{
|
||||
_.map(this.state.autocomplete.toNamespace, (n, i) => (
|
||||
<Select.Option key={`ns-dr-${i}`} value={n}>{n}</Select.Option>
|
||||
))
|
||||
}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
<Col span={colSpan}>
|
||||
<Form.Item>
|
||||
<AutoComplete
|
||||
dataSource={this.autoCompleteData("toResource")}
|
||||
onSelect={this.handleFormChange("toResource")}
|
||||
onSearch={this.handleFormChange("toResource")}
|
||||
placeholder="To Resource" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row gutter={rowGutter}>
|
||||
<Col span={2 * colSpan}>
|
||||
<Form.Item
|
||||
extra="Display requests with this :authority">
|
||||
|
||||
<AutoComplete
|
||||
dataSource={this.autoCompleteData("authority")}
|
||||
onSelect={this.handleFormChange("authority")}
|
||||
onSearch={this.handleFormChange("authority")}
|
||||
placeholder="Authority" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
<Col span={2 * colSpan}>
|
||||
<Form.Item
|
||||
extra="Display requests with paths that start with this prefix">
|
||||
<Input placeholder="Path" onChange={this.handleFormEvent("path")} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
</Row>
|
||||
|
||||
<Row gutter={rowGutter}>
|
||||
<Col span={colSpan}>
|
||||
<Form.Item
|
||||
extra="Display requests with this scheme">
|
||||
<Input placeholder="Scheme" onChange={this.handleFormEvent("scheme")} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
<Col span={colSpan}>
|
||||
<Form.Item
|
||||
extra="Maximum requests per second to tap">
|
||||
<Input
|
||||
defaultValue={defaultMaxRps}
|
||||
placeholder="Max RPS"
|
||||
onChange={this.handleFormEvent("maxRps")} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
<Col span={colSpan}>
|
||||
<Form.Item
|
||||
extra="Display requests with this HTTP method">
|
||||
<Select
|
||||
allowClear
|
||||
placeholder="HTTP method"
|
||||
onChange={this.handleFormChange("method")}>
|
||||
{
|
||||
_.map(httpMethods, m =>
|
||||
<Select.Option key={`method-select-${m}`} value={m}>{m}</Select.Option>
|
||||
)
|
||||
}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Form className="tap-form">
|
||||
<Row gutter={rowGutter}>
|
||||
<Col span={colSpan}>
|
||||
<Form.Item>
|
||||
<Select
|
||||
showSearch
|
||||
allowClear
|
||||
placeholder="Namespace"
|
||||
optionFilterProp="children"
|
||||
onChange={this.handleFormChange("namespace", "resource", true)}>
|
||||
{
|
||||
_.map(this.state.autocomplete.namespace, (n, i) => (
|
||||
<Select.Option key={`ns-dr-${i}`} value={n}>{n}</Select.Option>
|
||||
))
|
||||
}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
<Col span={colSpan}>
|
||||
<Form.Item>
|
||||
<AutoComplete
|
||||
dataSource={this.autoCompleteData("resource")}
|
||||
onSelect={this.handleFormChange("resource")}
|
||||
onSearch={this.handleFormChange("resource")}
|
||||
placeholder="Resource" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
<Col span={colSpan}>
|
||||
<Form.Item>
|
||||
{
|
||||
this.props.tapRequestInProgress ?
|
||||
<Button type="primary" className="tap-stop" onClick={this.props.handleTapStop}>Stop</Button> :
|
||||
<Button type="primary" className="tap-start" onClick={this.props.handleTapStart}>Start</Button>
|
||||
}
|
||||
{
|
||||
this.props.awaitingWebSocketConnection ?
|
||||
<Icon type="loading" style={{ paddingLeft: rowGutter, fontSize: 20, color: '#08c' }} /> : null
|
||||
}
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Button
|
||||
className="tap-form-toggle"
|
||||
onClick={() => this.toggleAdvancedForm(!this.state.showAdvancedForm)}>
|
||||
{ this.state.showAdvancedForm ?
|
||||
"Hide filters" : "Show more request filters" } <Icon type={this.state.showAdvancedForm ? 'up' : 'down'} />
|
||||
</Button>
|
||||
|
||||
{ !this.state.showAdvancedForm ? null : this.renderAdvancedTapForm() }
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
export const httpMethods = ["GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH"];
|
||||
|
||||
export const defaultMaxRps = "1.0";
|
||||
|
||||
export const tapQueryPropType = PropTypes.shape({
|
||||
resource: PropTypes.string,
|
||||
namespace: PropTypes.string,
|
||||
toResource: PropTypes.string,
|
||||
toNamespace: PropTypes.string,
|
||||
method: PropTypes.string,
|
||||
path: PropTypes.string,
|
||||
scheme: PropTypes.string,
|
||||
authority: PropTypes.string,
|
||||
maxRps: PropTypes.string
|
||||
});
|
Loading…
Reference in New Issue