index.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. import {Fragment, useCallback, useEffect, useState} from 'react';
  2. import {browserHistory} from 'react-router';
  3. import * as Sentry from '@sentry/react';
  4. import type {Location} from 'history';
  5. import {addErrorMessage} from 'sentry/actionCreators/indicator';
  6. import type {CursorHandler} from 'sentry/components/pagination';
  7. import type {AuditLog} from 'sentry/types';
  8. import {decodeScalar} from 'sentry/utils/queryString';
  9. import useApi from 'sentry/utils/useApi';
  10. import useOrganization from 'sentry/utils/useOrganization';
  11. import AuditLogList from './auditLogList';
  12. type Props = {
  13. location: Location;
  14. };
  15. type State = {
  16. entryList: AuditLog[] | null;
  17. entryListPageLinks: string | null;
  18. eventType: string | undefined;
  19. eventTypes: string[] | null;
  20. isLoading: boolean;
  21. currentCursor?: string;
  22. };
  23. function OrganizationAuditLog({location}: Props) {
  24. const [state, setState] = useState<State>({
  25. entryList: [],
  26. entryListPageLinks: null,
  27. eventType: decodeScalar(location.query.event),
  28. eventTypes: [],
  29. isLoading: true,
  30. });
  31. const organization = useOrganization();
  32. const api = useApi();
  33. const handleCursor: CursorHandler = resultsCursor => {
  34. setState(prevState => ({
  35. ...prevState,
  36. currentCursor: resultsCursor,
  37. }));
  38. };
  39. useEffect(() => {
  40. // Watch the location for changes so we can re-fetch data.
  41. const eventType = decodeScalar(location.query.event);
  42. setState(prevState => ({...prevState, eventType}));
  43. }, [location.query]);
  44. const fetchAuditLogData = useCallback(async () => {
  45. setState(prevState => ({...prevState, isLoading: true}));
  46. try {
  47. const payload = {cursor: state.currentCursor, event: state.eventType};
  48. if (!payload.cursor) {
  49. delete payload.cursor;
  50. }
  51. if (!payload.event) {
  52. delete payload.event;
  53. }
  54. setState(prevState => ({...prevState, isLoading: true}));
  55. const [data, _, response] = await api.requestPromise(
  56. `/organizations/${organization.slug}/audit-logs/`,
  57. {
  58. method: 'GET',
  59. includeAllArgs: true,
  60. query: payload,
  61. }
  62. );
  63. setState(prevState => ({
  64. ...prevState,
  65. entryList: data.rows,
  66. eventTypes: data.options,
  67. isLoading: false,
  68. entryListPageLinks: response?.getResponseHeader('Link') ?? null,
  69. }));
  70. } catch (err) {
  71. if (err.status !== 401 && err.status !== 403) {
  72. Sentry.captureException(err);
  73. }
  74. setState(prevState => ({
  75. ...prevState,
  76. isLoading: false,
  77. }));
  78. addErrorMessage('Unable to load audit logs.');
  79. }
  80. }, [api, organization.slug, state.currentCursor, state.eventType]);
  81. useEffect(() => {
  82. fetchAuditLogData();
  83. }, [fetchAuditLogData]);
  84. const handleEventSelect = (value: string) => {
  85. setState(prevState => ({
  86. ...prevState,
  87. eventType: value,
  88. }));
  89. browserHistory.push({
  90. pathname: location.pathname,
  91. query: {...location.query, event: value},
  92. });
  93. };
  94. return (
  95. <Fragment>
  96. <AuditLogList
  97. entries={state.entryList}
  98. pageLinks={state.entryListPageLinks}
  99. eventType={state.eventType}
  100. eventTypes={state.eventTypes}
  101. onEventSelect={handleEventSelect}
  102. isLoading={state.isLoading}
  103. onCursor={handleCursor}
  104. />
  105. </Fragment>
  106. );
  107. }
  108. export default OrganizationAuditLog;