mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-22 19:41:53 +03:00
Secure connexion between TinyBird and webhookResponseGraph (#7913)
TLDR: Secure connexion between tinybird and twenty using jwt when accessing datasource from tinybird. Solves: https://github.com/twentyhq/private-issues/issues/73 In order to test: 1. Set ANALYTICS_ENABLED to true 2. Set TINYBIRD_JWT_TOKEN to the ADMIN token from the workspace twenty_analytics_playground 3. Set TINYBIRD_JWT_TOKEN to the datasource or your admin token from the workspace twenty_analytics_playground 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. --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
parent
edf4ae084b
commit
373926b895
4
.github/workflows/ci-chrome-extension.yaml
vendored
4
.github/workflows/ci-chrome-extension.yaml
vendored
@ -34,10 +34,10 @@ jobs:
|
||||
packages/twenty-chrome-extension/**
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.changed-files.outputs.changed == 'true'
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/yarn-install
|
||||
- name: Chrome Extension / Run build
|
||||
if: steps.changed-files.outputs.changed == 'true'
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: npx nx build twenty-chrome-extension
|
||||
|
||||
- name: Mark as Valid if No Changes
|
||||
|
39
.github/workflows/ci-server.yaml
vendored
39
.github/workflows/ci-server.yaml
vendored
@ -37,30 +37,33 @@ jobs:
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v11
|
||||
with:
|
||||
files: 'package.json, packages/twenty-server/**, packages/twenty-emails/**'
|
||||
files: |
|
||||
package.json
|
||||
packages/twenty-server/**
|
||||
packages/twenty-emails/**
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.changed-files.outputs.changed == 'true'
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/yarn-install
|
||||
- name: Server / Restore Task Cache
|
||||
if: steps.changed-files.outputs.changed == 'true'
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/task-cache
|
||||
with:
|
||||
tag: scope:backend
|
||||
- name: Server / Run lint & typecheck
|
||||
if: steps.changed-files.outputs.changed == 'true'
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/nx-affected
|
||||
with:
|
||||
tag: scope:backend
|
||||
tasks: lint,typecheck
|
||||
- name: Server / Build
|
||||
if: steps.changed-files.outputs.changed == 'true'
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: npx nx build twenty-server
|
||||
- name: Server / Write .env
|
||||
if: steps.changed-files.outputs.changed == 'true'
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: npx nx reset:env twenty-server
|
||||
- name: Worker / Run
|
||||
if: steps.changed-files.outputs.changed == 'true'
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: npx nx run twenty-server:worker:ci
|
||||
|
||||
server-test:
|
||||
@ -78,18 +81,21 @@ jobs:
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v11
|
||||
with:
|
||||
files: 'package.json, packages/twenty-server/**, packages/twenty-emails/**'
|
||||
files: |
|
||||
package.json
|
||||
packages/twenty-server/**
|
||||
packages/twenty-emails/**
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.changed-files.outputs.changed == 'true'
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/yarn-install
|
||||
- name: Server / Restore Task Cache
|
||||
if: steps.changed-files.outputs.changed == 'true'
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/task-cache
|
||||
with:
|
||||
tag: scope:backend
|
||||
- name: Server / Run Tests
|
||||
if: steps.changed-files.outputs.changed == 'true'
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/nx-affected
|
||||
with:
|
||||
tag: scope:backend
|
||||
@ -122,18 +128,21 @@ jobs:
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v11
|
||||
with:
|
||||
files: 'package.json, packages/twenty-server/**, packages/twenty-emails/**'
|
||||
files: |
|
||||
package.json
|
||||
packages/twenty-server/**
|
||||
packages/twenty-emails/**
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.changed-files.outputs.changed == 'true'
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/yarn-install
|
||||
- name: Server / Restore Task Cache
|
||||
if: steps.changed-files.outputs.changed == 'true'
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/task-cache
|
||||
with:
|
||||
tag: scope:backend
|
||||
- name: Server / Run Integration Tests
|
||||
if: steps.changed-files.outputs.changed == 'true'
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/nx-affected
|
||||
with:
|
||||
tag: scope:backend
|
||||
|
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -45,5 +45,5 @@
|
||||
"search.exclude": {
|
||||
"**/.yarn": true,
|
||||
},
|
||||
"eslint.debug": true
|
||||
"eslint.debug": true,
|
||||
}
|
||||
|
@ -1058,6 +1058,7 @@ export type UpdateWorkspaceInput = {
|
||||
|
||||
export type User = {
|
||||
__typename?: 'User';
|
||||
analyticsTinybirdJwt?: Maybe<Scalars['String']>;
|
||||
canImpersonate: Scalars['Boolean'];
|
||||
createdAt: Scalars['DateTime'];
|
||||
defaultAvatarUrl?: Maybe<Scalars['String']>;
|
||||
@ -1520,7 +1521,7 @@ export type ImpersonateMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, metadataVersion: number, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, analyticsTinybirdJwt?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, metadataVersion: number, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
|
||||
export type RenewTokenMutationVariables = Exact<{
|
||||
appToken: Scalars['String'];
|
||||
@ -1553,7 +1554,7 @@ export type VerifyMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, metadataVersion: number, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, analyticsTinybirdJwt?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, metadataVersion: number, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
|
||||
export type CheckUserExistsQueryVariables = Exact<{
|
||||
email: Scalars['String'];
|
||||
@ -1607,7 +1608,7 @@ export type SkipSyncEmailOnboardingStepMutationVariables = Exact<{ [key: string]
|
||||
|
||||
export type SkipSyncEmailOnboardingStepMutation = { __typename?: 'Mutation', skipSyncEmailOnboardingStep: { __typename?: 'OnboardingStepSuccess', success: boolean } };
|
||||
|
||||
export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, metadataVersion: number, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> };
|
||||
export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, analyticsTinybirdJwt?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, metadataVersion: number, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> };
|
||||
|
||||
export type DeleteUserAccountMutationVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
@ -1624,7 +1625,7 @@ export type UploadProfilePictureMutation = { __typename?: 'Mutation', uploadProf
|
||||
export type GetCurrentUserQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, metadataVersion: number, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> } };
|
||||
export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, analyticsTinybirdJwt?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, metadataVersion: number, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> } };
|
||||
|
||||
export type ActivateWorkflowVersionMutationVariables = Exact<{
|
||||
workflowVersionId: Scalars['String'];
|
||||
@ -1825,6 +1826,7 @@ export const UserQueryFragmentFragmentDoc = gql`
|
||||
email
|
||||
canImpersonate
|
||||
supportUserHash
|
||||
analyticsTinybirdJwt
|
||||
onboardingStatus
|
||||
workspaceMember {
|
||||
...WorkspaceMemberQueryFragment
|
||||
|
@ -7,6 +7,7 @@ export type CurrentUser = Pick<
|
||||
| 'id'
|
||||
| 'email'
|
||||
| 'supportUserHash'
|
||||
| 'analyticsTinybirdJwt'
|
||||
| 'canImpersonate'
|
||||
| 'onboardingStatus'
|
||||
| 'userVars'
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useGraphData } from '@/settings/developers/webhook/hooks/useGraphData';
|
||||
import { webhookGraphDataState } from '@/settings/developers/webhook/states/webhookGraphDataState';
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
type SettingsDevelopersWebhookUsageGraphEffectProps = {
|
||||
@ -11,14 +11,18 @@ export const SettingsDevelopersWebhookUsageGraphEffect = ({
|
||||
webhookId,
|
||||
}: SettingsDevelopersWebhookUsageGraphEffectProps) => {
|
||||
const setWebhookGraphData = useSetRecoilState(webhookGraphDataState);
|
||||
const [isLoaded, setIsLoaded] = useState(false);
|
||||
|
||||
const { fetchGraphData } = useGraphData(webhookId);
|
||||
|
||||
useEffect(() => {
|
||||
fetchGraphData('7D').then((graphInput) => {
|
||||
setWebhookGraphData(graphInput);
|
||||
});
|
||||
}, [fetchGraphData, setWebhookGraphData, webhookId]);
|
||||
if (!isLoaded) {
|
||||
fetchGraphData('7D').then((graphInput) => {
|
||||
setWebhookGraphData(graphInput);
|
||||
});
|
||||
setIsLoaded(true);
|
||||
}
|
||||
}, [fetchGraphData, isLoaded, setWebhookGraphData, webhookId]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
@ -0,0 +1,47 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
|
||||
import { CurrentUser, currentUserState } from '@/auth/states/currentUserState';
|
||||
import { useAnalyticsTinybirdJwt } from '@/settings/developers/webhook/hooks/useAnalyticsTinybirdJwt';
|
||||
import { act } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper';
|
||||
|
||||
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||
apolloMocks: [],
|
||||
});
|
||||
|
||||
describe('useAnalyticsTinybirdJwt', () => {
|
||||
it('should return the analytics jwt token', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setCurrentUserState = useSetRecoilState(currentUserState);
|
||||
|
||||
return {
|
||||
useAnalyticsTinybirdJwt: useAnalyticsTinybirdJwt(),
|
||||
setCurrentUserState,
|
||||
};
|
||||
},
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.setCurrentUserState({
|
||||
analyticsTinybirdJwt: 'jwt',
|
||||
} as CurrentUser);
|
||||
});
|
||||
|
||||
expect(result.current.useAnalyticsTinybirdJwt).toBe('jwt');
|
||||
|
||||
act(() => {
|
||||
result.current.setCurrentUserState(null);
|
||||
});
|
||||
|
||||
expect(result.current.useAnalyticsTinybirdJwt).toBeUndefined();
|
||||
|
||||
act(() => {
|
||||
result.current.setCurrentUserState({} as CurrentUser);
|
||||
});
|
||||
|
||||
expect(result.current.useAnalyticsTinybirdJwt).toBeUndefined();
|
||||
});
|
||||
});
|
@ -0,0 +1,18 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { isNull } from '@sniptt/guards';
|
||||
|
||||
export const useAnalyticsTinybirdJwt = (): string | undefined => {
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
|
||||
if (!currentUser) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (isNull(currentUser.analyticsTinybirdJwt)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return currentUser.analyticsTinybirdJwt;
|
||||
};
|
@ -1,16 +1,24 @@
|
||||
import { useAnalyticsTinybirdJwt } from '@/settings/developers/webhook/hooks/useAnalyticsTinybirdJwt';
|
||||
import { fetchGraphDataOrThrow } from '@/settings/developers/webhook/utils/fetchGraphDataOrThrow';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { isUndefined } from '@sniptt/guards';
|
||||
|
||||
export const useGraphData = (webhookId: string) => {
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const analyticsTinybirdJwt = useAnalyticsTinybirdJwt();
|
||||
const fetchGraphData = async (
|
||||
windowLengthGraphOption: '7D' | '1D' | '12H' | '4H',
|
||||
) => {
|
||||
try {
|
||||
if (isUndefined(analyticsTinybirdJwt)) {
|
||||
throw new Error('No analyticsTinybirdJwt found');
|
||||
}
|
||||
|
||||
return await fetchGraphDataOrThrow({
|
||||
webhookId,
|
||||
windowLength: windowLengthGraphOption,
|
||||
tinybirdJwt: analyticsTinybirdJwt,
|
||||
});
|
||||
} catch (error) {
|
||||
enqueueSnackBar('Something went wrong while fetching webhook usage', {
|
||||
|
@ -4,22 +4,24 @@ import { WEBHOOK_GRAPH_API_OPTIONS_MAP } from '@/settings/developers/webhook/con
|
||||
type fetchGraphDataOrThrowProps = {
|
||||
webhookId: string;
|
||||
windowLength: '7D' | '1D' | '12H' | '4H';
|
||||
tinybirdJwt: string;
|
||||
};
|
||||
|
||||
export const fetchGraphDataOrThrow = async ({
|
||||
webhookId,
|
||||
windowLength,
|
||||
tinybirdJwt,
|
||||
}: fetchGraphDataOrThrowProps) => {
|
||||
const queryString = new URLSearchParams({
|
||||
...WEBHOOK_GRAPH_API_OPTIONS_MAP[windowLength],
|
||||
webhookIdRequest: webhookId,
|
||||
}).toString();
|
||||
const token = 'REPLACE_ME';
|
||||
|
||||
const response = await fetch(
|
||||
`https://api.eu-central-1.aws.tinybird.co/v0/pipes/getWebhooksAnalyticsV2.json?${queryString}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + token,
|
||||
Authorization: 'Bearer ' + tinybirdJwt,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
@ -8,6 +8,7 @@ export const USER_QUERY_FRAGMENT = gql`
|
||||
email
|
||||
canImpersonate
|
||||
supportUserHash
|
||||
analyticsTinybirdJwt
|
||||
onboardingStatus
|
||||
workspaceMember {
|
||||
...WorkspaceMemberQueryFragment
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { HttpModule } from '@nestjs/axios';
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { JwtModule } from 'src/engine/core-modules/jwt/jwt.module';
|
||||
|
||||
import { AnalyticsResolver } from './analytics.resolver';
|
||||
import { AnalyticsService } from './analytics.service';
|
||||
|
||||
@ -9,6 +11,7 @@ const TINYBIRD_BASE_URL = 'https://api.eu-central-1.aws.tinybird.co/v0';
|
||||
@Module({
|
||||
providers: [AnalyticsResolver, AnalyticsService],
|
||||
imports: [
|
||||
JwtModule,
|
||||
HttpModule.register({
|
||||
baseURL: TINYBIRD_BASE_URL,
|
||||
}),
|
||||
|
@ -1,7 +1,4 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { HttpService } from '@nestjs/axios';
|
||||
|
||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||
|
||||
import { AnalyticsResolver } from './analytics.resolver';
|
||||
import { AnalyticsService } from './analytics.service';
|
||||
@ -13,13 +10,8 @@ describe('AnalyticsResolver', () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
AnalyticsResolver,
|
||||
AnalyticsService,
|
||||
{
|
||||
provide: EnvironmentService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: HttpService,
|
||||
provide: AnalyticsService,
|
||||
useValue: {},
|
||||
},
|
||||
],
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { Args, Mutation, Resolver } from '@nestjs/graphql';
|
||||
|
||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator';
|
||||
@ -13,10 +12,7 @@ import { CreateAnalyticsInput } from './dtos/create-analytics.input';
|
||||
|
||||
@Resolver(() => Analytics)
|
||||
export class AnalyticsResolver {
|
||||
constructor(
|
||||
private readonly analyticsService: AnalyticsService,
|
||||
private readonly environmentService: EnvironmentService,
|
||||
) {}
|
||||
constructor(private readonly analyticsService: AnalyticsService) {}
|
||||
|
||||
@Mutation(() => Analytics)
|
||||
track(
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { HttpService } from '@nestjs/axios';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||
import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service';
|
||||
|
||||
import { AnalyticsService } from './analytics.service';
|
||||
|
||||
@ -16,6 +17,10 @@ describe('AnalyticsService', () => {
|
||||
provide: EnvironmentService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: JwtWrapperService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: HttpService,
|
||||
useValue: {},
|
||||
|
@ -4,6 +4,7 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
import { AxiosRequestConfig } from 'axios';
|
||||
|
||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||
import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service';
|
||||
|
||||
type CreateEventInput = {
|
||||
action: string;
|
||||
@ -16,6 +17,7 @@ export class AnalyticsService {
|
||||
private readonly defaultDatasource = 'event';
|
||||
|
||||
constructor(
|
||||
private readonly jwtWrapperService: JwtWrapperService,
|
||||
private readonly environmentService: EnvironmentService,
|
||||
private readonly httpService: HttpService,
|
||||
) {}
|
||||
@ -58,7 +60,7 @@ export class AnalyticsService {
|
||||
const config: AxiosRequestConfig = {
|
||||
headers: {
|
||||
Authorization:
|
||||
'Bearer ' + this.environmentService.get('TINYBIRD_TOKEN'),
|
||||
'Bearer ' + this.environmentService.get('TINYBIRD_INGEST_TOKEN'),
|
||||
},
|
||||
};
|
||||
|
||||
@ -86,4 +88,25 @@ export class AnalyticsService {
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
async generateWorkspaceJwt(workspaceId: string | undefined) {
|
||||
const pipeId = 't_b49e0fe60f9e438eae81cb31c5260df2'; // refactor this pass as params
|
||||
//perhaps a constant of name:pipeId??? better typing in this func^
|
||||
const payload = {
|
||||
name: 'my_demo_jwt',
|
||||
workspace_id: this.environmentService.get('TINYBIRD_WORKSPACE_UUID'),
|
||||
scopes: [
|
||||
{
|
||||
type: 'PIPES:READ',
|
||||
resource: pipeId,
|
||||
fixed_params: { workspaceId: workspaceId },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return this.jwtWrapperService.sign(payload, {
|
||||
secret: this.environmentService.get('TINYBIRD_GENERATE_JWT_TOKEN'),
|
||||
expiresIn: '7d',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +95,15 @@ export class EnvironmentVariables {
|
||||
|
||||
@IsString()
|
||||
@ValidateIf((env) => env.ANALYTICS_ENABLED)
|
||||
TINYBIRD_TOKEN: string;
|
||||
TINYBIRD_INGEST_TOKEN: string;
|
||||
|
||||
@IsString()
|
||||
@ValidateIf((env) => env.ANALYTICS_ENABLED)
|
||||
TINYBIRD_WORKSPACE_UUID: string;
|
||||
|
||||
@IsString()
|
||||
@ValidateIf((env) => env.ANALYTICS_ENABLED)
|
||||
TINYBIRD_GENERATE_JWT_TOKEN: string;
|
||||
|
||||
@CastToPositiveNumber()
|
||||
@IsNumber()
|
||||
|
@ -7,6 +7,7 @@ import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
|
||||
|
||||
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||
import { AnalyticsModule } from 'src/engine/core-modules/analytics/analytics.module';
|
||||
import { FileUploadModule } from 'src/engine/core-modules/file/file-upload/file-upload.module';
|
||||
import { FileModule } from 'src/engine/core-modules/file/file.module';
|
||||
import { KeyValuePair } from 'src/engine/core-modules/key-value-pair/key-value-pair.entity';
|
||||
@ -37,6 +38,7 @@ import { UserService } from './services/user.service';
|
||||
OnboardingModule,
|
||||
TypeOrmModule.forFeature([KeyValuePair], 'core'),
|
||||
UserVarsModule,
|
||||
AnalyticsModule,
|
||||
],
|
||||
exports: [UserService],
|
||||
providers: [UserService, UserResolver, TypeORMService],
|
||||
|
@ -19,6 +19,7 @@ import { Repository } from 'typeorm';
|
||||
import { SupportDriver } from 'src/engine/core-modules/environment/interfaces/support.interface';
|
||||
import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface';
|
||||
|
||||
import { AnalyticsService } from 'src/engine/core-modules/analytics/analytics.service';
|
||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
|
||||
import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service';
|
||||
import { FileService } from 'src/engine/core-modules/file/services/file.service';
|
||||
@ -55,6 +56,7 @@ export class UserResolver {
|
||||
private readonly onboardingService: OnboardingService,
|
||||
private readonly userVarService: UserVarsService,
|
||||
private readonly fileService: FileService,
|
||||
private readonly analyticsService: AnalyticsService,
|
||||
) {}
|
||||
|
||||
@Query(() => User)
|
||||
@ -154,6 +156,15 @@ export class UserResolver {
|
||||
return getHMACKey(parent.email, key);
|
||||
}
|
||||
|
||||
@ResolveField(() => String, {
|
||||
nullable: true,
|
||||
})
|
||||
async analyticsTinybirdJwt(
|
||||
@AuthWorkspace() workspace: Workspace | undefined,
|
||||
): Promise<string> {
|
||||
return await this.analyticsService.generateWorkspaceJwt(workspace?.id);
|
||||
}
|
||||
|
||||
@Mutation(() => String)
|
||||
async uploadProfilePicture(
|
||||
@AuthUser() { id }: User,
|
||||
|
Loading…
Reference in New Issue
Block a user