mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
parent
170fde848a
commit
1801af26b7
2 changed files with 0 additions and 338 deletions
|
|
@ -1,87 +0,0 @@
|
|||
import { TelemetryEventName, type TelemetryEvent, rooCodeTelemetryEventSchema } from "@roo-code/types"
|
||||
import { BaseTelemetryClient } from "@roo-code/telemetry"
|
||||
|
||||
import { getRooCodeApiUrl } from "./Config"
|
||||
import { AuthService } from "./AuthService"
|
||||
|
||||
export class RooCodeTelemetryClient extends BaseTelemetryClient {
|
||||
constructor(
|
||||
private authService: AuthService,
|
||||
debug = false,
|
||||
) {
|
||||
super(
|
||||
{
|
||||
type: "exclude",
|
||||
events: [TelemetryEventName.TASK_CONVERSATION_MESSAGE],
|
||||
},
|
||||
debug,
|
||||
)
|
||||
}
|
||||
|
||||
private async fetch(path: string, options: RequestInit) {
|
||||
if (!this.authService.isAuthenticated()) {
|
||||
return
|
||||
}
|
||||
|
||||
const token = this.authService.getSessionToken()
|
||||
|
||||
if (!token) {
|
||||
console.error(`[RooCodeTelemetryClient#fetch] Unauthorized: No session token available.`)
|
||||
return
|
||||
}
|
||||
|
||||
const response = await fetch(`${getRooCodeApiUrl()}/api/${path}`, {
|
||||
...options,
|
||||
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
console.error(
|
||||
`[RooCodeTelemetryClient#fetch] ${options.method} ${path} -> ${response.status} ${response.statusText}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public override async capture(event: TelemetryEvent) {
|
||||
if (!this.isTelemetryEnabled() || !this.isEventCapturable(event.event)) {
|
||||
if (this.debug) {
|
||||
console.info(`[RooCodeTelemetryClient#capture] Skipping event: ${event.event}`)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const payload = {
|
||||
type: event.event,
|
||||
properties: await this.getEventProperties(event),
|
||||
}
|
||||
|
||||
if (this.debug) {
|
||||
console.info(`[RooCodeTelemetryClient#capture] ${JSON.stringify(payload)}`)
|
||||
}
|
||||
|
||||
const result = rooCodeTelemetryEventSchema.safeParse(payload)
|
||||
|
||||
if (!result.success) {
|
||||
console.error(
|
||||
`[RooCodeTelemetryClient#capture] Invalid telemetry event: ${result.error.message} - ${JSON.stringify(payload)}`,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await this.fetch(`events`, { method: "POST", body: JSON.stringify(result.data) })
|
||||
} catch (error) {
|
||||
console.error(`[RooCodeTelemetryClient#capture] Error sending telemetry event: ${error}`)
|
||||
}
|
||||
}
|
||||
|
||||
public override updateTelemetryState(_didUserOptIn: boolean) {}
|
||||
|
||||
public override isTelemetryEnabled(): boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
public override async shutdown() {}
|
||||
}
|
||||
|
|
@ -1,251 +0,0 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
// npx vitest run src/__tests__/RooCodeTelemetryClient.test.ts
|
||||
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"
|
||||
|
||||
import { type TelemetryPropertiesProvider, TelemetryEventName } from "@roo-code/types"
|
||||
|
||||
import { RooCodeTelemetryClient } from "../RooCodeTelemetryClient"
|
||||
|
||||
const mockFetch = vi.fn()
|
||||
global.fetch = mockFetch as any
|
||||
|
||||
describe("RooCodeTelemetryClient", () => {
|
||||
const getPrivateProperty = <T>(instance: any, propertyName: string): T => {
|
||||
return instance[propertyName]
|
||||
}
|
||||
|
||||
let mockAuthService: any
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
|
||||
// Create a mock AuthService instead of using the singleton
|
||||
mockAuthService = {
|
||||
getSessionToken: vi.fn().mockReturnValue("mock-token"),
|
||||
getState: vi.fn().mockReturnValue("active-session"),
|
||||
isAuthenticated: vi.fn().mockReturnValue(true),
|
||||
hasActiveSession: vi.fn().mockReturnValue(true),
|
||||
}
|
||||
|
||||
mockFetch.mockResolvedValue({
|
||||
ok: true,
|
||||
json: vi.fn().mockResolvedValue({}),
|
||||
})
|
||||
|
||||
vi.spyOn(console, "info").mockImplementation(() => {})
|
||||
vi.spyOn(console, "error").mockImplementation(() => {})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
describe("isEventCapturable", () => {
|
||||
it("should return true for events not in exclude list", () => {
|
||||
const client = new RooCodeTelemetryClient(mockAuthService)
|
||||
|
||||
const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>(
|
||||
client,
|
||||
"isEventCapturable",
|
||||
).bind(client)
|
||||
|
||||
expect(isEventCapturable(TelemetryEventName.TASK_CREATED)).toBe(true)
|
||||
expect(isEventCapturable(TelemetryEventName.LLM_COMPLETION)).toBe(true)
|
||||
expect(isEventCapturable(TelemetryEventName.MODE_SWITCH)).toBe(true)
|
||||
expect(isEventCapturable(TelemetryEventName.TOOL_USED)).toBe(true)
|
||||
})
|
||||
|
||||
it("should return false for events in exclude list", () => {
|
||||
const client = new RooCodeTelemetryClient(mockAuthService)
|
||||
|
||||
const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>(
|
||||
client,
|
||||
"isEventCapturable",
|
||||
).bind(client)
|
||||
|
||||
expect(isEventCapturable(TelemetryEventName.TASK_CONVERSATION_MESSAGE)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("getEventProperties", () => {
|
||||
it("should merge provider properties with event properties", async () => {
|
||||
const client = new RooCodeTelemetryClient(mockAuthService)
|
||||
|
||||
const mockProvider: TelemetryPropertiesProvider = {
|
||||
getTelemetryProperties: vi.fn().mockResolvedValue({
|
||||
appVersion: "1.0.0",
|
||||
vscodeVersion: "1.60.0",
|
||||
platform: "darwin",
|
||||
editorName: "vscode",
|
||||
language: "en",
|
||||
mode: "code",
|
||||
}),
|
||||
}
|
||||
|
||||
client.setProvider(mockProvider)
|
||||
|
||||
const getEventProperties = getPrivateProperty<
|
||||
(event: { event: TelemetryEventName; properties?: Record<string, any> }) => Promise<Record<string, any>>
|
||||
>(client, "getEventProperties").bind(client)
|
||||
|
||||
const result = await getEventProperties({
|
||||
event: TelemetryEventName.TASK_CREATED,
|
||||
properties: {
|
||||
customProp: "value",
|
||||
mode: "override", // This should override the provider's mode.
|
||||
},
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
appVersion: "1.0.0",
|
||||
vscodeVersion: "1.60.0",
|
||||
platform: "darwin",
|
||||
editorName: "vscode",
|
||||
language: "en",
|
||||
mode: "override", // Event property takes precedence.
|
||||
customProp: "value",
|
||||
})
|
||||
|
||||
expect(mockProvider.getTelemetryProperties).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it("should handle errors from provider gracefully", async () => {
|
||||
const client = new RooCodeTelemetryClient(mockAuthService)
|
||||
|
||||
const mockProvider: TelemetryPropertiesProvider = {
|
||||
getTelemetryProperties: vi.fn().mockRejectedValue(new Error("Provider error")),
|
||||
}
|
||||
|
||||
const consoleErrorSpy = vi.spyOn(console, "error")
|
||||
|
||||
client.setProvider(mockProvider)
|
||||
|
||||
const getEventProperties = getPrivateProperty<
|
||||
(event: { event: TelemetryEventName; properties?: Record<string, any> }) => Promise<Record<string, any>>
|
||||
>(client, "getEventProperties").bind(client)
|
||||
|
||||
const result = await getEventProperties({
|
||||
event: TelemetryEventName.TASK_CREATED,
|
||||
properties: { customProp: "value" },
|
||||
})
|
||||
|
||||
expect(result).toEqual({ customProp: "value" })
|
||||
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Error getting telemetry properties: Provider error"),
|
||||
)
|
||||
})
|
||||
|
||||
it("should return event properties when no provider is set", async () => {
|
||||
const client = new RooCodeTelemetryClient(mockAuthService)
|
||||
|
||||
const getEventProperties = getPrivateProperty<
|
||||
(event: { event: TelemetryEventName; properties?: Record<string, any> }) => Promise<Record<string, any>>
|
||||
>(client, "getEventProperties").bind(client)
|
||||
|
||||
const result = await getEventProperties({
|
||||
event: TelemetryEventName.TASK_CREATED,
|
||||
properties: { customProp: "value" },
|
||||
})
|
||||
|
||||
expect(result).toEqual({ customProp: "value" })
|
||||
})
|
||||
})
|
||||
|
||||
describe("capture", () => {
|
||||
it("should not capture events that are not capturable", async () => {
|
||||
const client = new RooCodeTelemetryClient(mockAuthService)
|
||||
|
||||
await client.capture({
|
||||
event: TelemetryEventName.TASK_CONVERSATION_MESSAGE, // In exclude list.
|
||||
properties: { test: "value" },
|
||||
})
|
||||
|
||||
expect(mockFetch).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should not send request when schema validation fails", async () => {
|
||||
const client = new RooCodeTelemetryClient(mockAuthService)
|
||||
|
||||
await client.capture({
|
||||
event: TelemetryEventName.TASK_CREATED,
|
||||
properties: { test: "value" },
|
||||
})
|
||||
|
||||
expect(mockFetch).not.toHaveBeenCalled()
|
||||
expect(console.error).toHaveBeenCalledWith(expect.stringContaining("Invalid telemetry event"))
|
||||
})
|
||||
|
||||
it("should send request when event is capturable and validation passes", async () => {
|
||||
const client = new RooCodeTelemetryClient(mockAuthService)
|
||||
|
||||
const providerProperties = {
|
||||
appName: "roo-code",
|
||||
appVersion: "1.0.0",
|
||||
vscodeVersion: "1.60.0",
|
||||
platform: "darwin",
|
||||
editorName: "vscode",
|
||||
language: "en",
|
||||
mode: "code",
|
||||
}
|
||||
|
||||
const eventProperties = {
|
||||
taskId: "test-task-id",
|
||||
}
|
||||
|
||||
const mockValidatedData = {
|
||||
type: TelemetryEventName.TASK_CREATED,
|
||||
properties: {
|
||||
...providerProperties,
|
||||
taskId: "test-task-id",
|
||||
},
|
||||
}
|
||||
|
||||
const mockProvider: TelemetryPropertiesProvider = {
|
||||
getTelemetryProperties: vi.fn().mockResolvedValue(providerProperties),
|
||||
}
|
||||
|
||||
client.setProvider(mockProvider)
|
||||
|
||||
await client.capture({
|
||||
event: TelemetryEventName.TASK_CREATED,
|
||||
properties: eventProperties,
|
||||
})
|
||||
|
||||
expect(mockFetch).toHaveBeenCalledWith(
|
||||
"https://app.roocode.com/api/events",
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
body: JSON.stringify(mockValidatedData),
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("should handle fetch errors gracefully", async () => {
|
||||
const client = new RooCodeTelemetryClient(mockAuthService)
|
||||
|
||||
mockFetch.mockRejectedValue(new Error("Network error"))
|
||||
|
||||
await expect(
|
||||
client.capture({
|
||||
event: TelemetryEventName.TASK_CREATED,
|
||||
properties: { test: "value" },
|
||||
}),
|
||||
).resolves.not.toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe("telemetry state methods", () => {
|
||||
it("should always return true for isTelemetryEnabled", () => {
|
||||
const client = new RooCodeTelemetryClient(mockAuthService)
|
||||
expect(client.isTelemetryEnabled()).toBe(true)
|
||||
})
|
||||
|
||||
it("should have empty implementations for updateTelemetryState and shutdown", async () => {
|
||||
const client = new RooCodeTelemetryClient(mockAuthService)
|
||||
client.updateTelemetryState(true)
|
||||
await client.shutdown()
|
||||
})
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Reference in a new issue