import _ from 'lodash'; import PropTypes from 'prop-types'; import React from 'react'; import { addUrlProps, UrlQueryParamTypes } from 'react-url-query'; import { AutoComplete, Button, Col, Form, Icon, Input, Row, Select } from 'antd'; import { defaultMaxRps, emptyTapQuery, httpMethods, tapQueryProps, tapQueryPropType } from './util/TapUtils.jsx'; const colSpan = 5; const rowGutter = 16; // you can also tap resources to tap all pods in the resource const resourceTypes = [ "deployment", "daemonset", "pod", "replicationcontroller", "statefulset" ]; const getResourceList = (resourcesByNs, ns) => { return resourcesByNs[ns] || _.uniq(_.flatten(_.values(resourcesByNs))); }; const urlPropsQueryConfig = _.mapValues(tapQueryProps, () => { return { type: UrlQueryParamTypes.string }; }); class TapQueryForm extends React.Component { static propTypes = { enableAdvancedForm: PropTypes.bool, handleTapClear: PropTypes.func, handleTapStart: PropTypes.func.isRequired, handleTapStop: PropTypes.func.isRequired, query: tapQueryPropType.isRequired, tapIsClosing: PropTypes.bool, tapRequestInProgress: PropTypes.bool.isRequired, updateQuery: PropTypes.func.isRequired } static defaultProps = { enableAdvancedForm: true, handleTapClear: _.noop, tapIsClosing: false } constructor(props) { super(props); let query = _.merge({}, props.query, _.pick(this.props, _.keys(tapQueryProps))); props.updateQuery(query); let showAdvancedForm = _.some( _.omit(query, ['namespace', 'resource']), v => !_.isEmpty(v)); this.state = { query, showAdvancedForm, authoritiesByNs: {}, resourcesByNs: {}, 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; this.handleUrlUpdate(name, formVal); if (!_.isNil(scopeResource)) { // scope the available typeahead resources to the selected namespace state.autocomplete[scopeResource] = this.state.resourcesByNs[formVal]; if (_.isEmpty(state.query[scopeResource]) || state.query[scopeResource].indexOf("namespace") !== -1) { state.query[scopeResource] = `namespace/${formVal}`; this.handleUrlUpdate(scopeResource, `namespace/${formVal}`); } } if (shouldScopeAuthority) { state.autocomplete.authority = this.state.authoritiesByNs[formVal]; } this.setState(state); this.props.updateQuery(state.query); }; } // Each time state.query is updated, this method calls the equivalent // onChange method to reflect the update in url query params. These onChange // methods are automatically added to props by react-url-query. handleUrlUpdate = (name, formVal) => { this.props[`onChange${_.upperFirst(name)}`](formVal); } handleFormEvent = name => { let state = { query: this.state.query }; return event => { state.query[name] = event.target.value; this.handleUrlUpdate(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) .uniq() .sortBy() .value(); } resetTapForm = () => { this.setState({ query: emptyTapQuery() }); _.each(this.state.query, (_val, name) => { this.handleUrlUpdate(name, null); }); this.props.updateQuery(emptyTapQuery(), true); this.props.handleTapClear(); } renderAdvancedTapForm = () => { return ( {this.renderResourceSelect("toResource", "toNamespace")} ); } renderResourceSelect = (resourceKey, namespaceKey) => { let selectedNs = this.state.query[namespaceKey]; let nsEmpty = _.isNil(selectedNs) || _.isEmpty(selectedNs); let resourceOptions = _.concat( resourceTypes, this.state.autocomplete[resourceKey] || [], nsEmpty ? [] : [`namespace/${selectedNs}`] ); return ( ); } renderTapButton = (tapInProgress, tapIsClosing) => { if (tapIsClosing) { return (); } else if (tapInProgress) { return (); } else { return ( ); } } render() { return (
{this.renderResourceSelect("resource", "namespace")} { this.renderTapButton(this.props.tapRequestInProgress, this.props.tapIsClosing) } { !this.props.enableAdvancedForm ? null : { !this.state.showAdvancedForm ? null : this.renderAdvancedTapForm() } }
); } } export default addUrlProps({ urlPropsQueryConfig })(TapQueryForm);