<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import Relay from 'react-relay/classic';
import DocumentTitle from 'react-document-title';
import { seconds } from 'metrick/duration';

import AgentStateIcon from './state-icon';
import Button from '../shared/Button';
import FlashesStore from '../../stores/FlashesStore';
import FriendlyTime from '../shared/FriendlyTime';
import JobLink from '../shared/JobLink';
import PageWithContainer from '../shared/PageWithContainer';
import Panel from '../shared/Panel';
import permissions from '../../lib/permissions';
import { getLabelForConnectionState } from './shared';

import AgentStopMutation from '../../mutations/AgentStop';

class AgentShow extends React.Component {
  static propTypes = {
    agent: PropTypes.shape({
      id: PropTypes.string,
      name: PropTypes.string,
      connectionState: PropTypes.string,
      job: PropTypes.object,
      permissions: PropTypes.shape({
        agentStop: PropTypes.shape({
          allowed: PropTypes.bool
        })
      }),
      organization: PropTypes.shape({
        name: PropTypes.string,
        slug: PropTypes.string
      })
    }),
    relay: PropTypes.object.isRequired
  };

  state = {
    stopping: false
  };

  componentDidMount() {
    // Only bother setting up the delayed load and refresher if we've got an
    // actual agent to play with.
    if (this.props.agent && this.props.agent.id) {
      // This will cause a full refresh of the data every 3 seconds. This seems
      // very low, but chances are people aren't really looking at this page
      // for long periods of time.
      this.startTimeout();
    }
  }

  componentWillUnmount() {
    clearTimeout(this._agentRefreshTimeout);
  }

  startTimeout = () => {
    this._agentRefreshTimeout = setTimeout(
      this.fetchUpdatedData,
      3::seconds
    );
  };

  fetchUpdatedData = () => {
    this.props.relay.forceFetch(
      true,
      (readyState) => {
        if (readyState.done) {
          this.startTimeout();
        }
      }
    );
  };

  renderExtraItem(title, content) {
    return (
      <tr key={title} style={{ marginTop: 3 }} className="border-gray border-bottom">
        <th className="h4 p2 semi-bold left-align align-top" width={100}>{title}</th>
        <td className="h4 p2">{content}</td>
      </tr>
    );
  }

  renderExtras(agent) {
    const extras = [];

    extras.push(this.renderExtraItem('State', (
      <span>
        <AgentStateIcon agent={agent} className="pr2" />
        {getLabelForConnectionState(agent.connectionState)}
      </span>
    )));

    if (agent.version) {
      extras.push(this.renderExtraItem('Version', agent.version));
    }

    if (agent.hostname) {
      extras.push(this.renderExtraItem('Hostname', agent.hostname));
    }

    if (agent.pid) {
      extras.push(this.renderExtraItem('PID', agent.pid));
    }

    if (agent.ipAddress) {<commit_msg>Added note about how agents handle 'stopped'<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import Relay from 'react-relay/classic';
import DocumentTitle from 'react-document-title';
import { seconds } from 'metrick/duration';

import AgentStateIcon from './state-icon';
import Button from '../shared/Button';
import FlashesStore from '../../stores/FlashesStore';
import FriendlyTime from '../shared/FriendlyTime';
import JobLink from '../shared/JobLink';
import PageWithContainer from '../shared/PageWithContainer';
import Panel from '../shared/Panel';
import permissions from '../../lib/permissions';
import { getLabelForConnectionState } from './shared';

import AgentStopMutation from '../../mutations/AgentStop';

class AgentShow extends React.Component {
  static propTypes = {
    agent: PropTypes.shape({
      id: PropTypes.string,
      name: PropTypes.string,
      connectionState: PropTypes.string,
      job: PropTypes.object,
      permissions: PropTypes.shape({
        agentStop: PropTypes.shape({
          allowed: PropTypes.bool
        })
      }),
      organization: PropTypes.shape({
        name: PropTypes.string,
        slug: PropTypes.string
      })
    }),
    relay: PropTypes.object.isRequired
  };

  state = {
    stopping: false
  };

  componentDidMount() {
    // Only bother setting up the delayed load and refresher if we've got an
    // actual agent to play with.
    if (this.props.agent && this.props.agent.id) {
      // This will cause a full refresh of the data every 3 seconds. This seems
      // very low, but chances are people aren't really looking at this page
      // for long periods of time.
      this.startTimeout();
    }
  }

  componentWillUnmount() {
    clearTimeout(this._agentRefreshTimeout);
  }

  startTimeout = () => {
    this._agentRefreshTimeout = setTimeout(
      this.fetchUpdatedData,
      3::seconds
    );
  };

  fetchUpdatedData = () => {
    this.props.relay.forceFetch(
      true,
      (readyState) => {
        if (readyState.done) {
          this.startTimeout();
        }
      }
    );
  };

  renderExtraItem(title, content) {
    return (
      <tr key={title} style={{ marginTop: 3 }} className="border-gray border-bottom">
        <th className="h4 p2 semi-bold left-align align-top" width={100}>{title}</th>
        <td className="h4 p2">{content}</td>
      </tr>
    );
  }

  renderExtras(agent) {
    const extras = [];

    let extraStoppingInfo;
    if (agent.connectionState == "stopping") {
      extraStoppingInfo = (
        <div className="dark-gray mt1">
          If the agent doesn't respond to the "stop" signal within a few minutes, Buildkite will forcefully disconnect the agent and remove it from your pool of agents.
        </div>
      );
    }

    extras.push(this.renderExtraItem('State', (
      <div>
        <AgentStateIcon agent={agent} className="pr2" />
        {getLabelForConnectionState(agent.connectionState)}
        {extraStoppingInfo}
      </div>
    )));

    if (agent.version) {
      extras.push(this.renderExtraItem('Version', agent.version));
    }

    if (agent.hostname) {
      extras.push(this.renderExtraItem('Hostname', agent.hostname));
    }

    if (agent.pid) {
      extras.push(this.renderExtraItem('PID', agent.pid));
    }

    if (agent.ipAddress) {
{'fraction_non_alphanumeric': 0.13144042111979581, 'fraction_numerical': 0.0036688467060137183, 'mean_word_length': 2.7635054021608645, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '897113', 'n_tokens_mistral': 1807, 'n_tokens_neox': 1809, 'n_words': 456}