From 76daa20d69363ba49f10c280bb1e4401a115087b Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Thu, 22 Feb 2024 20:12:17 +0530 Subject: [PATCH 1/3] feat(telemetry): added telemetry events for creation of integration,project,machine identity and user invitation to org --- backend/src/server/lib/telemtry.ts | 17 ++++++ backend/src/server/routes/index.ts | 4 ++ .../src/server/routes/v1/identity-router.ts | 13 +++++ .../server/routes/v1/integration-router.ts | 46 ++++++++++------ .../src/server/routes/v1/invite-org-router.ts | 11 ++++ .../src/server/routes/v2/project-router.ts | 12 +++++ .../services/telemetry/telemetry-service.ts | 9 +++- .../src/services/telemetry/telemetry-types.ts | 54 ++++++++++++++++++- 8 files changed, 148 insertions(+), 18 deletions(-) create mode 100644 backend/src/server/lib/telemtry.ts diff --git a/backend/src/server/lib/telemtry.ts b/backend/src/server/lib/telemtry.ts new file mode 100644 index 000000000..6794d7bb3 --- /dev/null +++ b/backend/src/server/lib/telemtry.ts @@ -0,0 +1,17 @@ +import { FastifyRequest } from "fastify"; + +import { ActorType } from "@app/services/auth/auth-type"; + +// this is a unique id for sending posthog event +export const getTelemetryDistinctId = (req: FastifyRequest) => { + if (req.auth.actor === ActorType.USER) { + return req.auth.user.email; + } + if (req.auth.actor === ActorType.IDENTITY) { + return `identity-${req.auth.identityId}`; + } + if (req.auth.actor === ActorType.SERVICE) { + return `service-token-${req.auth.serviceToken.id}`; + } + return "unknown-auth-data"; +}; diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index 9444aff16..7366c7041 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -585,4 +585,8 @@ export const registerRoutes = async ( ); await server.register(registerV2Routes, { prefix: "/api/v2" }); await server.register(registerV3Routes, { prefix: "/api/v3" }); + + server.addHook("onClose", async () => { + await telemetryService.flushAll(); + }); }; diff --git a/backend/src/server/routes/v1/identity-router.ts b/backend/src/server/routes/v1/identity-router.ts index 7441d58cb..adf5bca30 100644 --- a/backend/src/server/routes/v1/identity-router.ts +++ b/backend/src/server/routes/v1/identity-router.ts @@ -2,8 +2,10 @@ import { z } from "zod"; import { IdentitiesSchema, OrgMembershipRole } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; +import { getTelemetryDistinctId } from "@app/server/lib/telemtry"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; +import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types"; export const registerIdentityRouter = async (server: FastifyZodProvider) => { server.route({ @@ -49,6 +51,17 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => { } }); + server.services.telemetry.sendPostHogEvents({ + event: PostHogEventTypes.MachineIdentityCreated, + distinctId: getTelemetryDistinctId(req), + properties: { + orgId: req.body.organizationId, + name: identity.name, + identityId: identity.id, + ...req.auditLogInfo + } + }); + return { identity }; } }); diff --git a/backend/src/server/routes/v1/integration-router.ts b/backend/src/server/routes/v1/integration-router.ts index ab0ba36eb..23d4f41eb 100644 --- a/backend/src/server/routes/v1/integration-router.ts +++ b/backend/src/server/routes/v1/integration-router.ts @@ -3,8 +3,10 @@ import { z } from "zod"; import { IntegrationsSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { removeTrailingSlash, shake } from "@app/lib/fn"; +import { getTelemetryDistinctId } from "@app/server/lib/telemtry"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; +import { PostHogEventTypes, TIntegrationCreatedEvent } from "@app/services/telemetry/telemetry-types"; export const registerIntegrationRouter = async (server: FastifyZodProvider) => { server.route({ @@ -53,28 +55,40 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { actorOrgId: req.permission.orgId, ...req.body }); + + const createIntegrationEventProperty = shake({ + integrationId: integration.id.toString(), + integration: integration.integration, + environment: req.body.sourceEnvironment, + secretPath: req.body.secretPath, + url: integration.url, + app: integration.app, + appId: integration.appId, + targetEnvironment: integration.targetEnvironment, + targetEnvironmentId: integration.targetEnvironmentId, + targetService: integration.targetService, + targetServiceId: integration.targetServiceId, + path: integration.path, + region: integration.region + }) as TIntegrationCreatedEvent["properties"]; + await server.services.auditLog.createAuditLog({ ...req.auditLogInfo, projectId: integrationAuth.projectId, event: { type: EventType.CREATE_INTEGRATION, // eslint-disable-next-line - metadata: shake({ - integrationId: integration.id.toString(), - integration: integration.integration, - environment: req.body.sourceEnvironment, - secretPath: req.body.secretPath, - url: integration.url, - app: integration.app, - appId: integration.appId, - targetEnvironment: integration.targetEnvironment, - targetEnvironmentId: integration.targetEnvironmentId, - targetService: integration.targetService, - targetServiceId: integration.targetServiceId, - path: integration.path, - region: integration.region - // eslint-disable-next-line - }) as any + metadata: createIntegrationEventProperty + } + }); + + server.services.telemetry.sendPostHogEvents({ + event: PostHogEventTypes.IntegrationCreated, + distinctId: getTelemetryDistinctId(req), + properties: { + ...createIntegrationEventProperty, + projectId: integrationAuth.projectId, + ...req.auditLogInfo } }); return { integration }; diff --git a/backend/src/server/routes/v1/invite-org-router.ts b/backend/src/server/routes/v1/invite-org-router.ts index 0d1fe5070..0184c0ea3 100644 --- a/backend/src/server/routes/v1/invite-org-router.ts +++ b/backend/src/server/routes/v1/invite-org-router.ts @@ -1,8 +1,10 @@ import { z } from "zod"; import { UsersSchema } from "@app/db/schemas"; +import { getTelemetryDistinctId } from "@app/server/lib/telemtry"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { ActorType, AuthMode } from "@app/services/auth/auth-type"; +import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types"; export const registerInviteOrgRouter = async (server: FastifyZodProvider) => { server.route({ @@ -30,6 +32,15 @@ export const registerInviteOrgRouter = async (server: FastifyZodProvider) => { actorOrgId: req.permission.orgId }); + server.services.telemetry.sendPostHogEvents({ + event: PostHogEventTypes.UserOrgInvitation, + distinctId: getTelemetryDistinctId(req), + properties: { + inviteeEmail: req.body.inviteeEmail, + ...req.auditLogInfo + } + }); + return { completeInviteLink, message: `Send an invite link to ${req.body.inviteeEmail}` diff --git a/backend/src/server/routes/v2/project-router.ts b/backend/src/server/routes/v2/project-router.ts index 7cbcff37c..a8ed57a52 100644 --- a/backend/src/server/routes/v2/project-router.ts +++ b/backend/src/server/routes/v2/project-router.ts @@ -4,8 +4,10 @@ import { z } from "zod"; import { ProjectKeysSchema, ProjectsSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { authRateLimit } from "@app/server/config/rateLimiter"; +import { getTelemetryDistinctId } from "@app/server/lib/telemtry"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; +import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types"; const projectWithEnv = ProjectsSchema.merge( z.object({ @@ -152,6 +154,16 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { slug: req.body.slug }); + server.services.telemetry.sendPostHogEvents({ + event: PostHogEventTypes.ProjectCreated, + distinctId: getTelemetryDistinctId(req), + properties: { + orgId: req.body.organizationId, + name: project.name, + ...req.auditLogInfo + } + }); + return { project }; } }); diff --git a/backend/src/services/telemetry/telemetry-service.ts b/backend/src/services/telemetry/telemetry-service.ts index c386abd95..a1a4b78f7 100644 --- a/backend/src/services/telemetry/telemetry-service.ts +++ b/backend/src/services/telemetry/telemetry-service.ts @@ -61,8 +61,15 @@ To opt into telemetry, you can set "TELEMETRY_ENABLED=true" within the environme } }; + const flushAll = async () => { + if (postHog) { + await postHog.shutdownAsync(); + } + }; + return { sendLoopsEvent, - sendPostHogEvents + sendPostHogEvents, + flushAll }; }; diff --git a/backend/src/services/telemetry/telemetry-types.ts b/backend/src/services/telemetry/telemetry-types.ts index d97c3c7ac..ada71e6ef 100644 --- a/backend/src/services/telemetry/telemetry-types.ts +++ b/backend/src/services/telemetry/telemetry-types.ts @@ -8,7 +8,11 @@ export enum PostHogEventTypes { UserSignedUp = "User Signed Up", SecretRotated = "secrets rotated", SecretScannerFull = "historical cloud secret scan", - SecretScannerPush = "cloud secret scan" + SecretScannerPush = "cloud secret scan", + ProjectCreated = "project created", + IntegrationCreated = "integration created", + MachineIdentityCreated = "machine identity created", + UserOrgInvitation = "user org invitation" } export type TSecretModifiedEvent = { @@ -53,9 +57,57 @@ export type TSecretScannerEvent = { }; }; +export type TProjectCreateEvent = { + event: PostHogEventTypes.ProjectCreated; + properties: { + name: string; + orgId: string; + }; +}; + +export type TMachineIdentityCreatedEvent = { + event: PostHogEventTypes.MachineIdentityCreated; + properties: { + name: string; + orgId: string; + identityId: string; + }; +}; + +export type TIntegrationCreatedEvent = { + event: PostHogEventTypes.IntegrationCreated; + properties: { + projectId: string; + integrationId: string; + integration: string; // TODO: fix type + environment: string; + secretPath: string; + url?: string; + app?: string; + appId?: string; + targetEnvironment?: string; + targetEnvironmentId?: string; + targetService?: string; + targetServiceId?: string; + path?: string; + region?: string; + }; +}; + +export type TUserOrgInvitedEvent = { + event: PostHogEventTypes.UserOrgInvitation; + properties: { + inviteeEmail: string; + }; +}; + export type TPostHogEvent = { distinctId: string } & ( | TSecretModifiedEvent | TAdminInitEvent | TUserSignedUpEvent | TSecretScannerEvent + | TUserOrgInvitedEvent + | TMachineIdentityCreatedEvent + | TIntegrationCreatedEvent + | TProjectCreateEvent ); From 92acb4d9435924181477b2884bdf922671079b58 Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Fri, 23 Feb 2024 00:07:51 +0530 Subject: [PATCH 2/3] feat(telemetry): fixed typo in filename lib/telemetry and capitalized new posthog event names --- backend/src/server/lib/{telemtry.ts => telemetry.ts} | 0 backend/src/server/routes/v1/identity-router.ts | 2 +- backend/src/server/routes/v1/integration-router.ts | 2 +- backend/src/server/routes/v1/invite-org-router.ts | 2 +- backend/src/server/routes/v2/project-router.ts | 2 +- backend/src/services/telemetry/telemetry-types.ts | 8 ++++---- 6 files changed, 8 insertions(+), 8 deletions(-) rename backend/src/server/lib/{telemtry.ts => telemetry.ts} (100%) diff --git a/backend/src/server/lib/telemtry.ts b/backend/src/server/lib/telemetry.ts similarity index 100% rename from backend/src/server/lib/telemtry.ts rename to backend/src/server/lib/telemetry.ts diff --git a/backend/src/server/routes/v1/identity-router.ts b/backend/src/server/routes/v1/identity-router.ts index adf5bca30..a03cde62f 100644 --- a/backend/src/server/routes/v1/identity-router.ts +++ b/backend/src/server/routes/v1/identity-router.ts @@ -2,7 +2,7 @@ import { z } from "zod"; import { IdentitiesSchema, OrgMembershipRole } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; -import { getTelemetryDistinctId } from "@app/server/lib/telemtry"; +import { getTelemetryDistinctId } from "@app/server/lib/telemetry"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types"; diff --git a/backend/src/server/routes/v1/integration-router.ts b/backend/src/server/routes/v1/integration-router.ts index 23d4f41eb..1ccf75e94 100644 --- a/backend/src/server/routes/v1/integration-router.ts +++ b/backend/src/server/routes/v1/integration-router.ts @@ -3,7 +3,7 @@ import { z } from "zod"; import { IntegrationsSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { removeTrailingSlash, shake } from "@app/lib/fn"; -import { getTelemetryDistinctId } from "@app/server/lib/telemtry"; +import { getTelemetryDistinctId } from "@app/server/lib/telemetry"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; import { PostHogEventTypes, TIntegrationCreatedEvent } from "@app/services/telemetry/telemetry-types"; diff --git a/backend/src/server/routes/v1/invite-org-router.ts b/backend/src/server/routes/v1/invite-org-router.ts index 0184c0ea3..dd2fd8157 100644 --- a/backend/src/server/routes/v1/invite-org-router.ts +++ b/backend/src/server/routes/v1/invite-org-router.ts @@ -1,7 +1,7 @@ import { z } from "zod"; import { UsersSchema } from "@app/db/schemas"; -import { getTelemetryDistinctId } from "@app/server/lib/telemtry"; +import { getTelemetryDistinctId } from "@app/server/lib/telemetry"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { ActorType, AuthMode } from "@app/services/auth/auth-type"; import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types"; diff --git a/backend/src/server/routes/v2/project-router.ts b/backend/src/server/routes/v2/project-router.ts index a8ed57a52..62f4c7cf5 100644 --- a/backend/src/server/routes/v2/project-router.ts +++ b/backend/src/server/routes/v2/project-router.ts @@ -4,7 +4,7 @@ import { z } from "zod"; import { ProjectKeysSchema, ProjectsSchema } from "@app/db/schemas"; import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { authRateLimit } from "@app/server/config/rateLimiter"; -import { getTelemetryDistinctId } from "@app/server/lib/telemtry"; +import { getTelemetryDistinctId } from "@app/server/lib/telemetry"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types"; diff --git a/backend/src/services/telemetry/telemetry-types.ts b/backend/src/services/telemetry/telemetry-types.ts index ada71e6ef..0f92da4da 100644 --- a/backend/src/services/telemetry/telemetry-types.ts +++ b/backend/src/services/telemetry/telemetry-types.ts @@ -9,10 +9,10 @@ export enum PostHogEventTypes { SecretRotated = "secrets rotated", SecretScannerFull = "historical cloud secret scan", SecretScannerPush = "cloud secret scan", - ProjectCreated = "project created", - IntegrationCreated = "integration created", - MachineIdentityCreated = "machine identity created", - UserOrgInvitation = "user org invitation" + ProjectCreated = "Project Created", + IntegrationCreated = "Integration Created", + MachineIdentityCreated = "Machine Identity Created", + UserOrgInvitation = "User Org Invitation" } export type TSecretModifiedEvent = { From e91499b30101200271be28c5e980b9bfea4bbb2b Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Fri, 23 Feb 2024 13:22:53 +0530 Subject: [PATCH 3/3] feat(telemetry): updated telemetry distinct id to use service token from secrets one --- backend/src/server/lib/telemetry.ts | 2 +- backend/src/server/routes/v3/secret-router.ts | 41 +++++++------------ 2 files changed, 15 insertions(+), 28 deletions(-) diff --git a/backend/src/server/lib/telemetry.ts b/backend/src/server/lib/telemetry.ts index 6794d7bb3..5169b0e97 100644 --- a/backend/src/server/lib/telemetry.ts +++ b/backend/src/server/lib/telemetry.ts @@ -11,7 +11,7 @@ export const getTelemetryDistinctId = (req: FastifyRequest) => { return `identity-${req.auth.identityId}`; } if (req.auth.actor === ActorType.SERVICE) { - return `service-token-${req.auth.serviceToken.id}`; + return req.auth.serviceToken.createdByEmail || `service-token-null-creator-${req.auth.serviceTokenId}`; // when user gets removed from system } return "unknown-auth-data"; }; diff --git a/backend/src/server/routes/v3/secret-router.ts b/backend/src/server/routes/v3/secret-router.ts index 6bbb437ad..cfe4a87d7 100644 --- a/backend/src/server/routes/v3/secret-router.ts +++ b/backend/src/server/routes/v3/secret-router.ts @@ -1,4 +1,3 @@ -import { FastifyRequest } from "fastify"; import picomatch from "picomatch"; import { z } from "zod"; @@ -13,6 +12,7 @@ import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { CommitType } from "@app/ee/services/secret-approval-request/secret-approval-request-types"; import { BadRequestError } from "@app/lib/errors"; import { removeTrailingSlash } from "@app/lib/fn"; +import { getTelemetryDistinctId } from "@app/server/lib/telemetry"; import { getUserAgentType } from "@app/server/plugins/audit-log"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { ActorType, AuthMode } from "@app/services/auth/auth-type"; @@ -20,19 +20,6 @@ import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types"; import { secretRawSchema } from "../sanitizedSchemas"; -const getDistinctId = (req: FastifyRequest) => { - if (req.auth.actor === ActorType.USER) { - return req.auth.user.email; - } - if (req.auth.actor === ActorType.IDENTITY) { - return `identity-${req.auth.identityId}`; - } - if (req.auth.actor === ActorType.SERVICE) { - return req.auth.serviceToken.createdByEmail || `service-token-null-creator-${req.auth.serviceTokenId}`; // when user gets removed from system - } - return "unknown-auth-data"; -}; - export const registerSecretRouter = async (server: FastifyZodProvider) => { server.route({ url: "/raw", @@ -110,7 +97,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { server.services.telemetry.sendPostHogEvents({ event: PostHogEventTypes.SecretPulled, - distinctId: getDistinctId(req), + distinctId: getTelemetryDistinctId(req), properties: { numberOfSecrets: secrets.length, workspaceId, @@ -200,7 +187,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { server.services.telemetry.sendPostHogEvents({ event: PostHogEventTypes.SecretPulled, - distinctId: getDistinctId(req), + distinctId: getTelemetryDistinctId(req), properties: { numberOfSecrets: 1, workspaceId, @@ -276,7 +263,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { server.services.telemetry.sendPostHogEvents({ event: PostHogEventTypes.SecretCreated, - distinctId: getDistinctId(req), + distinctId: getTelemetryDistinctId(req), properties: { numberOfSecrets: 1, workspaceId: req.body.workspaceId, @@ -351,7 +338,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { server.services.telemetry.sendPostHogEvents({ event: PostHogEventTypes.SecretUpdated, - distinctId: getDistinctId(req), + distinctId: getTelemetryDistinctId(req), properties: { numberOfSecrets: 1, workspaceId: req.body.workspaceId, @@ -421,7 +408,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { server.services.telemetry.sendPostHogEvents({ event: PostHogEventTypes.SecretDeleted, - distinctId: getDistinctId(req), + distinctId: getTelemetryDistinctId(req), properties: { numberOfSecrets: 1, workspaceId: req.body.workspaceId, @@ -527,7 +514,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { if (shouldCapture) { server.services.telemetry.sendPostHogEvents({ event: PostHogEventTypes.SecretPulled, - distinctId: getDistinctId(req), + distinctId: getTelemetryDistinctId(req), properties: { numberOfSecrets: shouldRecordK8Event ? approximateNumberTotalSecrets : secrets.length, workspaceId: req.query.workspaceId, @@ -604,7 +591,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { server.services.telemetry.sendPostHogEvents({ event: PostHogEventTypes.SecretPulled, - distinctId: getDistinctId(req), + distinctId: getTelemetryDistinctId(req), properties: { numberOfSecrets: 1, workspaceId: req.query.workspaceId, @@ -767,7 +754,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { server.services.telemetry.sendPostHogEvents({ event: PostHogEventTypes.SecretCreated, - distinctId: getDistinctId(req), + distinctId: getTelemetryDistinctId(req), properties: { numberOfSecrets: 1, workspaceId: req.body.workspaceId, @@ -949,7 +936,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { server.services.telemetry.sendPostHogEvents({ event: PostHogEventTypes.SecretUpdated, - distinctId: getDistinctId(req), + distinctId: getTelemetryDistinctId(req), properties: { numberOfSecrets: 1, workspaceId: req.body.workspaceId, @@ -1067,7 +1054,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { server.services.telemetry.sendPostHogEvents({ event: PostHogEventTypes.SecretDeleted, - distinctId: getDistinctId(req), + distinctId: getTelemetryDistinctId(req), properties: { numberOfSecrets: 1, workspaceId: req.body.workspaceId, @@ -1187,7 +1174,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { server.services.telemetry.sendPostHogEvents({ event: PostHogEventTypes.SecretCreated, - distinctId: getDistinctId(req), + distinctId: getTelemetryDistinctId(req), properties: { numberOfSecrets: secrets.length, workspaceId: req.body.workspaceId, @@ -1307,7 +1294,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { server.services.telemetry.sendPostHogEvents({ event: PostHogEventTypes.SecretUpdated, - distinctId: getDistinctId(req), + distinctId: getTelemetryDistinctId(req), properties: { numberOfSecrets: secrets.length, workspaceId: req.body.workspaceId, @@ -1415,7 +1402,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => { server.services.telemetry.sendPostHogEvents({ event: PostHogEventTypes.SecretDeleted, - distinctId: getDistinctId(req), + distinctId: getTelemetryDistinctId(req), properties: { numberOfSecrets: secrets.length, workspaceId: req.body.workspaceId,