monitorForm.tsx 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. import {Component, Fragment} from 'react';
  2. import {Observer} from 'mobx-react';
  3. import Access from 'sentry/components/acl/access';
  4. import Field from 'sentry/components/forms/field';
  5. import Form from 'sentry/components/forms/form';
  6. import NumberField from 'sentry/components/forms/numberField';
  7. import SelectField from 'sentry/components/forms/selectField';
  8. import TextCopyInput from 'sentry/components/forms/textCopyInput';
  9. import TextField from 'sentry/components/forms/textField';
  10. import {Panel, PanelBody, PanelHeader} from 'sentry/components/panels';
  11. import {t, tct} from 'sentry/locale';
  12. import {PageFilters, Project, SelectValue} from 'sentry/types';
  13. import withPageFilters from 'sentry/utils/withPageFilters';
  14. import withProjects from 'sentry/utils/withProjects';
  15. import MonitorModel from './monitorModel';
  16. import {Monitor, MonitorConfig, MonitorTypes, ScheduleType} from './types';
  17. const SCHEDULE_TYPES: SelectValue<ScheduleType>[] = [
  18. {value: 'crontab', label: 'Crontab'},
  19. {value: 'interval', label: 'Interval'},
  20. ];
  21. const MONITOR_TYPES: SelectValue<MonitorTypes>[] = [
  22. {value: 'cron_job', label: 'Cron Job'},
  23. ];
  24. const INTERVALS: SelectValue<string>[] = [
  25. {value: 'minute', label: 'minute(s)'},
  26. {value: 'hour', label: 'hour(s)'},
  27. {value: 'day', label: 'day(s)'},
  28. {value: 'week', label: 'week(s)'},
  29. {value: 'month', label: 'month(s)'},
  30. {value: 'year', label: 'year(s)'},
  31. ];
  32. type Props = {
  33. apiEndpoint: string;
  34. apiMethod: Form['props']['apiMethod'];
  35. onSubmitSuccess: Form['props']['onSubmitSuccess'];
  36. projects: Project[];
  37. selection: PageFilters;
  38. monitor?: Monitor;
  39. };
  40. class MonitorForm extends Component<Props> {
  41. form = new MonitorModel();
  42. formDataFromConfig(type: MonitorTypes, config: MonitorConfig) {
  43. const rv = {};
  44. switch (type) {
  45. case 'cron_job':
  46. rv['config.schedule_type'] = config.schedule_type;
  47. rv['config.checkin_margin'] = config.checkin_margin;
  48. rv['config.max_runtime'] = config.max_runtime;
  49. switch (config.schedule_type) {
  50. case 'interval':
  51. rv['config.schedule.frequency'] = config.schedule[0];
  52. rv['config.schedule.interval'] = config.schedule[1];
  53. break;
  54. case 'crontab':
  55. default:
  56. rv['config.schedule'] = config.schedule;
  57. }
  58. break;
  59. default:
  60. }
  61. return rv;
  62. }
  63. render() {
  64. const {monitor} = this.props;
  65. const selectedProjectId = this.props.selection.projects[0];
  66. const selectedProject = selectedProjectId
  67. ? this.props.projects.find(p => p.id === selectedProjectId + '')
  68. : null;
  69. return (
  70. <Access access={['project:write']}>
  71. {({hasAccess}) => (
  72. <Form
  73. allowUndo
  74. requireChanges
  75. apiEndpoint={this.props.apiEndpoint}
  76. apiMethod={this.props.apiMethod}
  77. model={this.form}
  78. initialData={
  79. monitor
  80. ? {
  81. name: monitor.name,
  82. type: monitor.type,
  83. project: monitor.project.slug,
  84. ...this.formDataFromConfig(monitor.type, monitor.config),
  85. }
  86. : {
  87. project: selectedProject ? selectedProject.slug : null,
  88. }
  89. }
  90. onSubmitSuccess={this.props.onSubmitSuccess}
  91. >
  92. <Panel>
  93. <PanelHeader>{t('Details')}</PanelHeader>
  94. <PanelBody>
  95. {monitor && (
  96. <Field label={t('ID')}>
  97. <div className="controls">
  98. <TextCopyInput>{monitor.id}</TextCopyInput>
  99. </div>
  100. </Field>
  101. )}
  102. <SelectField
  103. name="project"
  104. label={t('Project')}
  105. disabled={!hasAccess}
  106. options={this.props.projects
  107. .filter(p => p.isMember)
  108. .map(p => ({value: p.slug, label: p.slug}))}
  109. required
  110. />
  111. <TextField
  112. name="name"
  113. placeholder={t('My Cron Job')}
  114. label={t('Name')}
  115. disabled={!hasAccess}
  116. required
  117. />
  118. </PanelBody>
  119. </Panel>
  120. <Panel>
  121. <PanelHeader>{t('Config')}</PanelHeader>
  122. <PanelBody>
  123. <SelectField
  124. name="type"
  125. label={t('Type')}
  126. disabled={!hasAccess}
  127. options={MONITOR_TYPES}
  128. required
  129. />
  130. <Observer>
  131. {() => {
  132. switch (this.form.getValue('type')) {
  133. case 'cron_job':
  134. return (
  135. <Fragment>
  136. <NumberField
  137. name="config.max_runtime"
  138. label={t('Max Runtime')}
  139. disabled={!hasAccess}
  140. help={t(
  141. "The maximum runtime (in minutes) a check-in is allowed before it's marked as a failure."
  142. )}
  143. placeholder="e.g. 30"
  144. />
  145. <SelectField
  146. name="config.schedule_type"
  147. label={t('Schedule Type')}
  148. disabled={!hasAccess}
  149. options={SCHEDULE_TYPES}
  150. required
  151. />
  152. </Fragment>
  153. );
  154. default:
  155. return null;
  156. }
  157. }}
  158. </Observer>
  159. <Observer>
  160. {() => {
  161. switch (this.form.getValue('config.schedule_type')) {
  162. case 'crontab':
  163. return (
  164. <Fragment>
  165. <TextField
  166. name="config.schedule"
  167. label={t('Schedule')}
  168. disabled={!hasAccess}
  169. placeholder="*/5 * * * *"
  170. required
  171. help={tct(
  172. 'Changes to the schedule will apply on the next check-in. See [link:Wikipedia] for crontab syntax.',
  173. {
  174. link: <a href="https://en.wikipedia.org/wiki/Cron" />,
  175. }
  176. )}
  177. />
  178. <NumberField
  179. name="config.checkin_margin"
  180. label={t('Check-in Margin')}
  181. disabled={!hasAccess}
  182. help={t(
  183. "The margin (in minutes) a check-in is allowed to exceed it's scheduled window before being treated as missed."
  184. )}
  185. placeholder="e.g. 30"
  186. />
  187. </Fragment>
  188. );
  189. case 'interval':
  190. return (
  191. <Fragment>
  192. <NumberField
  193. name="config.schedule.frequency"
  194. label={t('Frequency')}
  195. disabled={!hasAccess}
  196. placeholder="e.g. 1"
  197. required
  198. />
  199. <SelectField
  200. name="config.schedule.interval"
  201. label={t('Interval')}
  202. disabled={!hasAccess}
  203. options={INTERVALS}
  204. required
  205. />
  206. <NumberField
  207. name="config.checkin_margin"
  208. label={t('Check-in Margin')}
  209. disabled={!hasAccess}
  210. help={t(
  211. "The margin (in minutes) a check-in is allowed to exceed it's scheduled window before being treated as missed."
  212. )}
  213. placeholder="e.g. 30"
  214. />
  215. </Fragment>
  216. );
  217. default:
  218. return null;
  219. }
  220. }}
  221. </Observer>
  222. </PanelBody>
  223. </Panel>
  224. </Form>
  225. )}
  226. </Access>
  227. );
  228. }
  229. }
  230. export default withPageFilters(withProjects(MonitorForm));