From de553c27431139ebb026ea110694efb7d83565dc Mon Sep 17 00:00:00 2001 From: Greg Methvin Date: Tue, 14 Apr 2026 15:47:49 -0700 Subject: [PATCH] Require date range for campaign metrics query --- src/client/campaigns.ts | 6 ++-- src/types/campaigns.ts | 8 +++--- tests/integration/campaigns.test.ts | 2 ++ tests/unit/campaigns.test.ts | 43 +++++++++++++++++++++++++---- 4 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/client/campaigns.ts b/src/client/campaigns.ts index b59fc8f..8309991 100644 --- a/src/client/campaigns.ts +++ b/src/client/campaigns.ts @@ -134,10 +134,8 @@ export function Campaigns>(Base: T) { ): Promise { const params = new URLSearchParams(); params.append("campaignId", options.campaignId.toString()); - if (options.startDateTime) - params.append("startDateTime", options.startDateTime); - if (options.endDateTime) - params.append("endDateTime", options.endDateTime); + params.append("startDateTime", options.startDateTime); + params.append("endDateTime", options.endDateTime); const response = await this.client.get( `/api/campaigns/metrics?${params.toString()}`, diff --git a/src/types/campaigns.ts b/src/types/campaigns.ts index 122a508..75e0ab1 100644 --- a/src/types/campaigns.ts +++ b/src/types/campaigns.ts @@ -134,11 +134,11 @@ export type GetChildCampaignsParams = z.infer< export const GetCampaignMetricsParamsSchema = z.object({ campaignId: z.number().describe("Campaign ID to get metrics for"), - startDateTime: IterableDateTimeSchema.optional().describe( - "Start date for metrics (YYYY-MM-DD HH:MM:SS format)" + startDateTime: IterableDateTimeSchema.describe( + "Start of the metrics date range (YYYY-MM-DD HH:MM:SS format). Always use the narrowest window possible for performance." ), - endDateTime: IterableDateTimeSchema.optional().describe( - "End date for metrics (YYYY-MM-DD HH:MM:SS format)" + endDateTime: IterableDateTimeSchema.describe( + "End of the metrics date range (YYYY-MM-DD HH:MM:SS format). Always use the narrowest window possible for performance." ), }); diff --git a/tests/integration/campaigns.test.ts b/tests/integration/campaigns.test.ts index e914106..15250c9 100644 --- a/tests/integration/campaigns.test.ts +++ b/tests/integration/campaigns.test.ts @@ -293,6 +293,8 @@ describe("Campaign Management Integration Tests", () => { withTimeout( client.getCampaignMetrics({ campaignId: campaign.id, + startDateTime: new Date(campaign.createdAt).toISOString(), + endDateTime: new Date().toISOString(), }) ), "Get campaign metrics" diff --git a/tests/unit/campaigns.test.ts b/tests/unit/campaigns.test.ts index 130f66f..89ef388 100644 --- a/tests/unit/campaigns.test.ts +++ b/tests/unit/campaigns.test.ts @@ -239,13 +239,17 @@ describe("Campaign Management", () => { it("should parse CSV metrics correctly", async () => { const mockCsvData = "id,sent,delivered\n12345,1000,950\n12346,500,475"; const mockResponse = { data: mockCsvData }; - const options = { campaignId: 12345, startDateTime: "2023-01-01" }; + const options = { + campaignId: 12345, + startDateTime: "2023-01-01", + endDateTime: "2023-12-31", + }; mockAxiosInstance.get.mockResolvedValue(mockResponse); const result = await client.getCampaignMetrics(options); expect(mockAxiosInstance.get).toHaveBeenCalledWith( - "/api/campaigns/metrics?campaignId=12345&startDateTime=2023-01-01", + "/api/campaigns/metrics?campaignId=12345&startDateTime=2023-01-01&endDateTime=2023-12-31", { responseType: "text" } ); expect(result).toHaveLength(2); @@ -261,7 +265,11 @@ describe("Campaign Management", () => { const mockResponse = { data: "" }; mockAxiosInstance.get.mockResolvedValue(mockResponse); - const result = await client.getCampaignMetrics({ campaignId: 12345 }); + const result = await client.getCampaignMetrics({ + campaignId: 12345, + startDateTime: "2023-01-01", + endDateTime: "2023-12-31", + }); expect(result).toEqual([]); }); @@ -270,7 +278,11 @@ describe("Campaign Management", () => { const mockResponse = { data: "id,sent,delivered" }; mockAxiosInstance.get.mockResolvedValue(mockResponse); - const result = await client.getCampaignMetrics({ campaignId: 12345 }); + const result = await client.getCampaignMetrics({ + campaignId: 12345, + startDateTime: "2023-01-01", + endDateTime: "2023-12-31", + }); expect(result).toEqual([]); }); @@ -296,7 +308,11 @@ describe("Campaign Management", () => { mockAxiosInstance.get.mockResolvedValue(mockResponse); await expect( - client.getCampaignMetrics({ campaignId: 12345 }) + client.getCampaignMetrics({ + campaignId: 12345, + startDateTime: "2023-01-01", + endDateTime: "2023-12-31", + }) ).rejects.toThrow("CSV parse error"); }); }); @@ -518,6 +534,23 @@ describe("Campaign Management", () => { expect(() => GetCampaignMetricsParamsSchema.parse({ startDateTime: new Date("2023-01-01T00:00:00Z"), + endDateTime: new Date("2023-12-31T23:59:59Z"), + }) + ).toThrow(); + + // Missing required startDateTime + expect(() => + GetCampaignMetricsParamsSchema.parse({ + campaignId: 12345, + endDateTime: new Date("2023-12-31T23:59:59Z"), + }) + ).toThrow(); + + // Missing required endDateTime + expect(() => + GetCampaignMetricsParamsSchema.parse({ + campaignId: 12345, + startDateTime: new Date("2023-01-01T00:00:00Z"), }) ).toThrow(); });