integrationServerlessFunctions.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import {Fragment, useEffect} from 'react';
  2. import styled from '@emotion/styled';
  3. import {Alert} from 'sentry/components/alert';
  4. import Panel from 'sentry/components/panels/panel';
  5. import PanelBody from 'sentry/components/panels/panelBody';
  6. import PanelHeader from 'sentry/components/panels/panelHeader';
  7. import {t} from 'sentry/locale';
  8. import {space} from 'sentry/styles/space';
  9. import type {
  10. OrganizationIntegration,
  11. ServerlessFunction,
  12. } from 'sentry/types/integrations';
  13. import {trackAnalytics} from 'sentry/utils/analytics';
  14. import {
  15. type ApiQueryKey,
  16. setApiQueryData,
  17. useApiQuery,
  18. useQueryClient,
  19. } from 'sentry/utils/queryClient';
  20. import useOrganization from 'sentry/utils/useOrganization';
  21. import {IntegrationServerlessRow} from 'sentry/views/settings/organizationIntegrations/integrationServerlessRow';
  22. export function IntegrationServerlessFunctions({
  23. integration,
  24. }: {
  25. integration: OrganizationIntegration;
  26. }) {
  27. const organization = useOrganization();
  28. const queryClient = useQueryClient();
  29. const queryKey: ApiQueryKey = [
  30. `/organizations/${organization.slug}/integrations/${integration.id}/serverless-functions/`,
  31. ];
  32. const {data: serverlessFunctions = [], isSuccess} = useApiQuery<ServerlessFunction[]>(
  33. queryKey,
  34. {staleTime: 0}
  35. );
  36. useEffect(() => {
  37. if (isSuccess) {
  38. trackAnalytics('integrations.serverless_functions_viewed', {
  39. integration: integration.provider.key,
  40. integration_type: 'first_party',
  41. num_functions: serverlessFunctions.length,
  42. organization,
  43. });
  44. }
  45. // eslint-disable-next-line react-hooks/exhaustive-deps
  46. }, [isSuccess]);
  47. return (
  48. <Fragment>
  49. <Alert type="info">
  50. {t(
  51. 'Manage your AWS Lambda functions below. Only Node and Python runtimes are currently supported.'
  52. )}
  53. </Alert>
  54. <Panel>
  55. <StyledPanelHeader disablePadding hasButtons>
  56. <NameHeader>{t('Name')}</NameHeader>
  57. <LayerStatusWrapper>{t('Layer Status')}</LayerStatusWrapper>
  58. <EnableHeader>{t('Enabled')}</EnableHeader>
  59. </StyledPanelHeader>
  60. <StyledPanelBody>
  61. {serverlessFunctions.map((serverlessFn, i) => (
  62. <IntegrationServerlessRow
  63. key={serverlessFn.name}
  64. serverlessFunction={serverlessFn}
  65. integration={integration}
  66. onUpdate={(update: Partial<ServerlessFunction>) => {
  67. setApiQueryData<ServerlessFunction[]>(
  68. queryClient,
  69. queryKey,
  70. existingServerlessFunctions => {
  71. const newServerlessFunctions = [...existingServerlessFunctions];
  72. const updatedFunction = {
  73. ...newServerlessFunctions[i],
  74. ...update,
  75. };
  76. newServerlessFunctions[i] = updatedFunction;
  77. return newServerlessFunctions;
  78. }
  79. );
  80. }}
  81. />
  82. ))}
  83. </StyledPanelBody>
  84. </Panel>
  85. </Fragment>
  86. );
  87. }
  88. const StyledPanelHeader = styled(PanelHeader)`
  89. padding: ${space(2)};
  90. display: grid;
  91. grid-column-gap: ${space(1)};
  92. align-items: center;
  93. grid-template-columns: 2fr 1fr 0.5fr;
  94. grid-template-areas: 'function-name layer-status enable-switch';
  95. `;
  96. const HeaderText = styled('div')`
  97. flex: 1;
  98. `;
  99. const StyledPanelBody = styled(PanelBody)``;
  100. const NameHeader = styled(HeaderText)`
  101. grid-area: function-name;
  102. `;
  103. const LayerStatusWrapper = styled(HeaderText)`
  104. grid-area: layer-status;
  105. `;
  106. const EnableHeader = styled(HeaderText)`
  107. grid-area: enable-switch;
  108. `;