projectServiceHooks.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import React from 'react';
  2. import {Link, WithRouterProps} from 'react-router';
  3. import PropTypes from 'prop-types';
  4. import {
  5. addErrorMessage,
  6. addLoadingMessage,
  7. clearIndicators,
  8. } from 'app/actionCreators/indicator';
  9. import Button from 'app/components/button';
  10. import {Panel, PanelAlert, PanelBody, PanelHeader} from 'app/components/panels';
  11. import Switch from 'app/components/switchButton';
  12. import Truncate from 'app/components/truncate';
  13. import {IconAdd, IconFlag} from 'app/icons';
  14. import {t} from 'app/locale';
  15. import {ServiceHook} from 'app/types';
  16. import AsyncView from 'app/views/asyncView';
  17. import EmptyMessage from 'app/views/settings/components/emptyMessage';
  18. import Field from 'app/views/settings/components/forms/field';
  19. import SettingsPageHeader from 'app/views/settings/components/settingsPageHeader';
  20. type RowProps = {
  21. orgId: string;
  22. projectId: string;
  23. hook: ServiceHook;
  24. onToggleActive: () => void;
  25. };
  26. function ServiceHookRow({orgId, projectId, hook, onToggleActive}: RowProps) {
  27. return (
  28. <Field
  29. label={
  30. <Link
  31. data-test-id="project-service-hook"
  32. to={`/settings/${orgId}/projects/${projectId}/hooks/${hook.id}/`}
  33. >
  34. <Truncate value={hook.url} />
  35. </Link>
  36. }
  37. help={
  38. <small>
  39. {hook.events && hook.events.length !== 0 ? (
  40. hook.events.join(', ')
  41. ) : (
  42. <em>{t('no events configured')}</em>
  43. )}
  44. </small>
  45. }
  46. >
  47. <Switch isActive={hook.status === 'active'} size="lg" toggle={onToggleActive} />
  48. </Field>
  49. );
  50. }
  51. type Props = WithRouterProps<{orgId: string; projectId: string}, {}>;
  52. type State = {
  53. hookList: null | ServiceHook[];
  54. } & AsyncView['state'];
  55. export default class ProjectServiceHooks extends AsyncView<Props, State> {
  56. static contextTypes = {
  57. router: PropTypes.object,
  58. organization: PropTypes.object.isRequired,
  59. };
  60. getEndpoints(): ReturnType<AsyncView['getEndpoints']> {
  61. const {orgId, projectId} = this.props.params;
  62. return [['hookList', `/projects/${orgId}/${projectId}/hooks/`]];
  63. }
  64. onToggleActive = (hook: ServiceHook) => {
  65. const {orgId, projectId} = this.props.params;
  66. const {hookList} = this.state;
  67. if (!hookList) {
  68. return;
  69. }
  70. addLoadingMessage(t('Saving changes\u2026'));
  71. this.api.request(`/projects/${orgId}/${projectId}/hooks/${hook.id}/`, {
  72. method: 'PUT',
  73. data: {
  74. isActive: hook.status !== 'active',
  75. },
  76. success: data => {
  77. clearIndicators();
  78. this.setState({
  79. hookList: hookList.map(h => {
  80. if (h.id === data.id) {
  81. return {
  82. ...h,
  83. ...data,
  84. };
  85. }
  86. return h;
  87. }),
  88. });
  89. },
  90. error: () => {
  91. addErrorMessage(t('Unable to remove application. Please try again.'));
  92. },
  93. });
  94. };
  95. renderEmpty() {
  96. return (
  97. <EmptyMessage>
  98. {t('There are no service hooks associated with this project.')}
  99. </EmptyMessage>
  100. );
  101. }
  102. renderResults() {
  103. const {orgId, projectId} = this.props.params;
  104. return (
  105. <React.Fragment>
  106. <PanelHeader key="header">{t('Service Hook')}</PanelHeader>
  107. <PanelBody key="body">
  108. <PanelAlert type="info" icon={<IconFlag size="md" />}>
  109. {t(
  110. 'Service Hooks are an early adopter preview feature and will change in the future.'
  111. )}
  112. </PanelAlert>
  113. {this.state.hookList?.map(hook => (
  114. <ServiceHookRow
  115. key={hook.id}
  116. orgId={orgId}
  117. projectId={projectId}
  118. hook={hook}
  119. onToggleActive={this.onToggleActive.bind(this, hook)}
  120. />
  121. ))}
  122. </PanelBody>
  123. </React.Fragment>
  124. );
  125. }
  126. renderBody() {
  127. const {hookList} = this.state;
  128. const body =
  129. hookList && hookList.length > 0 ? this.renderResults() : this.renderEmpty();
  130. const {orgId, projectId} = this.props.params;
  131. const access = new Set(this.context.organization.access);
  132. return (
  133. <React.Fragment>
  134. <SettingsPageHeader
  135. title={t('Service Hooks')}
  136. action={
  137. access.has('project:write') ? (
  138. <Button
  139. data-test-id="new-service-hook"
  140. to={`/settings/${orgId}/projects/${projectId}/hooks/new/`}
  141. size="small"
  142. priority="primary"
  143. icon={<IconAdd size="xs" isCircled />}
  144. >
  145. {t('Create New Hook')}
  146. </Button>
  147. ) : null
  148. }
  149. />
  150. <Panel>{body}</Panel>
  151. </React.Fragment>
  152. );
  153. }
  154. }