Browse Source

ref(tsc): Convert ProjectHpkpReports to FC (#57747)

Convert to FC and useApiQuery.
Use `useOrganization` and `useParams` instead of receiving them via
props.

- Ref https://github.com/getsentry/frontend-tsc/issues/2
ArthurKnaus 1 year ago
parent
commit
ee448405d1

+ 30 - 8
static/app/views/settings/projectSecurityHeaders/hpkp.spec.tsx

@@ -1,26 +1,48 @@
 import {initializeOrg} from 'sentry-test/initializeOrg';
-import {render} from 'sentry-test/reactTestingLibrary';
+import {render, screen} from 'sentry-test/reactTestingLibrary';
 
 import ProjectHpkpReports from 'sentry/views/settings/projectSecurityHeaders/hpkp';
 
 describe('ProjectHpkpReports', function () {
-  const {organization: org, project, routerProps} = initializeOrg();
+  const {organization, project} = initializeOrg();
+  const keysUrl = `/projects/${organization.slug}/${project.slug}/keys/`;
 
   beforeEach(function () {
     MockApiClient.clearMockResponses();
     MockApiClient.addMockResponse({
-      url: `/projects/${org.slug}/${project.slug}/`,
+      url: keysUrl,
       method: 'GET',
       body: [],
     });
+  });
+
+  it('renders', async function () {
+    render(<ProjectHpkpReports />, {
+      organization,
+    });
+
+    // Renders the loading indication initially
+    expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
+
+    // Heading
+    expect(
+      await screen.findByText('HTTP Public Key Pinning', {selector: 'h4'})
+    ).toBeInTheDocument();
+  });
+
+  it('renders loading error', async function () {
     MockApiClient.addMockResponse({
-      url: `/projects/${org.slug}/${project.slug}/keys/`,
+      url: keysUrl,
       method: 'GET',
-      body: [],
+      statusCode: 400,
+      body: {},
+    });
+    render(<ProjectHpkpReports />, {
+      organization,
     });
-  });
 
-  it('renders', function () {
-    render(<ProjectHpkpReports {...routerProps} organization={org} />);
+    expect(
+      await screen.findByText('There was an error loading data.')
+    ).toBeInTheDocument();
   });
 });

+ 100 - 106
static/app/views/settings/projectSecurityHeaders/hpkp.tsx

@@ -1,140 +1,134 @@
-import {RouteComponentProps} from 'react-router';
-
 import ExternalLink from 'sentry/components/links/externalLink';
+import LoadingError from 'sentry/components/loadingError';
+import LoadingIndicator from 'sentry/components/loadingIndicator';
 import Panel from 'sentry/components/panels/panel';
 import PanelBody from 'sentry/components/panels/panelBody';
 import PanelHeader from 'sentry/components/panels/panelHeader';
 import PreviewFeature from 'sentry/components/previewFeature';
+import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
 import {t, tct} from 'sentry/locale';
-import {Organization, ProjectKey} from 'sentry/types';
+import {ProjectKey} from 'sentry/types';
+import {useApiQuery} from 'sentry/utils/queryClient';
 import routeTitleGen from 'sentry/utils/routeTitle';
-import withOrganization from 'sentry/utils/withOrganization';
-import DeprecatedAsyncView from 'sentry/views/deprecatedAsyncView';
+import useOrganization from 'sentry/utils/useOrganization';
+import {useParams} from 'sentry/utils/useParams';
 import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
 import ReportUri, {
   getSecurityDsn,
 } from 'sentry/views/settings/projectSecurityHeaders/reportUri';
 
-type Props = RouteComponentProps<{projectId: string}, {}> & {
-  organization: Organization;
-};
-
-type State = {
-  keyList: null | ProjectKey[];
-} & DeprecatedAsyncView['state'];
-
-class ProjectHpkpReports extends DeprecatedAsyncView<Props, State> {
-  getEndpoints(): ReturnType<DeprecatedAsyncView['getEndpoints']> {
-    const {organization} = this.props;
-    const {projectId} = this.props.params;
-    return [['keyList', `/projects/${organization.slug}/${projectId}/keys/`]];
-  }
+function getInstructions(keyList: ProjectKey[]) {
+  return (
+    'def middleware(request, response):\n' +
+    "    response['Public-Key-Pins'] = \\\n" +
+    '        \'pin-sha256="cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs="; \' \\\n' +
+    '        \'pin-sha256="M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE="; \' \\\n' +
+    "        'max-age=5184000; includeSubDomains; ' \\\n" +
+    `        \'report-uri="${getSecurityDsn(keyList)}"\' \n` +
+    '    return response\n'
+  );
+}
 
-  getTitle() {
-    const {projectId} = this.props.params;
-    return routeTitleGen(t('HTTP Public Key Pinning (HPKP)'), projectId, false);
-  }
+function getReportOnlyInstructions(keyList: ProjectKey[]) {
+  return (
+    'def middleware(request, response):\n' +
+    "    response['Public-Key-Pins-Report-Only'] = \\\n" +
+    '        \'pin-sha256="cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs="; \' \\\n' +
+    '        \'pin-sha256="M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE="; \' \\\n' +
+    "        'max-age=5184000; includeSubDomains; ' \\\n" +
+    `        \'report-uri="${getSecurityDsn(keyList)}"\' \n` +
+    '    return response\n'
+  );
+}
 
-  getInstructions(keyList: ProjectKey[]) {
-    return (
-      'def middleware(request, response):\n' +
-      "    response['Public-Key-Pins'] = \\\n" +
-      '        \'pin-sha256="cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs="; \' \\\n' +
-      '        \'pin-sha256="M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE="; \' \\\n' +
-      "        'max-age=5184000; includeSubDomains; ' \\\n" +
-      `        \'report-uri="${getSecurityDsn(keyList)}"\' \n` +
-      '    return response\n'
-    );
+function ProjectHpkpReports() {
+  const organization = useOrganization();
+  const {projectId} = useParams();
+
+  const {
+    data: keyList,
+    isLoading,
+    isError,
+    refetch,
+  } = useApiQuery<ProjectKey[]>([`/projects/${organization.slug}/${projectId}/keys/`], {
+    staleTime: 0,
+  });
+
+  if (isLoading) {
+    return <LoadingIndicator />;
   }
 
-  getReportOnlyInstructions(keyList: ProjectKey[]) {
-    return (
-      'def middleware(request, response):\n' +
-      "    response['Public-Key-Pins-Report-Only'] = \\\n" +
-      '        \'pin-sha256="cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs="; \' \\\n' +
-      '        \'pin-sha256="M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE="; \' \\\n' +
-      "        'max-age=5184000; includeSubDomains; ' \\\n" +
-      `        \'report-uri="${getSecurityDsn(keyList)}"\' \n` +
-      '    return response\n'
-    );
+  if (isError) {
+    return <LoadingError onRetry={refetch} />;
   }
 
-  renderBody() {
-    const {organization, params} = this.props;
-    const {keyList} = this.state;
-    if (!keyList) {
-      return null;
-    }
+  return (
+    <div>
+      <SentryDocumentTitle
+        title={routeTitleGen(t('HTTP Public Key Pinning (HPKP)'), projectId, false)}
+      />
+      <SettingsPageHeader title={t('HTTP Public Key Pinning')} />
 
-    return (
-      <div>
-        <SettingsPageHeader title={t('HTTP Public Key Pinning')} />
+      <PreviewFeature />
 
-        <PreviewFeature />
+      <ReportUri keyList={keyList} orgId={organization.slug} projectId={projectId} />
 
-        <ReportUri
-          keyList={keyList}
-          orgId={organization.slug}
-          projectId={params.projectId}
-        />
+      <Panel>
+        <PanelHeader>{t('About')}</PanelHeader>
 
-        <Panel>
-          <PanelHeader>{t('About')}</PanelHeader>
-
-          <PanelBody withPadding>
-            <p>
-              {tct(
-                `[link:HTTP Public Key Pinning]
+        <PanelBody withPadding>
+          <p>
+            {tct(
+              `[link:HTTP Public Key Pinning]
               (HPKP) is a security feature that tells a web client to associate a specific
               cryptographic public key with a certain web server to decrease the risk of MITM
               attacks with forged certificates. It's enforced by browser vendors, and Sentry
               supports capturing violations using the standard reporting hooks.`,
-                {
-                  link: (
-                    <ExternalLink href="https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning" />
-                  ),
-                }
-              )}
-            </p>
-
-            <p>
-              {t(
-                `To configure HPKP reports
+              {
+                link: (
+                  <ExternalLink href="https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning" />
+                ),
+              }
+            )}
+          </p>
+
+          <p>
+            {t(
+              `To configure HPKP reports
               in Sentry, you'll need to send a header from your server describing your
               policy, as well specifying the authenticated Sentry endpoint.`
-              )}
-            </p>
-
-            <p>
-              {t(
-                'For example, in Python you might achieve this via a simple web middleware'
-              )}
-            </p>
-            <pre>{this.getInstructions(keyList)}</pre>
-
-            <p>
-              {t(`Alternatively you can setup HPKP reports to simply send reports rather than
+            )}
+          </p>
+
+          <p>
+            {t(
+              'For example, in Python you might achieve this via a simple web middleware'
+            )}
+          </p>
+          <pre>{getInstructions(keyList)}</pre>
+
+          <p>
+            {t(`Alternatively you can setup HPKP reports to simply send reports rather than
               actually enforcing the policy`)}
-            </p>
-            <pre>{this.getReportOnlyInstructions(keyList)}</pre>
+          </p>
+          <pre>{getReportOnlyInstructions(keyList)}</pre>
 
-            <p>
-              {tct(
-                `We recommend setting this up to only run on a percentage of requests, as
+          <p>
+            {tct(
+              `We recommend setting this up to only run on a percentage of requests, as
               otherwise you may find that you've quickly exhausted your quota. For more
               information, take a look at [link:the documentation on MDN].`,
-                {
-                  link: (
-                    <ExternalLink href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Public_Key_Pinning" />
-                  ),
-                }
-              )}
-            </p>
-          </PanelBody>
-        </Panel>
-      </div>
-    );
-  }
+              {
+                link: (
+                  <ExternalLink href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Public_Key_Pinning" />
+                ),
+              }
+            )}
+          </p>
+        </PanelBody>
+      </Panel>
+    </div>
+  );
 }
 
-export default withOrganization(ProjectHpkpReports);
+export default ProjectHpkpReports;