From e8252c442312502d5bbb1ddc7548bb3044da84be Mon Sep 17 00:00:00 2001 From: Ana Sofia Marin Alexandre <61988046+anamarn@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:41:41 +0200 Subject: [PATCH] Add webhook response graph from the last 5 days (#7487) #7346 #7343 #7342 #7344 Before: Screenshot 2024-10-08 at 11 59 37 Now: ![Screenshot 2024-10-07 at 18 56 21](https://github.com/user-attachments/assets/c87ee17a-c6c4-4938-b024-aaa635bab022) In order to test: 1. Set ANALYTICS_ENABLED to true 2. Set TINYBIRD_TOKEN to your token from the workspace _twenty_analytics_playground_ 3. Write your client tinybird token in SettingsDeveloppersWebhookDetail.tsx in line 93 4. Create a Webhook in twenty and set wich events it needs to track 5. Run twenty-worker in order to make the webhooks work. 6. Do your tasks in order to populate the data 7. Enter to settings> webhook>your webhook and the statistics section should be displayed. --- package.json | 2 - packages/twenty-front/package.json | 3 + .../modules/app/components/SettingsRoutes.tsx | 4 +- .../SettingsDevelopersWebhookUsageGraph.tsx | 59 +++ ...tingsDevelopersWebhookUsageGraphEffect.tsx | 101 +++++ .../webhook/states/webhookGraphDataState.ts | 7 + .../modules/workspace/types/FeatureFlagKey.ts | 3 +- ...ttingsDevelopersWebhooksDetail.stories.tsx | 2 +- .../SettingsDevelopersWebhooksNew.stories.tsx | 2 +- .../SettingsDevelopersWebhookDetail.tsx | 13 + .../SettingsDevelopersWebhooksNew.tsx | 0 .../typeorm-seeds/core/feature-flags.ts | 5 + .../jobs/call-webhook.job.ts | 35 +- .../jobs/workspace-query-runner-job.module.ts | 2 + .../enums/feature-flag-key.enum.ts | 1 + yarn.lock | 399 ++++++++++-------- 16 files changed, 453 insertions(+), 185 deletions(-) create mode 100644 packages/twenty-front/src/modules/settings/developers/webhook/components/SettingsDevelopersWebhookUsageGraph.tsx create mode 100644 packages/twenty-front/src/modules/settings/developers/webhook/components/SettingsDevelopersWebhookUsageGraphEffect.tsx create mode 100644 packages/twenty-front/src/modules/settings/developers/webhook/states/webhookGraphDataState.ts rename packages/twenty-front/src/pages/settings/developers/webhooks/{ => components}/SettingsDevelopersWebhookDetail.tsx (91%) rename packages/twenty-front/src/pages/settings/developers/webhooks/{ => components}/SettingsDevelopersWebhooksNew.tsx (100%) diff --git a/package.json b/package.json index 5503530175de..a4dc90df92ac 100644 --- a/package.json +++ b/package.json @@ -36,8 +36,6 @@ "@nestjs/serve-static": "^4.0.1", "@nestjs/terminus": "^9.2.2", "@nestjs/typeorm": "^10.0.0", - "@nivo/calendar": "^0.84.0", - "@nivo/core": "^0.84.0", "@nx/eslint-plugin": "^17.2.8", "@octokit/graphql": "^7.0.2", "@ptc-org/nestjs-query-core": "^4.2.0", diff --git a/packages/twenty-front/package.json b/packages/twenty-front/package.json index 90bd99f40175..b7c173b7fed0 100644 --- a/packages/twenty-front/package.json +++ b/packages/twenty-front/package.json @@ -30,6 +30,9 @@ "workerDirectory": "public" }, "dependencies": { + "@nivo/calendar": "^0.87.0", + "@nivo/core": "^0.87.0", + "@nivo/line": "^0.87.0", "@xyflow/react": "^12.0.4", "transliteration": "^2.3.5" } diff --git a/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx b/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx index 6f37c0595e12..5eede03959b5 100644 --- a/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx +++ b/packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx @@ -65,7 +65,7 @@ const SettingsDevelopersApiKeysNew = lazy(() => const SettingsDevelopersWebhooksNew = lazy(() => import( - '~/pages/settings/developers/webhooks/SettingsDevelopersWebhooksNew' + '~/pages/settings/developers/webhooks/components/SettingsDevelopersWebhooksNew' ).then((module) => ({ default: module.SettingsDevelopersWebhooksNew, })), @@ -165,7 +165,7 @@ const SettingsObjects = lazy(() => const SettingsDevelopersWebhooksDetail = lazy(() => import( - '~/pages/settings/developers/webhooks/SettingsDevelopersWebhookDetail' + '~/pages/settings/developers/webhooks/components/SettingsDevelopersWebhookDetail' ).then((module) => ({ default: module.SettingsDevelopersWebhooksDetail, })), diff --git a/packages/twenty-front/src/modules/settings/developers/webhook/components/SettingsDevelopersWebhookUsageGraph.tsx b/packages/twenty-front/src/modules/settings/developers/webhook/components/SettingsDevelopersWebhookUsageGraph.tsx new file mode 100644 index 000000000000..eb2e359fff16 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/developers/webhook/components/SettingsDevelopersWebhookUsageGraph.tsx @@ -0,0 +1,59 @@ +import { webhookGraphDataState } from '@/settings/developers/webhook/states/webhookGraphDataState'; +import styled from '@emotion/styled'; +import { ResponsiveLine } from '@nivo/line'; +import { Section } from '@react-email/components'; +import { useRecoilValue } from 'recoil'; +import { H2Title } from 'twenty-ui'; + +export type NivoLineInput = { + id: string | number; + color?: string; + data: Array<{ + x: number | string | Date; + y: number | string | Date; + }>; +}; +const StyledGraphContainer = styled.div` + height: 200px; + width: 100%; +`; +export const SettingsDeveloppersWebhookUsageGraph = () => { + const webhookGraphData = useRecoilValue(webhookGraphDataState); + + return ( + <> + {webhookGraphData.length ? ( +
+ + + d.color} + margin={{ top: 0, right: 0, bottom: 50, left: 60 }} + xFormat="time:%Y-%m-%d %H:%M%" + xScale={{ + type: 'time', + useUTC: false, + format: '%Y-%m-%d %H:%M:%S', + precision: 'hour', + }} + yScale={{ + type: 'linear', + }} + axisBottom={{ + tickValues: 'every day', + format: '%b %d', + }} + enableTouchCrosshair={true} + enableGridY={false} + enableGridX={false} + enablePoints={false} + /> + +
+ ) : ( + <> + )} + + ); +}; diff --git a/packages/twenty-front/src/modules/settings/developers/webhook/components/SettingsDevelopersWebhookUsageGraphEffect.tsx b/packages/twenty-front/src/modules/settings/developers/webhook/components/SettingsDevelopersWebhookUsageGraphEffect.tsx new file mode 100644 index 000000000000..0c26350243b2 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/developers/webhook/components/SettingsDevelopersWebhookUsageGraphEffect.tsx @@ -0,0 +1,101 @@ +import { NivoLineInput } from '@/settings/developers/webhook/components/SettingsDevelopersWebhookUsageGraph'; +import { webhookGraphDataState } from '@/settings/developers/webhook/states/webhookGraphDataState'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; +import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; +import { useEffect } from 'react'; +import { useSetRecoilState } from 'recoil'; + +type SettingsDevelopersWebhookUsageGraphEffectProps = { + webhookId: string; +}; + +export const SettingsDevelopersWebhookUsageGraphEffect = ({ + webhookId, +}: SettingsDevelopersWebhookUsageGraphEffectProps) => { + const setWebhookGraphData = useSetRecoilState(webhookGraphDataState); + + const { enqueueSnackBar } = useSnackBar(); + + useEffect(() => { + const fetchData = async () => { + try { + const queryString = new URLSearchParams({ + webhookIdRequest: webhookId, + }).toString(); + const token = 'REPLACE_ME'; + const response = await fetch( + `https://api.eu-central-1.aws.tinybird.co/v0/pipes/getWebhooksAnalytics.json?${queryString}`, + { + headers: { + Authorization: 'Bearer ' + token, + }, + }, + ); + const result = await response.json(); + + if (!response.ok) { + enqueueSnackBar('Something went wrong while fetching webhook usage', { + variant: SnackBarVariant.Error, + }); + return; + } + + const graphInput = result.data + .flatMap( + (dataRow: { + start_interval: string; + failure_count: number; + success_count: number; + }) => [ + { + x: dataRow.start_interval, + y: dataRow.failure_count, + id: 'failure_count', + color: 'red', + }, + { + x: dataRow.start_interval, + y: dataRow.success_count, + id: 'success_count', + color: 'green', + }, + ], + ) + .reduce( + ( + acc: NivoLineInput[], + { + id, + x, + y, + color, + }: { id: string; x: string; y: number; color: string }, + ) => { + const existingGroupIndex = acc.findIndex( + (group) => group.id === id, + ); + const isExistingGroup = existingGroupIndex !== -1; + + if (isExistingGroup) { + return acc.map((group, index) => + index === existingGroupIndex + ? { ...group, data: [...group.data, { x, y }] } + : group, + ); + } else { + return [...acc, { id, color, data: [{ x, y }] }]; + } + }, + [], + ); + setWebhookGraphData(graphInput); + } catch (error) { + enqueueSnackBar('Something went wrong while fetching webhook usage', { + variant: SnackBarVariant.Error, + }); + } + }; + fetchData(); + }, [enqueueSnackBar, setWebhookGraphData, webhookId]); + return <>; +}; diff --git a/packages/twenty-front/src/modules/settings/developers/webhook/states/webhookGraphDataState.ts b/packages/twenty-front/src/modules/settings/developers/webhook/states/webhookGraphDataState.ts new file mode 100644 index 000000000000..bb91864e2782 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/developers/webhook/states/webhookGraphDataState.ts @@ -0,0 +1,7 @@ +import { NivoLineInput } from '@/settings/developers/webhook/components/SettingsDevelopersWebhookUsageGraph'; +import { createState } from 'twenty-ui'; + +export const webhookGraphDataState = createState({ + key: 'webhookGraphData', + defaultValue: [], +}); diff --git a/packages/twenty-front/src/modules/workspace/types/FeatureFlagKey.ts b/packages/twenty-front/src/modules/workspace/types/FeatureFlagKey.ts index 3abc8299b7f0..9a79efa933bf 100644 --- a/packages/twenty-front/src/modules/workspace/types/FeatureFlagKey.ts +++ b/packages/twenty-front/src/modules/workspace/types/FeatureFlagKey.ts @@ -13,4 +13,5 @@ export type FeatureFlagKey = | 'IS_SEARCH_ENABLED' | 'IS_QUERY_RUNNER_TWENTY_ORM_ENABLED' | 'IS_GMAIL_SEND_EMAIL_SCOPE_ENABLED' - | 'IS_WORKSPACE_MIGRATED_FOR_SEARCH'; + | 'IS_WORKSPACE_MIGRATED_FOR_SEARCH' + | 'IS_ANALYTICS_V2_ENABLED'; diff --git a/packages/twenty-front/src/pages/settings/developers/__stories__/webhooks/SettingsDevelopersWebhooksDetail.stories.tsx b/packages/twenty-front/src/pages/settings/developers/__stories__/webhooks/SettingsDevelopersWebhooksDetail.stories.tsx index ec430fcf3058..a77e38d73a5b 100644 --- a/packages/twenty-front/src/pages/settings/developers/__stories__/webhooks/SettingsDevelopersWebhooksDetail.stories.tsx +++ b/packages/twenty-front/src/pages/settings/developers/__stories__/webhooks/SettingsDevelopersWebhooksDetail.stories.tsx @@ -2,7 +2,7 @@ import { Meta, StoryObj } from '@storybook/react'; import { within } from '@storybook/test'; import { graphql, HttpResponse } from 'msw'; -import { SettingsDevelopersWebhooksDetail } from '~/pages/settings/developers/webhooks/SettingsDevelopersWebhookDetail'; +import { SettingsDevelopersWebhooksDetail } from '~/pages/settings/developers/webhooks/components/SettingsDevelopersWebhookDetail'; import { PageDecorator, PageDecoratorArgs, diff --git a/packages/twenty-front/src/pages/settings/developers/__stories__/webhooks/SettingsDevelopersWebhooksNew.stories.tsx b/packages/twenty-front/src/pages/settings/developers/__stories__/webhooks/SettingsDevelopersWebhooksNew.stories.tsx index dd4adfb71305..430349183258 100644 --- a/packages/twenty-front/src/pages/settings/developers/__stories__/webhooks/SettingsDevelopersWebhooksNew.stories.tsx +++ b/packages/twenty-front/src/pages/settings/developers/__stories__/webhooks/SettingsDevelopersWebhooksNew.stories.tsx @@ -1,7 +1,7 @@ import { Meta, StoryObj } from '@storybook/react'; import { within } from '@storybook/test'; -import { SettingsDevelopersWebhooksNew } from '~/pages/settings/developers/webhooks/SettingsDevelopersWebhooksNew'; +import { SettingsDevelopersWebhooksNew } from '~/pages/settings/developers/webhooks/components/SettingsDevelopersWebhooksNew'; import { PageDecorator, PageDecoratorArgs, diff --git a/packages/twenty-front/src/pages/settings/developers/webhooks/SettingsDevelopersWebhookDetail.tsx b/packages/twenty-front/src/pages/settings/developers/webhooks/components/SettingsDevelopersWebhookDetail.tsx similarity index 91% rename from packages/twenty-front/src/pages/settings/developers/webhooks/SettingsDevelopersWebhookDetail.tsx rename to packages/twenty-front/src/pages/settings/developers/webhooks/components/SettingsDevelopersWebhookDetail.tsx index ffe6d9807c85..f88ba926c6b8 100644 --- a/packages/twenty-front/src/pages/settings/developers/webhooks/SettingsDevelopersWebhookDetail.tsx +++ b/packages/twenty-front/src/pages/settings/developers/webhooks/components/SettingsDevelopersWebhookDetail.tsx @@ -11,6 +11,8 @@ import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons'; import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; import { Webhook } from '@/settings/developers/types/webhook/Webhook'; +import { SettingsDeveloppersWebhookUsageGraph } from '@/settings/developers/webhook/components/SettingsDevelopersWebhookUsageGraph'; +import { SettingsDevelopersWebhookUsageGraphEffect } from '@/settings/developers/webhook/components/SettingsDevelopersWebhookUsageGraphEffect'; import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; import { Button } from '@/ui/input/button/components/Button'; @@ -20,6 +22,7 @@ import { TextInput } from '@/ui/input/components/TextInput'; import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal'; import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer'; import { Section } from '@/ui/layout/section/components/Section'; +import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; const StyledFilterRow = styled.div` display: flex; @@ -63,6 +66,8 @@ export const SettingsDevelopersWebhooksDetail = () => { navigate(developerPath); }; + const isAnalyticsV2Enabled = useIsFeatureEnabled('IS_ANALYTICS_V2_ENABLED'); + const fieldTypeOptions = [ { value: '*', label: 'All Objects' }, ...objectMetadataItems.map((item) => ({ @@ -173,6 +178,14 @@ export const SettingsDevelopersWebhooksDetail = () => { /> + {isAnalyticsV2Enabled ? ( + <> + + + + ) : ( + <> + )}