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:
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 ? (
+ <>
+
+
+ >
+ ) : (
+ <>>
+ )}