From 667d7180473dcf9d0346e187a5a236ea62a766ee Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Wed, 9 Sep 2026 23:11:55 -0700 Subject: [PATCH] Route legacy report APIs through compatibility handlers --- docs/report-api-migration.md | 14 +- next.config.ts | 9 + packages/api-client/MIGRATION.md | 2 +- packages/api-client/src/client.test.ts | 15 +- packages/api-client/src/client.ts | 18 - .../api-client/src/generated/operations.ts | 368 --- packages/api-client/src/generated/types.ts | 1964 ----------------- packages/api-client/src/http.test.ts | 19 +- .../compat}/api/reports/[reportId]/route.ts | 0 .../compat}/api/reports/attribution/route.ts | 0 .../compat}/api/reports/breakdown/route.ts | 0 .../compat}/api/reports/funnel/route.ts | 0 .../compat}/api/reports/goal/route.ts | 0 .../compat}/api/reports/heatmap/route.ts | 0 .../compat}/api/reports/journey/route.ts | 0 .../compat}/api/reports/performance/route.ts | 0 .../compat}/api/reports/retention/route.ts | 0 .../compat}/api/reports/revenue/route.ts | 0 .../compat}/api/reports/route.ts | 0 .../compat}/api/reports/utm/route.ts | 0 .../api/websites/[websiteId]/reports/route.ts | 0 .../reports/[reportId]/contract.generated.ts | 453 ---- .../reports/attribution/contract.generated.ts | 224 -- .../reports/breakdown/contract.generated.ts | 123 -- src/app/api/reports/contract.generated.ts | 439 ---- .../api/reports/funnel/contract.generated.ts | 145 -- .../api/reports/goal/contract.generated.ts | 101 - .../api/reports/heatmap/contract.generated.ts | 251 --- .../api/reports/journey/contract.generated.ts | 122 - .../reports/performance/contract.generated.ts | 101 - .../reports/retention/contract.generated.ts | 113 - .../api/reports/revenue/contract.generated.ts | 180 -- src/app/api/reports/utm/contract.generated.ts | 115 - .../[websiteId]/reports/contract.generated.ts | 254 --- src/openapi/compatibility-routing.test.ts | 57 + src/openapi/document.ts | 8 - src/openapi/operation-descriptions.ts | 77 - src/openapi/operation-ids.ts | 16 - src/openapi/report-deprecations.test.ts | 34 - src/openapi/report-deprecations.ts | 27 - 40 files changed, 94 insertions(+), 5155 deletions(-) rename src/app/{ => (compat)/compat}/api/reports/[reportId]/route.ts (100%) rename src/app/{ => (compat)/compat}/api/reports/attribution/route.ts (100%) rename src/app/{ => (compat)/compat}/api/reports/breakdown/route.ts (100%) rename src/app/{ => (compat)/compat}/api/reports/funnel/route.ts (100%) rename src/app/{ => (compat)/compat}/api/reports/goal/route.ts (100%) rename src/app/{ => (compat)/compat}/api/reports/heatmap/route.ts (100%) rename src/app/{ => (compat)/compat}/api/reports/journey/route.ts (100%) rename src/app/{ => (compat)/compat}/api/reports/performance/route.ts (100%) rename src/app/{ => (compat)/compat}/api/reports/retention/route.ts (100%) rename src/app/{ => (compat)/compat}/api/reports/revenue/route.ts (100%) rename src/app/{ => (compat)/compat}/api/reports/route.ts (100%) rename src/app/{ => (compat)/compat}/api/reports/utm/route.ts (100%) rename src/app/{ => (compat)/compat}/api/websites/[websiteId]/reports/route.ts (100%) delete mode 100644 src/app/api/reports/[reportId]/contract.generated.ts delete mode 100644 src/app/api/reports/attribution/contract.generated.ts delete mode 100644 src/app/api/reports/breakdown/contract.generated.ts delete mode 100644 src/app/api/reports/contract.generated.ts delete mode 100644 src/app/api/reports/funnel/contract.generated.ts delete mode 100644 src/app/api/reports/goal/contract.generated.ts delete mode 100644 src/app/api/reports/heatmap/contract.generated.ts delete mode 100644 src/app/api/reports/journey/contract.generated.ts delete mode 100644 src/app/api/reports/performance/contract.generated.ts delete mode 100644 src/app/api/reports/retention/contract.generated.ts delete mode 100644 src/app/api/reports/revenue/contract.generated.ts delete mode 100644 src/app/api/reports/utm/contract.generated.ts delete mode 100644 src/app/api/websites/[websiteId]/reports/contract.generated.ts create mode 100644 src/openapi/compatibility-routing.test.ts delete mode 100644 src/openapi/report-deprecations.test.ts delete mode 100644 src/openapi/report-deprecations.ts diff --git a/docs/report-api-migration.md b/docs/report-api-migration.md index 6789a25ee..2005887d6 100644 --- a/docs/report-api-migration.md +++ b/docs/report-api-migration.md @@ -82,10 +82,15 @@ Section-based sharing is preserved. Heatmaps remain authenticated-only. New defi support type-specific section sharing and exclude deleted websites. Legacy report list routes retain their original, differing authentication behavior during the transition. -## Compatibility and removal gate +## Compatibility routing and removal gate All legacy calculation and saved-report routes remain callable with their existing contracts. -OpenAPI marks them deprecated; generated SDK methods carry `@deprecated` and remain available. +Next.js rewrites the old URLs to handlers under `src/app/(compat)/compat/api` without +changing request methods, bodies, query parameters, or response shapes. These handlers live +outside `src/app/api`, so contract discovery, OpenAPI, and the generated SDK expose only the +new feature APIs. Existing clients can continue calling the old URLs; new SDK versions use +the replacements above. HTTP redirects to the feature APIs would not preserve compatibility +because the methods, request envelopes, and some response shapes differ. Legacy handlers and new feature routes call the same SQL functions; no internal HTTP forwarding is involved. Legacy calculation envelopes and date precedence remain unchanged. @@ -99,14 +104,13 @@ Before scheduling removal: 5. Announce a compatibility window and the breaking release that will remove legacy routes. 6. Verify the application, boards, SDK examples, and MCP no longer call legacy endpoints. -No removal date or version is scheduled by this change. The metadata and documentation prepare -the deprecation; they do not announce a release externally or delete historical records. +No removal date or version is scheduled by this change. Historical records are preserved. ## Validation Unit tests cover GET serialization, invalid structured input, normalized ranges, permission checks, definition website/type mismatches, immutable update scope, SQL dataset separation for -PostgreSQL and ClickHouse, cache keys, SDK requests, MCP composition, and deprecation metadata. +PostgreSQL and ClickHouse, cache keys, SDK requests, MCP composition, and compatibility routing. `tests/api/report-migration.spec.ts` exercises legacy/new result parity, saved-definition lifecycles, shared access, and split datasets against the existing disposable API test stack. diff --git a/next.config.ts b/next.config.ts index ce134be8f..24c422e72 100644 --- a/next.config.ts +++ b/next.config.ts @@ -236,6 +236,15 @@ export default withNextIntl({ async rewrites() { return [ ...rewrites, + // Preserve legacy report requests outside the published API contract. + { + source: '/api/reports/:path*', + destination: '/compat/api/reports/:path*', + }, + { + source: '/api/websites/:websiteId/reports', + destination: '/compat/api/websites/:websiteId/reports', + }, { source: '/telemetry.js', destination: '/api/scripts/telemetry', diff --git a/packages/api-client/MIGRATION.md b/packages/api-client/MIGRATION.md index b4f683ef9..03824c767 100644 --- a/packages/api-client/MIGRATION.md +++ b/packages/api-client/MIGRATION.md @@ -45,7 +45,7 @@ They are deprecated and will be removed in the next major release. The object fo ## Method names Method names are OpenAPI `operationId`s. Most match the old names (`getWebsiteStats`, -`runFunnelReport`, `getRealtime`, `createTeam`, …). Endpoints that were not in the old client are +`getWebsiteFunnelStats`, `getRealtime`, `createTeam`, …). Endpoints that were not in the old client are now available automatically. Endpoints under `/api/admin`, `/api/config`, `/api/dashboard` and other internal routes are intentionally excluded. diff --git a/packages/api-client/src/client.test.ts b/packages/api-client/src/client.test.ts index 2b5839688..e37d1107b 100644 --- a/packages/api-client/src/client.test.ts +++ b/packages/api-client/src/client.test.ts @@ -68,16 +68,19 @@ describe('UmamiClient', () => { test('sends JSON bodies for POST operations', async () => { const { client, calls } = createClient([]); - await client.runFunnelReport({ + await client.createWebsiteFunnel({ websiteId: 'w1', - type: 'funnel', - filters: {}, - parameters: { startDate: 'a', endDate: 'b', window: 60, steps: [] }, + name: 'Signup funnel', + parameters: { window: 60, steps: [] }, }); expect(calls[0].method).toBe('POST'); - expect(calls[0].url.pathname).toBe('/api/reports/funnel'); - expect(JSON.parse(calls[0].body as string)).toMatchObject({ websiteId: 'w1', type: 'funnel' }); + expect(calls[0].url.pathname).toBe('/api/websites/w1/funnels'); + expect(JSON.parse(calls[0].body as string)).toEqual({ + websiteId: 'w1', + name: 'Signup funnel', + parameters: { window: 60, steps: [] }, + }); }); test('sends structured funnel criteria through the GET stats API', async () => { diff --git a/packages/api-client/src/client.ts b/packages/api-client/src/client.ts index 54afb6df8..9c17c4883 100644 --- a/packages/api-client/src/client.ts +++ b/packages/api-client/src/client.ts @@ -504,22 +504,4 @@ export class UmamiClient extends GeneratedUmamiClient { return super.getWebsiteSessionProperties(first, second as RequestOptions | undefined); } - - override getWebsiteReports( - input: Input<'getWebsiteReports'>, - options?: RequestOptions, - ): Output<'getWebsiteReports'>; - override getWebsiteReports( - websiteId: string, - params?: Params<'getWebsiteReports', 'websiteId'>, - options?: RequestOptions, - ): Output<'getWebsiteReports'>; - override getWebsiteReports( - first: string | Input<'getWebsiteReports'>, - second?: Record | RequestOptions, - third?: RequestOptions, - ) { - const [input, options] = positional<'getWebsiteReports'>('websiteId', first, second, third); - return super.getWebsiteReports(input, options); - } } diff --git a/packages/api-client/src/generated/operations.ts b/packages/api-client/src/generated/operations.ts index 963e1d37a..1f2324761 100644 --- a/packages/api-client/src/generated/operations.ts +++ b/packages/api-client/src/generated/operations.ts @@ -106,14 +106,6 @@ export const operations = { queryParams: [], hasBody: true, }, - createReport: { - operationId: 'createReport', - method: 'post', - path: '/api/reports', - pathParams: [], - queryParams: [], - hasBody: true, - }, createShare: { operationId: 'createShare', method: 'post', @@ -226,14 +218,6 @@ export const operations = { queryParams: [], hasBody: false, }, - deleteReport: { - operationId: 'deleteReport', - method: 'delete', - path: '/api/reports/{reportId}', - pathParams: ['reportId'], - queryParams: [], - hasBody: false, - }, deleteShare: { operationId: 'deleteShare', method: 'delete', @@ -1055,22 +1039,6 @@ export const operations = { ], hasBody: false, }, - getReport: { - operationId: 'getReport', - method: 'get', - path: '/api/reports/{reportId}', - pathParams: ['reportId'], - queryParams: [], - hasBody: false, - }, - getReports: { - operationId: 'getReports', - method: 'get', - path: '/api/reports', - pathParams: [], - queryParams: ['websiteId', 'type', 'page', 'pageSize', 'maxResults'], - hasBody: false, - }, getSessionDataArraySeries: { operationId: 'getSessionDataArraySeries', method: 'get', @@ -2307,14 +2275,6 @@ export const operations = { queryParams: [], hasBody: false, }, - getWebsiteReports: { - operationId: 'getWebsiteReports', - method: 'get', - path: '/api/websites/{websiteId}/reports', - pathParams: ['websiteId'], - queryParams: ['type', 'page', 'pageSize', 'maxResults'], - hasBody: false, - }, getWebsiteRetention: { operationId: 'getWebsiteRetention', method: 'get', @@ -2995,86 +2955,6 @@ export const operations = { queryParams: [], hasBody: false, }, - runAttributionReport: { - operationId: 'runAttributionReport', - method: 'post', - path: '/api/reports/attribution', - pathParams: [], - queryParams: [], - hasBody: true, - }, - runBreakdownReport: { - operationId: 'runBreakdownReport', - method: 'post', - path: '/api/reports/breakdown', - pathParams: [], - queryParams: [], - hasBody: true, - }, - runFunnelReport: { - operationId: 'runFunnelReport', - method: 'post', - path: '/api/reports/funnel', - pathParams: [], - queryParams: [], - hasBody: true, - }, - runGoalReport: { - operationId: 'runGoalReport', - method: 'post', - path: '/api/reports/goal', - pathParams: [], - queryParams: [], - hasBody: true, - }, - runHeatmapReport: { - operationId: 'runHeatmapReport', - method: 'post', - path: '/api/reports/heatmap', - pathParams: [], - queryParams: [], - hasBody: true, - }, - runJourneyReport: { - operationId: 'runJourneyReport', - method: 'post', - path: '/api/reports/journey', - pathParams: [], - queryParams: [], - hasBody: true, - }, - runPerformanceReport: { - operationId: 'runPerformanceReport', - method: 'post', - path: '/api/reports/performance', - pathParams: [], - queryParams: [], - hasBody: true, - }, - runRetentionReport: { - operationId: 'runRetentionReport', - method: 'post', - path: '/api/reports/retention', - pathParams: [], - queryParams: [], - hasBody: true, - }, - runRevenueReport: { - operationId: 'runRevenueReport', - method: 'post', - path: '/api/reports/revenue', - pathParams: [], - queryParams: [], - hasBody: true, - }, - runUtmReport: { - operationId: 'runUtmReport', - method: 'post', - path: '/api/reports/utm', - pathParams: [], - queryParams: [], - hasBody: true, - }, saveWebsiteReplay: { operationId: 'saveWebsiteReplay', method: 'post', @@ -3139,14 +3019,6 @@ export const operations = { queryParams: [], hasBody: true, }, - updateReport: { - operationId: 'updateReport', - method: 'post', - path: '/api/reports/{reportId}', - pathParams: ['reportId'], - queryParams: [], - hasBody: true, - }, updateShare: { operationId: 'updateShare', method: 'post', @@ -3409,21 +3281,6 @@ export abstract class GeneratedUmamiClient { return this.execute('createPixelShare', input, options); } - /** - * Save a report - * @deprecated See the operation description for migration details. - * Saves a report's name, description, type, and parameters for a website so it can be opened again later. - * - * Deprecated: use website-scoped /funnels or /goals resources for saved definitions. Existing IDs are preserved. Other legacy report types remain accessible here pending a persisted-data audit; no removal date has been scheduled. - * `POST /api/reports` - */ - createReport( - input: OperationInput<'createReport'>, - options?: RequestOptions, - ): Promise> { - return this.execute('createReport', input, options); - } - /** * Create a share * Creates a named share for a website, board, link, or pixel with parameters and an optional custom slug. @@ -3590,21 +3447,6 @@ export abstract class GeneratedUmamiClient { return this.execute('deletePixel', input, options); } - /** - * Delete a saved report - * @deprecated See the operation description for migration details. - * Deletes the specified saved report definition. - * - * Deprecated: use website-scoped /funnels or /goals resources for saved definitions. Existing IDs are preserved. Other legacy report types remain accessible here pending a persisted-data audit; no removal date has been scheduled. - * `DELETE /api/reports/{reportId}` - */ - deleteReport( - input: OperationInput<'deleteReport'>, - options?: RequestOptions, - ): Promise> { - return this.execute('deleteReport', input, options); - } - /** * Delete a share * Deletes the specified share after checking permission to delete shares for its resource. @@ -4095,36 +3937,6 @@ export abstract class GeneratedUmamiClient { return this.execute('getRealtime', input, options); } - /** - * Get a saved report - * @deprecated See the operation description for migration details. - * Returns the specified report's saved name, description, type, and parameters. - * - * Deprecated: use website-scoped /funnels or /goals resources for saved definitions. Existing IDs are preserved. Other legacy report types remain accessible here pending a persisted-data audit; no removal date has been scheduled. - * `GET /api/reports/{reportId}` - */ - getReport( - input: OperationInput<'getReport'>, - options?: RequestOptions, - ): Promise> { - return this.execute('getReport', input, options); - } - - /** - * List saved reports - * @deprecated See the operation description for migration details. - * Returns a paginated list of saved reports for the requested website, optionally filtered by report type. - * - * Deprecated: use website-scoped /funnels or /goals resources for saved definitions. Existing IDs are preserved. Other legacy report types remain accessible here pending a persisted-data audit; no removal date has been scheduled. - * `GET /api/reports` - */ - getReports( - input: OperationInput<'getReports'>, - options?: RequestOptions, - ): Promise> { - return this.execute('getReports', input, options); - } - /** * Get session array values over time * Counts distinct sessions for individual values in an array property, grouped by value and time interval. @@ -4736,21 +4548,6 @@ export abstract class GeneratedUmamiClient { return this.execute('getWebsiteReplaySaved', input, options); } - /** - * List a website's saved reports - * @deprecated See the operation description for migration details. - * Returns a paginated list of saved report definitions for the website, optionally filtered by report type. - * - * Deprecated: use website-scoped /funnels or /goals resources for saved definitions. Existing IDs are preserved. Other legacy report types remain accessible here pending a persisted-data audit; no removal date has been scheduled. - * `GET /api/websites/{websiteId}/reports` - */ - getWebsiteReports( - input: OperationInput<'getWebsiteReports'>, - options?: RequestOptions, - ): Promise> { - return this.execute('getWebsiteReports', input, options); - } - /** * Get website retention * `GET /api/websites/{websiteId}/retention` @@ -5095,156 +4892,6 @@ export abstract class GeneratedUmamiClient { return this.execute('resetWebsite', input, options); } - /** - * Run an attribution report - * @deprecated See the operation description for migration details. - * Calculates how traffic sources contribute to conversions using the supplied attribution settings, date range, and filters. - * - * Deprecated: use GET /api/websites/{websiteId}/attribution. The legacy request and response remain supported during migration. - * `POST /api/reports/attribution` - */ - runAttributionReport( - input: OperationInput<'runAttributionReport'>, - options?: RequestOptions, - ): Promise> { - return this.execute('runAttributionReport', input, options); - } - - /** - * Run a breakdown report - * @deprecated See the operation description for migration details. - * Groups website activity by the selected dimensions for the requested date range and filters. - * - * Deprecated: use GET /api/websites/{websiteId}/breakdown. The legacy request and response remain supported during migration. - * `POST /api/reports/breakdown` - */ - runBreakdownReport( - input: OperationInput<'runBreakdownReport'>, - options?: RequestOptions, - ): Promise> { - return this.execute('runBreakdownReport', input, options); - } - - /** - * Run a funnel report - * @deprecated See the operation description for migration details. - * Calculates visitor progression through a sequence of pages or events using the supplied funnel steps and filters. - * - * Deprecated: use GET /api/websites/{websiteId}/funnels/stats or /funnels/{funnelId}/stats. The legacy request and response remain supported during migration. - * `POST /api/reports/funnel` - */ - runFunnelReport( - input: OperationInput<'runFunnelReport'>, - options?: RequestOptions, - ): Promise> { - return this.execute('runFunnelReport', input, options); - } - - /** - * Run a goal report - * @deprecated See the operation description for migration details. - * Counts visitors who reached a matching page or triggered a matching event and returns the total visitor count for comparison. - * - * Deprecated: use GET /api/websites/{websiteId}/goals/stats or /goals/{goalId}/stats. The legacy request and response remain supported during migration. - * `POST /api/reports/goal` - */ - runGoalReport( - input: OperationInput<'runGoalReport'>, - options?: RequestOptions, - ): Promise> { - return this.execute('runGoalReport', input, options); - } - - /** - * Get page heatmap data - * @deprecated See the operation description for migration details. - * Returns recorded click or scroll data for the selected page and date range to display as a heatmap. - * - * Deprecated: use GET /api/websites/{websiteId}/heatmaps. The legacy request and response remain supported during migration. - * `POST /api/reports/heatmap` - */ - runHeatmapReport( - input: OperationInput<'runHeatmapReport'>, - options?: RequestOptions, - ): Promise> { - return this.execute('runHeatmapReport', input, options); - } - - /** - * Run a visitor journey report - * @deprecated See the operation description for migration details. - * Returns paths through pages or events using the supplied journey settings and website filters. - * - * Deprecated: use GET /api/websites/{websiteId}/journeys. The legacy request and response remain supported during migration. - * `POST /api/reports/journey` - */ - runJourneyReport( - input: OperationInput<'runJourneyReport'>, - options?: RequestOptions, - ): Promise> { - return this.execute('runJourneyReport', input, options); - } - - /** - * Run a performance report - * @deprecated See the operation description for migration details. - * Returns performance trends, summary metrics, and breakdowns by page, page title, device, and browser for the selected website and date range. - * - * Deprecated: use GET /api/websites/{websiteId}/performance/stats, /performance/chart, and /performance/metrics. The legacy request and response remain supported during migration. - * `POST /api/reports/performance` - */ - runPerformanceReport( - input: OperationInput<'runPerformanceReport'>, - options?: RequestOptions, - ): Promise> { - return this.execute('runPerformanceReport', input, options); - } - - /** - * Run a retention report - * @deprecated See the operation description for migration details. - * Calculates how groups of visitors return over time using the supplied retention settings, date range, and filters. - * - * Deprecated: use GET /api/websites/{websiteId}/retention. The legacy request and response remain supported during migration. - * `POST /api/reports/retention` - */ - runRetentionReport( - input: OperationInput<'runRetentionReport'>, - options?: RequestOptions, - ): Promise> { - return this.execute('runRetentionReport', input, options); - } - - /** - * Run a revenue report - * @deprecated See the operation description for migration details. - * Returns revenue trends, totals with a comparison period, and breakdowns by country, region, referrer, and channel. - * - * Deprecated: use GET /api/websites/{websiteId}/revenue/stats, /revenue/chart, and /revenue/metrics. The legacy request and response remain supported during migration. - * `POST /api/reports/revenue` - */ - runRevenueReport( - input: OperationInput<'runRevenueReport'>, - options?: RequestOptions, - ): Promise> { - return this.execute('runRevenueReport', input, options); - } - - /** - * Run a campaign report - * @deprecated See the operation description for migration details. - * Returns traffic breakdowns for UTM source, medium, campaign, term, and content using the supplied date range and filters. - * - * Deprecated: use GET /api/websites/{websiteId}/utm/metrics for each UTM dimension. The legacy request and response remain supported during migration. - * `POST /api/reports/utm` - */ - runUtmReport( - input: OperationInput<'runUtmReport'>, - options?: RequestOptions, - ): Promise> { - return this.execute('runUtmReport', input, options); - } - /** * Save or unsave a session replay * Adds a replay to the website's saved replays with an optional name, or removes it when isSaved is false. @@ -5335,21 +4982,6 @@ export abstract class GeneratedUmamiClient { return this.execute('updatePixel', input, options); } - /** - * Update a saved report - * @deprecated See the operation description for migration details. - * Updates the specified report's website, name, description, type, and parameters. - * - * Deprecated: use website-scoped /funnels or /goals resources for saved definitions. Existing IDs are preserved. Other legacy report types remain accessible here pending a persisted-data audit; no removal date has been scheduled. - * `POST /api/reports/{reportId}` - */ - updateReport( - input: OperationInput<'updateReport'>, - options?: RequestOptions, - ): Promise> { - return this.execute('updateReport', input, options); - } - /** * Update a share * Updates the specified share's name, slug, and parameters. diff --git a/packages/api-client/src/generated/types.ts b/packages/api-client/src/generated/types.ts index 238f7a421..0eedc6725 100644 --- a/packages/api-client/src/generated/types.ts +++ b/packages/api-client/src/generated/types.ts @@ -886,303 +886,6 @@ export interface paths { patch?: never; trace?: never; }; - '/api/reports': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List saved reports - * @deprecated - * @description Returns a paginated list of saved reports for the requested website, optionally filtered by report type. - * - * Deprecated: use website-scoped /funnels or /goals resources for saved definitions. Existing IDs are preserved. Other legacy report types remain accessible here pending a persisted-data audit; no removal date has been scheduled. - */ - get: operations['getReports']; - put?: never; - /** - * Save a report - * @deprecated - * @description Saves a report's name, description, type, and parameters for a website so it can be opened again later. - * - * Deprecated: use website-scoped /funnels or /goals resources for saved definitions. Existing IDs are preserved. Other legacy report types remain accessible here pending a persisted-data audit; no removal date has been scheduled. - */ - post: operations['createReport']; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/api/reports/{reportId}': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get a saved report - * @deprecated - * @description Returns the specified report's saved name, description, type, and parameters. - * - * Deprecated: use website-scoped /funnels or /goals resources for saved definitions. Existing IDs are preserved. Other legacy report types remain accessible here pending a persisted-data audit; no removal date has been scheduled. - */ - get: operations['getReport']; - put?: never; - /** - * Update a saved report - * @deprecated - * @description Updates the specified report's website, name, description, type, and parameters. - * - * Deprecated: use website-scoped /funnels or /goals resources for saved definitions. Existing IDs are preserved. Other legacy report types remain accessible here pending a persisted-data audit; no removal date has been scheduled. - */ - post: operations['updateReport']; - /** - * Delete a saved report - * @deprecated - * @description Deletes the specified saved report definition. - * - * Deprecated: use website-scoped /funnels or /goals resources for saved definitions. Existing IDs are preserved. Other legacy report types remain accessible here pending a persisted-data audit; no removal date has been scheduled. - */ - delete: operations['deleteReport']; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/api/reports/attribution': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Run an attribution report - * @deprecated - * @description Calculates how traffic sources contribute to conversions using the supplied attribution settings, date range, and filters. - * - * Deprecated: use GET /api/websites/{websiteId}/attribution. The legacy request and response remain supported during migration. - */ - post: operations['runAttributionReport']; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/api/reports/breakdown': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Run a breakdown report - * @deprecated - * @description Groups website activity by the selected dimensions for the requested date range and filters. - * - * Deprecated: use GET /api/websites/{websiteId}/breakdown. The legacy request and response remain supported during migration. - */ - post: operations['runBreakdownReport']; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/api/reports/funnel': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Run a funnel report - * @deprecated - * @description Calculates visitor progression through a sequence of pages or events using the supplied funnel steps and filters. - * - * Deprecated: use GET /api/websites/{websiteId}/funnels/stats or /funnels/{funnelId}/stats. The legacy request and response remain supported during migration. - */ - post: operations['runFunnelReport']; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/api/reports/goal': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Run a goal report - * @deprecated - * @description Counts visitors who reached a matching page or triggered a matching event and returns the total visitor count for comparison. - * - * Deprecated: use GET /api/websites/{websiteId}/goals/stats or /goals/{goalId}/stats. The legacy request and response remain supported during migration. - */ - post: operations['runGoalReport']; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/api/reports/heatmap': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Get page heatmap data - * @deprecated - * @description Returns recorded click or scroll data for the selected page and date range to display as a heatmap. - * - * Deprecated: use GET /api/websites/{websiteId}/heatmaps. The legacy request and response remain supported during migration. - */ - post: operations['runHeatmapReport']; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/api/reports/journey': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Run a visitor journey report - * @deprecated - * @description Returns paths through pages or events using the supplied journey settings and website filters. - * - * Deprecated: use GET /api/websites/{websiteId}/journeys. The legacy request and response remain supported during migration. - */ - post: operations['runJourneyReport']; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/api/reports/performance': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Run a performance report - * @deprecated - * @description Returns performance trends, summary metrics, and breakdowns by page, page title, device, and browser for the selected website and date range. - * - * Deprecated: use GET /api/websites/{websiteId}/performance/stats, /performance/chart, and /performance/metrics. The legacy request and response remain supported during migration. - */ - post: operations['runPerformanceReport']; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/api/reports/retention': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Run a retention report - * @deprecated - * @description Calculates how groups of visitors return over time using the supplied retention settings, date range, and filters. - * - * Deprecated: use GET /api/websites/{websiteId}/retention. The legacy request and response remain supported during migration. - */ - post: operations['runRetentionReport']; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/api/reports/revenue': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Run a revenue report - * @deprecated - * @description Returns revenue trends, totals with a comparison period, and breakdowns by country, region, referrer, and channel. - * - * Deprecated: use GET /api/websites/{websiteId}/revenue/stats, /revenue/chart, and /revenue/metrics. The legacy request and response remain supported during migration. - */ - post: operations['runRevenueReport']; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - '/api/reports/utm': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** - * Run a campaign report - * @deprecated - * @description Returns traffic breakdowns for UTM source, medium, campaign, term, and content using the supplied date range and filters. - * - * Deprecated: use GET /api/websites/{websiteId}/utm/metrics for each UTM dimension. The legacy request and response remain supported during migration. - */ - post: operations['runUtmReport']; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; '/api/scripts/telemetry': { parameters: { query?: never; @@ -2492,29 +2195,6 @@ export interface paths { patch?: never; trace?: never; }; - '/api/websites/{websiteId}/reports': { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List a website's saved reports - * @deprecated - * @description Returns a paginated list of saved report definitions for the website, optionally filtered by report type. - * - * Deprecated: use website-scoped /funnels or /goals resources for saved definitions. Existing IDs are preserved. Other legacy report types remain accessible here pending a persisted-data audit; no removal date has been scheduled. - */ - get: operations['getWebsiteReports']; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; '/api/websites/{websiteId}/reset': { parameters: { query?: never; @@ -9279,1516 +8959,6 @@ export interface operations { }; }; }; - getReports: { - parameters: { - query: { - /** @description Maximum number of results to include. */ - maxResults?: number; - /** @description Page number, starting at 1. */ - page?: number; - /** @description Number of results per page. */ - pageSize?: number; - /** @description Type of resource or analytics dimension to return. */ - type?: string; - /** @description ID of the website. */ - websiteId: string; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description The operation completed successfully. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': { - /** @description Number of matching records. */ - count: number; - /** @description Data returned by the operation. */ - data: ({ - /** - * Format: date-time - * @description Date and time the record was created. - */ - createdAt: string; - /** @description Description of the resource. */ - description: string; - /** @description Unique identifier of the resource. */ - id: string; - /** @description Display name of the resource. */ - name: string; - /** @description Configuration parameters for the resource. */ - parameters: - | string - | number - | false - | true - | { - [key: string]: - | string - | number - | false - | true - | unknown - | { - length: number; - }; - } - | { - length: number; - }; - type: string; - /** - * Format: date-time - * @description Date and time the record was last updated. - */ - updatedAt: string; - /** @description ID of the associated user. */ - userId: string; - /** @description ID of the website. */ - websiteId: string; - } & { - website?: { - /** @description Domain name associated with the resource. */ - domain: string; - /** @description ID of the associated user. */ - userId: string; - }; - })[]; - /** @description Whether the results were truncated by the maximum result limit. */ - isCapped?: boolean; - /** @description Field to sort the results by. */ - orderBy?: string; - /** @description Page number, starting at 1. */ - page: number; - /** @description Number of results per page. */ - pageSize: number; - /** @description Search text used to filter results. */ - search?: string; - /** @description Whether to sort results in descending order. */ - sortDescending?: boolean; - }; - }; - }; - /** @description Bad request. */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "bad-request", - * "message": "Bad request.", - * "status": 400 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - /** @description Unauthorized. */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "unauthorized", - * "message": "Unauthorized.", - * "status": 401 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - }; - }; - createReport: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - 'application/json': { - /** @description Description of the resource. */ - description?: string; - /** @description Display name of the resource. */ - name: string; - /** @description Configuration parameters for the resource. */ - parameters: { - [key: string]: unknown; - }; - type: string; - /** - * Format: uuid - * @description ID of the website. - */ - websiteId: string; - }; - }; - }; - responses: { - /** @description The operation completed successfully. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': { - /** - * Format: date-time - * @description Date and time the record was created. - */ - createdAt: string; - /** @description Description of the resource. */ - description: string; - /** @description Unique identifier of the resource. */ - id: string; - /** @description Display name of the resource. */ - name: string; - /** @description Configuration parameters for the resource. */ - parameters: - | string - | number - | false - | true - | { - [key: string]: - | string - | number - | false - | true - | unknown - | { - length: number; - }; - } - | { - length: number; - }; - type: string; - /** - * Format: date-time - * @description Date and time the record was last updated. - */ - updatedAt: string; - /** @description ID of the associated user. */ - userId: string; - /** @description ID of the website. */ - websiteId: string; - }; - }; - }; - /** @description Bad request. */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "bad-request", - * "message": "Bad request.", - * "status": 400 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - /** @description Unauthorized. */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "unauthorized", - * "message": "Unauthorized.", - * "status": 401 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - }; - }; - getReport: { - parameters: { - query?: never; - header?: never; - path: { - /** @description ID of the saved report. */ - reportId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description The operation completed successfully. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': { - /** - * Format: date-time - * @description Date and time the record was created. - */ - createdAt: string; - /** @description Description of the resource. */ - description: string; - /** @description Unique identifier of the resource. */ - id: string; - /** @description Display name of the resource. */ - name: string; - /** @description Configuration parameters for the resource. */ - parameters: - | string - | number - | false - | true - | { - [key: string]: - | string - | number - | false - | true - | unknown - | { - length: number; - }; - } - | { - length: number; - }; - type: string; - /** - * Format: date-time - * @description Date and time the record was last updated. - */ - updatedAt: string; - /** @description ID of the associated user. */ - userId: string; - /** @description ID of the website. */ - websiteId: string; - }; - }; - }; - /** @description Unauthorized. */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "unauthorized", - * "message": "Unauthorized.", - * "status": 401 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - /** @description Not found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "not-found", - * "message": "Not found.", - * "status": 404 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - }; - }; - updateReport: { - parameters: { - query?: never; - header?: never; - path: { - /** @description ID of the saved report. */ - reportId: string; - }; - cookie?: never; - }; - requestBody: { - content: { - 'application/json': { - /** @description Description of the resource. */ - description?: string; - /** @description Display name of the resource. */ - name: string; - /** @description Configuration parameters for the resource. */ - parameters: { - [key: string]: unknown; - }; - type: string; - /** - * Format: uuid - * @description ID of the website. - */ - websiteId: string; - }; - }; - }; - responses: { - /** @description The operation completed successfully. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': { - /** - * Format: date-time - * @description Date and time the record was created. - */ - createdAt: string; - /** @description Description of the resource. */ - description: string; - /** @description Unique identifier of the resource. */ - id: string; - /** @description Display name of the resource. */ - name: string; - /** @description Configuration parameters for the resource. */ - parameters: - | string - | number - | false - | true - | { - [key: string]: - | string - | number - | false - | true - | unknown - | { - length: number; - }; - } - | { - length: number; - }; - type: string; - /** - * Format: date-time - * @description Date and time the record was last updated. - */ - updatedAt: string; - /** @description ID of the associated user. */ - userId: string; - /** @description ID of the website. */ - websiteId: string; - }; - }; - }; - /** @description Bad request. */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "bad-request", - * "message": "Bad request.", - * "status": 400 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - /** @description Unauthorized. */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "unauthorized", - * "message": "Unauthorized.", - * "status": 401 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - /** @description Not found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "not-found", - * "message": "Not found.", - * "status": 404 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - }; - }; - deleteReport: { - parameters: { - query?: never; - header?: never; - path: { - /** @description ID of the saved report. */ - reportId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description The operation completed successfully. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': { - /** - * @description Whether the operation succeeded. - * @constant - */ - ok: true; - }; - }; - }; - /** @description Unauthorized. */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "unauthorized", - * "message": "Unauthorized.", - * "status": 401 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - /** @description Not found. */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "not-found", - * "message": "Not found.", - * "status": 404 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - }; - }; - runAttributionReport: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - 'application/json': { - /** @description Filters applied to the analytics data. */ - filters: { - [key: string]: unknown; - }; - /** @description Configuration parameters for the resource. */ - parameters: { - [key: string]: unknown; - }; - type: string; - /** - * Format: uuid - * @description ID of the website. - */ - websiteId: string; - }; - }; - }; - responses: { - /** @description The operation completed successfully. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': { - paidAds: { - /** @description Display name of the resource. */ - name: string; - value: number; - }[]; - /** @description URL of the referring page. */ - referrer: { - /** @description Display name of the resource. */ - name: string; - value: number; - }[]; - total: { - /** @description Pageview counts for the selected period. */ - pageviews: number; - /** @description Unique visitor counts for the selected period. */ - visitors: number; - /** @description Visit counts for the selected period. */ - visits: number; - }; - utm_campaign: { - /** @description Display name of the resource. */ - name: string; - value: number; - }[]; - utm_content: { - /** @description Display name of the resource. */ - name: string; - value: number; - }[]; - utm_medium: { - /** @description Display name of the resource. */ - name: string; - value: number; - }[]; - utm_source: { - /** @description Display name of the resource. */ - name: string; - value: number; - }[]; - utm_term: { - /** @description Display name of the resource. */ - name: string; - value: number; - }[]; - }; - }; - }; - /** @description Bad request. */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "bad-request", - * "message": "Bad request.", - * "status": 400 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - /** @description Unauthorized. */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "unauthorized", - * "message": "Unauthorized.", - * "status": 401 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - }; - }; - runBreakdownReport: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - 'application/json': { - /** @description Filters applied to the analytics data. */ - filters: { - [key: string]: unknown; - }; - /** @description Configuration parameters for the resource. */ - parameters: { - [key: string]: unknown; - }; - type: string; - /** - * Format: uuid - * @description ID of the website. - */ - websiteId: string; - }; - }; - }; - responses: { - /** @description The operation completed successfully. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': ({ - /** @description Number of visits with only one pageview. */ - bounces: number; - /** @description Total visit duration in seconds. */ - totaltime: number; - /** @description Pageview counts for the selected period. */ - views: number; - /** @description Unique visitor counts for the selected period. */ - visitors: number; - /** @description Visit counts for the selected period. */ - visits: number; - } & { - [key: string]: string | number; - })[]; - }; - }; - /** @description Bad request. */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "bad-request", - * "message": "Bad request.", - * "status": 400 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - /** @description Unauthorized. */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "unauthorized", - * "message": "Unauthorized.", - * "status": 401 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - }; - }; - runFunnelReport: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - 'application/json': { - /** @description Filters applied to the analytics data. */ - filters: { - [key: string]: unknown; - }; - /** @description Configuration parameters for the resource. */ - parameters: { - [key: string]: unknown; - }; - type: string; - /** - * Format: uuid - * @description ID of the website. - */ - websiteId: string; - }; - }; - }; - responses: { - /** @description The operation completed successfully. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': { - dropoff: number; - dropped: number; - /** @description Filters applied to the analytics data. */ - filters?: { - operator: string; - property: string; - value: string; - }[]; - previous: number; - remaining: number; - type: string; - value: string; - /** @description Unique visitor counts for the selected period. */ - visitors: number; - }[]; - }; - }; - /** @description Bad request. */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "bad-request", - * "message": "Bad request.", - * "status": 400 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - /** @description Unauthorized. */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "unauthorized", - * "message": "Unauthorized.", - * "status": 401 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - }; - }; - runGoalReport: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - 'application/json': { - /** @description Filters applied to the analytics data. */ - filters: { - [key: string]: unknown; - }; - /** @description Configuration parameters for the resource. */ - parameters: { - [key: string]: unknown; - }; - type: string; - /** - * Format: uuid - * @description ID of the website. - */ - websiteId: string; - }; - }; - }; - responses: { - /** @description The operation completed successfully. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': { - num: number; - total: number; - }; - }; - }; - /** @description Bad request. */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "bad-request", - * "message": "Bad request.", - * "status": 400 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - /** @description Unauthorized. */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "unauthorized", - * "message": "Unauthorized.", - * "status": 401 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - }; - }; - runHeatmapReport: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - 'application/json': { - /** @description Filters applied to the analytics data. */ - filters: { - [key: string]: unknown; - }; - /** @description Configuration parameters for the resource. */ - parameters: { - [key: string]: unknown; - }; - type: string; - /** - * Format: uuid - * @description ID of the website. - */ - websiteId: string; - }; - }; - }; - responses: { - /** @description The operation completed successfully. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': { - mode: 'click' | 'scroll'; - pages: { - /** @description Number of matching records. */ - count: number; - sessions: number; - /** @description Path portion of the page URL. */ - urlPath: string; - }[]; - points: { - /** @description Number of matching records. */ - count: number; - /** @description Page height in pixels. */ - pageH: number; - /** @description Page width in pixels. */ - pageW: number; - /** @description Horizontal position on the page in pixels. */ - pageX: number; - /** @description Vertical position on the page in pixels. */ - pageY: number; - /** @description Viewport height in pixels. */ - viewportH: number; - /** @description Viewport width in pixels. */ - viewportW: number; - x: number; - y: number; - }[]; - scroll: { - buckets: { - depth: number; - /** @description Page height in pixels. */ - pageH: number; - /** @description Page width in pixels. */ - pageW: number; - sessions: number; - /** @description Viewport height in pixels. */ - viewportH: number; - /** @description Viewport width in pixels. */ - viewportW: number; - }[]; - /** @description Page height in pixels. */ - pageH: number; - /** @description Page width in pixels. */ - pageW: number; - totalSessions: number; - /** @description Viewport height in pixels. */ - viewportH: number; - /** @description Viewport width in pixels. */ - viewportW: number; - }; - snapshot: { - /** @description Unique identifier of the resource. */ - id: string; - /** @constant */ - kind: 'iframe'; - /** @description Page height in pixels. */ - pageH: number; - /** @description Page width in pixels. */ - pageW: number; - /** @description URL associated with the resource. */ - url: string; - /** @description Viewport height in pixels. */ - viewportH: number; - /** @description Viewport width in pixels. */ - viewportW: number; - }; - }; - }; - }; - /** @description Bad request. */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "bad-request", - * "message": "Bad request.", - * "status": 400 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - /** @description Unauthorized. */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "unauthorized", - * "message": "Unauthorized.", - * "status": 401 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - }; - }; - runJourneyReport: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - 'application/json': { - /** @description Filters applied to the analytics data. */ - filters: { - [key: string]: unknown; - }; - /** @description Configuration parameters for the resource. */ - parameters: { - [key: string]: unknown; - }; - type: string; - /** - * Format: uuid - * @description ID of the website. - */ - websiteId: string; - }; - }; - }; - responses: { - /** @description The operation completed successfully. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': { - /** @description Number of matching records. */ - count: number; - e1: string; - e2: string; - e3: string; - e4: string; - e5: string; - e6: string; - e7: string; - }[]; - }; - }; - /** @description Bad request. */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "bad-request", - * "message": "Bad request.", - * "status": 400 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - /** @description Unauthorized. */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "unauthorized", - * "message": "Unauthorized.", - * "status": 401 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - }; - }; - runPerformanceReport: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - 'application/json': { - /** @description Filters applied to the analytics data. */ - filters: { - [key: string]: unknown; - }; - /** @description Configuration parameters for the resource. */ - parameters: { - [key: string]: unknown; - }; - type: string; - /** - * Format: uuid - * @description ID of the website. - */ - websiteId: string; - }; - }; - }; - responses: { - /** @description The operation completed successfully. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': { - browsers: unknown; - chart: unknown; - devices: unknown; - pages: unknown; - pageTitles: unknown; - summary: unknown; - }; - }; - }; - /** @description Bad request. */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "bad-request", - * "message": "Bad request.", - * "status": 400 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - /** @description Unauthorized. */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "unauthorized", - * "message": "Unauthorized.", - * "status": 401 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - }; - }; - runRetentionReport: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - 'application/json': { - /** @description Filters applied to the analytics data. */ - filters: { - [key: string]: unknown; - }; - /** @description Configuration parameters for the resource. */ - parameters: { - [key: string]: unknown; - }; - type: string; - /** - * Format: uuid - * @description ID of the website. - */ - websiteId: string; - }; - }; - }; - responses: { - /** @description The operation completed successfully. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': { - date: string; - day: number; - percentage: number; - returnVisitors: number; - /** @description Unique visitor counts for the selected period. */ - visitors: number; - }[]; - }; - }; - /** @description Bad request. */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "bad-request", - * "message": "Bad request.", - * "status": 400 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - /** @description Unauthorized. */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "unauthorized", - * "message": "Unauthorized.", - * "status": 401 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - }; - }; - runRevenueReport: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - 'application/json': { - /** @description Filters applied to the analytics data. */ - filters: { - [key: string]: unknown; - }; - /** @description Configuration parameters for the resource. */ - parameters: { - [key: string]: unknown; - }; - type: string; - /** - * Format: uuid - * @description ID of the website. - */ - websiteId: string; - }; - }; - }; - responses: { - /** @description The operation completed successfully. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': { - channel: { - /** @description Display name of the resource. */ - name: string; - value: number; - }[]; - chart: { - /** @description Number of matching records. */ - count: number; - t: string; - x: string; - y: number; - }[]; - /** @description Country code of the visitor. */ - country: { - /** @description Display name of the resource. */ - name: string; - value: number; - }[]; - /** @description URL of the referring page. */ - referrer: { - /** @description Display name of the resource. */ - name: string; - value: number; - }[]; - /** @description Region or subdivision of the visitor. */ - region: { - /** @description Country code of the visitor. */ - country: string; - /** @description Display name of the resource. */ - name: string; - value: number; - }[]; - total: unknown; - }; - }; - }; - /** @description Bad request. */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "bad-request", - * "message": "Bad request.", - * "status": 400 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - /** @description Unauthorized. */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "unauthorized", - * "message": "Unauthorized.", - * "status": 401 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - }; - }; - runUtmReport: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - 'application/json': { - /** @description Filters applied to the analytics data. */ - filters: { - [key: string]: unknown; - }; - /** @description Configuration parameters for the resource. */ - parameters: { - [key: string]: unknown; - }; - type: string; - /** - * Format: uuid - * @description ID of the website. - */ - websiteId: string; - }; - }; - }; - responses: { - /** @description The operation completed successfully. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': { - utm_campaign: unknown[]; - utm_content: unknown[]; - utm_medium: unknown[]; - utm_source: unknown[]; - utm_term: unknown[]; - }; - }; - }; - /** @description Bad request. */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "bad-request", - * "message": "Bad request.", - * "status": 400 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - /** @description Unauthorized. */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "unauthorized", - * "message": "Unauthorized.", - * "status": 401 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - }; - }; getScriptsTelemetry: { parameters: { query?: never; @@ -20488,140 +18658,6 @@ export interface operations { }; }; }; - getWebsiteReports: { - parameters: { - query?: { - /** @description Maximum number of results to include. */ - maxResults?: number; - /** @description Page number, starting at 1. */ - page?: number; - /** @description Number of results per page. */ - pageSize?: number; - /** @description Type of resource or analytics dimension to return. */ - type?: string; - }; - header?: never; - path: { - /** @description ID of the website. */ - websiteId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description The operation completed successfully. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - 'application/json': { - /** @description Number of matching records. */ - count: number; - /** @description Data returned by the operation. */ - data: ({ - /** - * Format: date-time - * @description Date and time the record was created. - */ - createdAt: string; - /** @description Description of the resource. */ - description: string; - /** @description Unique identifier of the resource. */ - id: string; - /** @description Display name of the resource. */ - name: string; - /** @description Configuration parameters for the resource. */ - parameters: - | string - | number - | false - | true - | { - [key: string]: - | string - | number - | false - | true - | unknown - | { - length: number; - }; - } - | { - length: number; - }; - type: string; - /** - * Format: date-time - * @description Date and time the record was last updated. - */ - updatedAt: string; - /** @description ID of the associated user. */ - userId: string; - /** @description ID of the website. */ - websiteId: string; - } & { - website?: { - /** @description Domain name associated with the resource. */ - domain: string; - /** @description ID of the associated user. */ - userId: string; - }; - })[]; - /** @description Whether the results were truncated by the maximum result limit. */ - isCapped?: boolean; - /** @description Field to sort the results by. */ - orderBy?: string; - /** @description Page number, starting at 1. */ - page: number; - /** @description Number of results per page. */ - pageSize: number; - /** @description Search text used to filter results. */ - search?: string; - /** @description Whether to sort results in descending order. */ - sortDescending?: boolean; - }; - }; - }; - /** @description Bad request. */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "bad-request", - * "message": "Bad request.", - * "status": 400 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - /** @description Unauthorized. */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "unauthorized", - * "message": "Unauthorized.", - * "status": 401 - * } - * } - */ - 'application/json': components['schemas']['ApiError']; - }; - }; - }; - }; resetWebsite: { parameters: { query?: never; diff --git a/packages/api-client/src/http.test.ts b/packages/api-client/src/http.test.ts index 13634b22b..aa690ab26 100644 --- a/packages/api-client/src/http.test.ts +++ b/packages/api-client/src/http.test.ts @@ -19,11 +19,11 @@ const statsOperation: OperationDefinition = { hasBody: false, }; -const reportOperation: OperationDefinition = { - operationId: 'runFunnelReport', +const funnelOperation: OperationDefinition = { + operationId: 'createWebsiteFunnel', method: 'post', - path: '/api/reports/funnel', - pathParams: [], + path: '/api/websites/{websiteId}/funnels', + pathParams: ['websiteId'], queryParams: [], hasBody: true, }; @@ -111,16 +111,15 @@ describe('splitInput', () => { test('routes unknown keys to the body for operations with a request body', () => { expect( - splitInput(reportOperation, { + splitInput(funnelOperation, { websiteId: 'w', - type: 'funnel', + name: 'Signup', parameters: { steps: [] }, - filters: {}, }), ).toEqual({ - path: {}, + path: { websiteId: 'w' }, query: {}, - body: { websiteId: 'w', type: 'funnel', parameters: { steps: [] }, filters: {} }, + body: { websiteId: 'w', name: 'Signup', parameters: { steps: [] } }, }); }); }); @@ -143,7 +142,7 @@ describe('sendRequest', () => { const result = await sendRequest(fetchImpl, { method: 'post', - url: new URL('https://example.com/api/reports/funnel'), + url: new URL('https://example.com/api/websites/w/funnels'), headers: { authorization: 'Bearer t', 'x-custom': '1' }, body: { websiteId: 'w' }, }); diff --git a/src/app/api/reports/[reportId]/route.ts b/src/app/(compat)/compat/api/reports/[reportId]/route.ts similarity index 100% rename from src/app/api/reports/[reportId]/route.ts rename to src/app/(compat)/compat/api/reports/[reportId]/route.ts diff --git a/src/app/api/reports/attribution/route.ts b/src/app/(compat)/compat/api/reports/attribution/route.ts similarity index 100% rename from src/app/api/reports/attribution/route.ts rename to src/app/(compat)/compat/api/reports/attribution/route.ts diff --git a/src/app/api/reports/breakdown/route.ts b/src/app/(compat)/compat/api/reports/breakdown/route.ts similarity index 100% rename from src/app/api/reports/breakdown/route.ts rename to src/app/(compat)/compat/api/reports/breakdown/route.ts diff --git a/src/app/api/reports/funnel/route.ts b/src/app/(compat)/compat/api/reports/funnel/route.ts similarity index 100% rename from src/app/api/reports/funnel/route.ts rename to src/app/(compat)/compat/api/reports/funnel/route.ts diff --git a/src/app/api/reports/goal/route.ts b/src/app/(compat)/compat/api/reports/goal/route.ts similarity index 100% rename from src/app/api/reports/goal/route.ts rename to src/app/(compat)/compat/api/reports/goal/route.ts diff --git a/src/app/api/reports/heatmap/route.ts b/src/app/(compat)/compat/api/reports/heatmap/route.ts similarity index 100% rename from src/app/api/reports/heatmap/route.ts rename to src/app/(compat)/compat/api/reports/heatmap/route.ts diff --git a/src/app/api/reports/journey/route.ts b/src/app/(compat)/compat/api/reports/journey/route.ts similarity index 100% rename from src/app/api/reports/journey/route.ts rename to src/app/(compat)/compat/api/reports/journey/route.ts diff --git a/src/app/api/reports/performance/route.ts b/src/app/(compat)/compat/api/reports/performance/route.ts similarity index 100% rename from src/app/api/reports/performance/route.ts rename to src/app/(compat)/compat/api/reports/performance/route.ts diff --git a/src/app/api/reports/retention/route.ts b/src/app/(compat)/compat/api/reports/retention/route.ts similarity index 100% rename from src/app/api/reports/retention/route.ts rename to src/app/(compat)/compat/api/reports/retention/route.ts diff --git a/src/app/api/reports/revenue/route.ts b/src/app/(compat)/compat/api/reports/revenue/route.ts similarity index 100% rename from src/app/api/reports/revenue/route.ts rename to src/app/(compat)/compat/api/reports/revenue/route.ts diff --git a/src/app/api/reports/route.ts b/src/app/(compat)/compat/api/reports/route.ts similarity index 100% rename from src/app/api/reports/route.ts rename to src/app/(compat)/compat/api/reports/route.ts diff --git a/src/app/api/reports/utm/route.ts b/src/app/(compat)/compat/api/reports/utm/route.ts similarity index 100% rename from src/app/api/reports/utm/route.ts rename to src/app/(compat)/compat/api/reports/utm/route.ts diff --git a/src/app/api/websites/[websiteId]/reports/route.ts b/src/app/(compat)/compat/api/websites/[websiteId]/reports/route.ts similarity index 100% rename from src/app/api/websites/[websiteId]/reports/route.ts rename to src/app/(compat)/compat/api/websites/[websiteId]/reports/route.ts diff --git a/src/app/api/reports/[reportId]/contract.generated.ts b/src/app/api/reports/[reportId]/contract.generated.ts deleted file mode 100644 index c3ee5bf6a..000000000 --- a/src/app/api/reports/[reportId]/contract.generated.ts +++ /dev/null @@ -1,453 +0,0 @@ -// Generated by `pnpm openapi:contracts`. Do not edit directly. - -import type { ZodOpenApiOperationObject } from 'zod-openapi'; -import { defineOperation } from '@/openapi/operation'; - -const operation1 = defineOperation({ - method: 'delete', - path: '/api/reports/{reportId}', - audience: 'public', - auth: 'bearer', - operation: { - operationId: 'deleteReport', - summary: 'Delete reports report id', - tags: ['Reports'], - parameters: [ - { - name: 'reportId', - in: 'path', - required: true, - schema: { - type: 'string', - }, - }, - ], - responses: { - '200': { - description: 'The operation completed successfully.', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - ok: { - const: true, - }, - }, - required: ['ok'], - }, - }, - }, - }, - '401': { - description: 'Unauthorized.', - content: { - 'application/json': { - example: { - error: { - message: 'Unauthorized.', - code: 'unauthorized', - status: 401, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - '404': { - description: 'Not found.', - content: { - 'application/json': { - example: { - error: { - message: 'Not found.', - code: 'not-found', - status: 404, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - }, - } as ZodOpenApiOperationObject, -}); - -const operation2 = defineOperation({ - method: 'get', - path: '/api/reports/{reportId}', - audience: 'public', - auth: 'bearer-or-share', - operation: { - operationId: 'getReport', - summary: 'Get reports report id', - tags: ['Reports'], - parameters: [ - { - name: 'reportId', - in: 'path', - required: true, - schema: { - type: 'string', - }, - }, - ], - responses: { - '200': { - description: 'The operation completed successfully.', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - id: { - type: 'string', - }, - userId: { - type: 'string', - }, - createdAt: { - type: 'string', - format: 'date-time', - }, - updatedAt: { - type: 'string', - format: 'date-time', - }, - name: { - type: 'string', - }, - type: { - type: 'string', - }, - description: { - type: 'string', - }, - parameters: { - anyOf: [ - { - type: 'string', - }, - { - type: 'number', - }, - { - const: false, - }, - { - const: true, - }, - { - type: 'object', - properties: {}, - additionalProperties: { - anyOf: [ - { - type: 'string', - }, - { - type: 'number', - }, - { - const: false, - }, - { - const: true, - }, - {}, - { - type: 'object', - properties: { - length: { - type: 'number', - }, - }, - required: ['length'], - }, - ], - }, - }, - { - type: 'object', - properties: { - length: { - type: 'number', - }, - }, - required: ['length'], - }, - ], - }, - websiteId: { - type: 'string', - }, - }, - required: [ - 'id', - 'userId', - 'createdAt', - 'updatedAt', - 'name', - 'type', - 'description', - 'parameters', - 'websiteId', - ], - }, - }, - }, - }, - '401': { - description: 'Unauthorized.', - content: { - 'application/json': { - example: { - error: { - message: 'Unauthorized.', - code: 'unauthorized', - status: 401, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - '404': { - description: 'Not found.', - content: { - 'application/json': { - example: { - error: { - message: 'Not found.', - code: 'not-found', - status: 404, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - }, - } as ZodOpenApiOperationObject, -}); - -const operation3 = defineOperation({ - method: 'post', - path: '/api/reports/{reportId}', - audience: 'public', - auth: 'bearer', - operation: { - operationId: 'updateReport', - summary: 'Create or update reports report id', - tags: ['Reports'], - parameters: [ - { - name: 'reportId', - in: 'path', - required: true, - schema: { - type: 'string', - }, - }, - ], - requestBody: { - required: true, - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - websiteId: { - type: 'string', - format: 'uuid', - }, - type: { - type: 'string', - }, - name: { - type: 'string', - maxLength: 200, - }, - description: { - type: 'string', - maxLength: 500, - }, - parameters: { - type: 'object', - additionalProperties: true, - }, - }, - required: ['websiteId', 'type', 'name', 'parameters'], - }, - }, - }, - }, - responses: { - '200': { - description: 'The operation completed successfully.', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - id: { - type: 'string', - }, - userId: { - type: 'string', - }, - createdAt: { - type: 'string', - format: 'date-time', - }, - updatedAt: { - type: 'string', - format: 'date-time', - }, - name: { - type: 'string', - }, - type: { - type: 'string', - }, - description: { - type: 'string', - }, - parameters: { - anyOf: [ - { - type: 'string', - }, - { - type: 'number', - }, - { - const: false, - }, - { - const: true, - }, - { - type: 'object', - properties: {}, - additionalProperties: { - anyOf: [ - { - type: 'string', - }, - { - type: 'number', - }, - { - const: false, - }, - { - const: true, - }, - {}, - { - type: 'object', - properties: { - length: { - type: 'number', - }, - }, - required: ['length'], - }, - ], - }, - }, - { - type: 'object', - properties: { - length: { - type: 'number', - }, - }, - required: ['length'], - }, - ], - }, - websiteId: { - type: 'string', - }, - }, - required: [ - 'id', - 'userId', - 'createdAt', - 'updatedAt', - 'name', - 'type', - 'description', - 'parameters', - 'websiteId', - ], - }, - }, - }, - }, - '400': { - description: 'Bad request.', - content: { - 'application/json': { - example: { - error: { - message: 'Bad request.', - code: 'bad-request', - status: 400, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - '401': { - description: 'Unauthorized.', - content: { - 'application/json': { - example: { - error: { - message: 'Unauthorized.', - code: 'unauthorized', - status: 401, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - '404': { - description: 'Not found.', - content: { - 'application/json': { - example: { - error: { - message: 'Not found.', - code: 'not-found', - status: 404, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - }, - } as ZodOpenApiOperationObject, -}); - -export const operations = [operation1, operation2, operation3] as const; diff --git a/src/app/api/reports/attribution/contract.generated.ts b/src/app/api/reports/attribution/contract.generated.ts deleted file mode 100644 index fa819302a..000000000 --- a/src/app/api/reports/attribution/contract.generated.ts +++ /dev/null @@ -1,224 +0,0 @@ -// Generated by `pnpm openapi:contracts`. Do not edit directly. - -import type { ZodOpenApiOperationObject } from 'zod-openapi'; -import { defineOperation } from '@/openapi/operation'; - -const operation1 = defineOperation({ - method: 'post', - path: '/api/reports/attribution', - audience: 'public', - auth: 'bearer-or-share', - operation: { - operationId: 'runAttributionReport', - summary: 'Create or update reports attribution', - tags: ['Reports'], - requestBody: { - required: true, - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - websiteId: { - type: 'string', - format: 'uuid', - }, - filters: { - type: 'object', - additionalProperties: true, - }, - type: { - type: 'string', - }, - parameters: { - type: 'object', - additionalProperties: true, - }, - }, - required: ['websiteId', 'filters', 'type', 'parameters'], - }, - }, - }, - }, - responses: { - '200': { - description: 'The operation completed successfully.', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - referrer: { - type: 'array', - items: { - type: 'object', - properties: { - name: { - type: 'string', - }, - value: { - type: 'number', - }, - }, - required: ['name', 'value'], - }, - }, - paidAds: { - type: 'array', - items: { - type: 'object', - properties: { - name: { - type: 'string', - }, - value: { - type: 'number', - }, - }, - required: ['name', 'value'], - }, - }, - utm_source: { - type: 'array', - items: { - type: 'object', - properties: { - name: { - type: 'string', - }, - value: { - type: 'number', - }, - }, - required: ['name', 'value'], - }, - }, - utm_medium: { - type: 'array', - items: { - type: 'object', - properties: { - name: { - type: 'string', - }, - value: { - type: 'number', - }, - }, - required: ['name', 'value'], - }, - }, - utm_campaign: { - type: 'array', - items: { - type: 'object', - properties: { - name: { - type: 'string', - }, - value: { - type: 'number', - }, - }, - required: ['name', 'value'], - }, - }, - utm_content: { - type: 'array', - items: { - type: 'object', - properties: { - name: { - type: 'string', - }, - value: { - type: 'number', - }, - }, - required: ['name', 'value'], - }, - }, - utm_term: { - type: 'array', - items: { - type: 'object', - properties: { - name: { - type: 'string', - }, - value: { - type: 'number', - }, - }, - required: ['name', 'value'], - }, - }, - total: { - type: 'object', - properties: { - pageviews: { - type: 'number', - }, - visitors: { - type: 'number', - }, - visits: { - type: 'number', - }, - }, - required: ['pageviews', 'visitors', 'visits'], - }, - }, - required: [ - 'referrer', - 'paidAds', - 'utm_source', - 'utm_medium', - 'utm_campaign', - 'utm_content', - 'utm_term', - 'total', - ], - }, - }, - }, - }, - '400': { - description: 'Bad request.', - content: { - 'application/json': { - example: { - error: { - message: 'Bad request.', - code: 'bad-request', - status: 400, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - '401': { - description: 'Unauthorized.', - content: { - 'application/json': { - example: { - error: { - message: 'Unauthorized.', - code: 'unauthorized', - status: 401, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - }, - } as ZodOpenApiOperationObject, -}); - -export const operations = [operation1] as const; diff --git a/src/app/api/reports/breakdown/contract.generated.ts b/src/app/api/reports/breakdown/contract.generated.ts deleted file mode 100644 index c020929f9..000000000 --- a/src/app/api/reports/breakdown/contract.generated.ts +++ /dev/null @@ -1,123 +0,0 @@ -// Generated by `pnpm openapi:contracts`. Do not edit directly. - -import type { ZodOpenApiOperationObject } from 'zod-openapi'; -import { defineOperation } from '@/openapi/operation'; - -const operation1 = defineOperation({ - method: 'post', - path: '/api/reports/breakdown', - audience: 'public', - auth: 'bearer-or-share', - operation: { - operationId: 'runBreakdownReport', - summary: 'Create or update reports breakdown', - tags: ['Reports'], - requestBody: { - required: true, - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - websiteId: { - type: 'string', - format: 'uuid', - }, - filters: { - type: 'object', - additionalProperties: true, - }, - type: { - type: 'string', - }, - parameters: { - type: 'object', - additionalProperties: true, - }, - }, - required: ['websiteId', 'filters', 'type', 'parameters'], - }, - }, - }, - }, - responses: { - '200': { - description: 'The operation completed successfully.', - content: { - 'application/json': { - schema: { - type: 'array', - items: { - type: 'object', - properties: { - views: { - type: 'number', - }, - visitors: { - type: 'number', - }, - visits: { - type: 'number', - }, - bounces: { - type: 'number', - }, - totaltime: { - type: 'number', - }, - }, - required: ['views', 'visitors', 'visits', 'bounces', 'totaltime'], - additionalProperties: { - anyOf: [ - { - type: 'string', - }, - { - type: 'number', - }, - ], - }, - }, - }, - }, - }, - }, - '400': { - description: 'Bad request.', - content: { - 'application/json': { - example: { - error: { - message: 'Bad request.', - code: 'bad-request', - status: 400, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - '401': { - description: 'Unauthorized.', - content: { - 'application/json': { - example: { - error: { - message: 'Unauthorized.', - code: 'unauthorized', - status: 401, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - }, - } as ZodOpenApiOperationObject, -}); - -export const operations = [operation1] as const; diff --git a/src/app/api/reports/contract.generated.ts b/src/app/api/reports/contract.generated.ts deleted file mode 100644 index 98997777f..000000000 --- a/src/app/api/reports/contract.generated.ts +++ /dev/null @@ -1,439 +0,0 @@ -// Generated by `pnpm openapi:contracts`. Do not edit directly. - -import type { ZodOpenApiOperationObject } from 'zod-openapi'; -import { defineOperation } from '@/openapi/operation'; - -const operation1 = defineOperation({ - method: 'get', - path: '/api/reports', - audience: 'public', - auth: 'bearer-or-share', - operation: { - operationId: 'getReports', - summary: 'Get reports', - tags: ['Reports'], - parameters: [ - { - name: 'websiteId', - in: 'query', - required: true, - schema: { - type: 'string', - format: 'uuid', - }, - }, - { - name: 'type', - in: 'query', - required: false, - schema: { - type: 'string', - }, - }, - { - name: 'page', - in: 'query', - required: false, - schema: { - type: 'integer', - minimum: 1, - }, - }, - { - name: 'pageSize', - in: 'query', - required: false, - schema: { - type: 'integer', - minimum: 1, - }, - }, - { - name: 'maxResults', - in: 'query', - required: false, - schema: { - type: 'integer', - minimum: 1, - }, - }, - ], - responses: { - '200': { - description: 'The operation completed successfully.', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - data: { - type: 'array', - items: { - allOf: [ - { - type: 'object', - properties: { - id: { - type: 'string', - }, - userId: { - type: 'string', - }, - createdAt: { - type: 'string', - format: 'date-time', - }, - updatedAt: { - type: 'string', - format: 'date-time', - }, - name: { - type: 'string', - }, - type: { - type: 'string', - }, - description: { - type: 'string', - }, - parameters: { - anyOf: [ - { - type: 'string', - }, - { - type: 'number', - }, - { - const: false, - }, - { - const: true, - }, - { - type: 'object', - properties: {}, - additionalProperties: { - anyOf: [ - { - type: 'string', - }, - { - type: 'number', - }, - { - const: false, - }, - { - const: true, - }, - {}, - { - type: 'object', - properties: { - length: { - type: 'number', - }, - }, - required: ['length'], - }, - ], - }, - }, - { - type: 'object', - properties: { - length: { - type: 'number', - }, - }, - required: ['length'], - }, - ], - }, - websiteId: { - type: 'string', - }, - }, - required: [ - 'id', - 'userId', - 'createdAt', - 'updatedAt', - 'name', - 'type', - 'description', - 'parameters', - 'websiteId', - ], - }, - { - type: 'object', - properties: { - website: { - type: 'object', - properties: { - domain: { - type: 'string', - }, - userId: { - type: 'string', - }, - }, - required: ['domain', 'userId'], - }, - }, - }, - ], - }, - }, - count: { - type: 'number', - }, - page: { - type: 'number', - }, - pageSize: { - type: 'number', - }, - orderBy: { - type: 'string', - }, - sortDescending: { - type: 'boolean', - }, - search: { - type: 'string', - }, - isCapped: { - type: 'boolean', - }, - }, - required: ['data', 'count', 'page', 'pageSize'], - }, - }, - }, - }, - '400': { - description: 'Bad request.', - content: { - 'application/json': { - example: { - error: { - message: 'Bad request.', - code: 'bad-request', - status: 400, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - '401': { - description: 'Unauthorized.', - content: { - 'application/json': { - example: { - error: { - message: 'Unauthorized.', - code: 'unauthorized', - status: 401, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - }, - } as ZodOpenApiOperationObject, -}); - -const operation2 = defineOperation({ - method: 'post', - path: '/api/reports', - audience: 'public', - auth: 'bearer', - operation: { - operationId: 'createReport', - summary: 'Create or update reports', - tags: ['Reports'], - requestBody: { - required: true, - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - websiteId: { - type: 'string', - format: 'uuid', - }, - type: { - type: 'string', - }, - name: { - type: 'string', - maxLength: 200, - }, - description: { - type: 'string', - maxLength: 500, - }, - parameters: { - type: 'object', - additionalProperties: true, - }, - }, - required: ['websiteId', 'type', 'name', 'parameters'], - }, - }, - }, - }, - responses: { - '200': { - description: 'The operation completed successfully.', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - id: { - type: 'string', - }, - userId: { - type: 'string', - }, - createdAt: { - type: 'string', - format: 'date-time', - }, - updatedAt: { - type: 'string', - format: 'date-time', - }, - name: { - type: 'string', - }, - type: { - type: 'string', - }, - description: { - type: 'string', - }, - parameters: { - anyOf: [ - { - type: 'string', - }, - { - type: 'number', - }, - { - const: false, - }, - { - const: true, - }, - { - type: 'object', - properties: {}, - additionalProperties: { - anyOf: [ - { - type: 'string', - }, - { - type: 'number', - }, - { - const: false, - }, - { - const: true, - }, - {}, - { - type: 'object', - properties: { - length: { - type: 'number', - }, - }, - required: ['length'], - }, - ], - }, - }, - { - type: 'object', - properties: { - length: { - type: 'number', - }, - }, - required: ['length'], - }, - ], - }, - websiteId: { - type: 'string', - }, - }, - required: [ - 'id', - 'userId', - 'createdAt', - 'updatedAt', - 'name', - 'type', - 'description', - 'parameters', - 'websiteId', - ], - }, - }, - }, - }, - '400': { - description: 'Bad request.', - content: { - 'application/json': { - example: { - error: { - message: 'Bad request.', - code: 'bad-request', - status: 400, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - '401': { - description: 'Unauthorized.', - content: { - 'application/json': { - example: { - error: { - message: 'Unauthorized.', - code: 'unauthorized', - status: 401, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - }, - } as ZodOpenApiOperationObject, -}); - -export const operations = [operation1, operation2] as const; diff --git a/src/app/api/reports/funnel/contract.generated.ts b/src/app/api/reports/funnel/contract.generated.ts deleted file mode 100644 index d4da8ce58..000000000 --- a/src/app/api/reports/funnel/contract.generated.ts +++ /dev/null @@ -1,145 +0,0 @@ -// Generated by `pnpm openapi:contracts`. Do not edit directly. - -import type { ZodOpenApiOperationObject } from 'zod-openapi'; -import { defineOperation } from '@/openapi/operation'; - -const operation1 = defineOperation({ - method: 'post', - path: '/api/reports/funnel', - audience: 'public', - auth: 'bearer-or-share', - operation: { - operationId: 'runFunnelReport', - summary: 'Create or update reports funnel', - tags: ['Reports'], - requestBody: { - required: true, - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - websiteId: { - type: 'string', - format: 'uuid', - }, - filters: { - type: 'object', - additionalProperties: true, - }, - type: { - type: 'string', - }, - parameters: { - type: 'object', - additionalProperties: true, - }, - }, - required: ['websiteId', 'filters', 'type', 'parameters'], - }, - }, - }, - }, - responses: { - '200': { - description: 'The operation completed successfully.', - content: { - 'application/json': { - schema: { - type: 'array', - items: { - type: 'object', - properties: { - visitors: { - type: 'number', - }, - previous: { - type: 'number', - }, - dropped: { - type: 'number', - }, - dropoff: { - type: 'number', - }, - remaining: { - type: 'number', - }, - type: { - type: 'string', - }, - value: { - type: 'string', - }, - filters: { - type: 'array', - items: { - type: 'object', - properties: { - property: { - type: 'string', - }, - operator: { - type: 'string', - }, - value: { - type: 'string', - }, - }, - required: ['property', 'operator', 'value'], - }, - }, - }, - required: [ - 'visitors', - 'previous', - 'dropped', - 'dropoff', - 'remaining', - 'type', - 'value', - ], - }, - }, - }, - }, - }, - '400': { - description: 'Bad request.', - content: { - 'application/json': { - example: { - error: { - message: 'Bad request.', - code: 'bad-request', - status: 400, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - '401': { - description: 'Unauthorized.', - content: { - 'application/json': { - example: { - error: { - message: 'Unauthorized.', - code: 'unauthorized', - status: 401, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - }, - } as ZodOpenApiOperationObject, -}); - -export const operations = [operation1] as const; diff --git a/src/app/api/reports/goal/contract.generated.ts b/src/app/api/reports/goal/contract.generated.ts deleted file mode 100644 index f0fd8532d..000000000 --- a/src/app/api/reports/goal/contract.generated.ts +++ /dev/null @@ -1,101 +0,0 @@ -// Generated by `pnpm openapi:contracts`. Do not edit directly. - -import type { ZodOpenApiOperationObject } from 'zod-openapi'; -import { defineOperation } from '@/openapi/operation'; - -const operation1 = defineOperation({ - method: 'post', - path: '/api/reports/goal', - audience: 'public', - auth: 'bearer-or-share', - operation: { - operationId: 'runGoalReport', - summary: 'Create or update reports goal', - tags: ['Reports'], - requestBody: { - required: true, - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - websiteId: { - type: 'string', - format: 'uuid', - }, - filters: { - type: 'object', - additionalProperties: true, - }, - type: { - type: 'string', - }, - parameters: { - type: 'object', - additionalProperties: true, - }, - }, - required: ['websiteId', 'filters', 'type', 'parameters'], - }, - }, - }, - }, - responses: { - '200': { - description: 'The operation completed successfully.', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - num: { - type: 'number', - }, - total: { - type: 'number', - }, - }, - required: ['num', 'total'], - }, - }, - }, - }, - '400': { - description: 'Bad request.', - content: { - 'application/json': { - example: { - error: { - message: 'Bad request.', - code: 'bad-request', - status: 400, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - '401': { - description: 'Unauthorized.', - content: { - 'application/json': { - example: { - error: { - message: 'Unauthorized.', - code: 'unauthorized', - status: 401, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - }, - } as ZodOpenApiOperationObject, -}); - -export const operations = [operation1] as const; diff --git a/src/app/api/reports/heatmap/contract.generated.ts b/src/app/api/reports/heatmap/contract.generated.ts deleted file mode 100644 index 5bb363169..000000000 --- a/src/app/api/reports/heatmap/contract.generated.ts +++ /dev/null @@ -1,251 +0,0 @@ -// Generated by `pnpm openapi:contracts`. Do not edit directly. - -import type { ZodOpenApiOperationObject } from 'zod-openapi'; -import { defineOperation } from '@/openapi/operation'; - -const operation1 = defineOperation({ - method: 'post', - path: '/api/reports/heatmap', - audience: 'public', - auth: 'bearer-or-share', - operation: { - operationId: 'runHeatmapReport', - summary: 'Create or update reports heatmap', - tags: ['Reports'], - requestBody: { - required: true, - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - websiteId: { - type: 'string', - format: 'uuid', - }, - filters: { - type: 'object', - additionalProperties: true, - }, - type: { - type: 'string', - }, - parameters: { - type: 'object', - additionalProperties: true, - }, - }, - required: ['websiteId', 'filters', 'type', 'parameters'], - }, - }, - }, - }, - responses: { - '200': { - description: 'The operation completed successfully.', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - mode: { - anyOf: [ - { - const: 'click', - }, - { - const: 'scroll', - }, - ], - }, - pages: { - type: 'array', - items: { - type: 'object', - properties: { - urlPath: { - type: 'string', - }, - count: { - type: 'number', - }, - sessions: { - type: 'number', - }, - }, - required: ['urlPath', 'count', 'sessions'], - }, - }, - points: { - type: 'array', - items: { - type: 'object', - properties: { - x: { - type: 'number', - }, - y: { - type: 'number', - }, - pageX: { - type: 'number', - }, - pageY: { - type: 'number', - }, - pageW: { - type: 'number', - }, - pageH: { - type: 'number', - }, - viewportW: { - type: 'number', - }, - viewportH: { - type: 'number', - }, - count: { - type: 'number', - }, - }, - required: [ - 'x', - 'y', - 'pageX', - 'pageY', - 'pageW', - 'pageH', - 'viewportW', - 'viewportH', - 'count', - ], - }, - }, - snapshot: { - type: 'object', - properties: { - kind: { - const: 'iframe', - }, - id: { - type: 'string', - }, - url: { - type: 'string', - }, - pageW: { - type: 'number', - }, - pageH: { - type: 'number', - }, - viewportW: { - type: 'number', - }, - viewportH: { - type: 'number', - }, - }, - required: ['kind', 'id', 'url', 'pageW', 'pageH', 'viewportW', 'viewportH'], - }, - scroll: { - type: 'object', - properties: { - buckets: { - type: 'array', - items: { - type: 'object', - properties: { - depth: { - type: 'number', - }, - sessions: { - type: 'number', - }, - pageW: { - type: 'number', - }, - pageH: { - type: 'number', - }, - viewportW: { - type: 'number', - }, - viewportH: { - type: 'number', - }, - }, - required: ['depth', 'sessions', 'pageW', 'pageH', 'viewportW', 'viewportH'], - }, - }, - totalSessions: { - type: 'number', - }, - pageW: { - type: 'number', - }, - pageH: { - type: 'number', - }, - viewportW: { - type: 'number', - }, - viewportH: { - type: 'number', - }, - }, - required: [ - 'buckets', - 'totalSessions', - 'pageW', - 'pageH', - 'viewportW', - 'viewportH', - ], - }, - }, - required: ['mode', 'pages', 'points', 'snapshot', 'scroll'], - }, - }, - }, - }, - '400': { - description: 'Bad request.', - content: { - 'application/json': { - example: { - error: { - message: 'Bad request.', - code: 'bad-request', - status: 400, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - '401': { - description: 'Unauthorized.', - content: { - 'application/json': { - example: { - error: { - message: 'Unauthorized.', - code: 'unauthorized', - status: 401, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - }, - } as ZodOpenApiOperationObject, -}); - -export const operations = [operation1] as const; diff --git a/src/app/api/reports/journey/contract.generated.ts b/src/app/api/reports/journey/contract.generated.ts deleted file mode 100644 index cbc0fb7bd..000000000 --- a/src/app/api/reports/journey/contract.generated.ts +++ /dev/null @@ -1,122 +0,0 @@ -// Generated by `pnpm openapi:contracts`. Do not edit directly. - -import type { ZodOpenApiOperationObject } from 'zod-openapi'; -import { defineOperation } from '@/openapi/operation'; - -const operation1 = defineOperation({ - method: 'post', - path: '/api/reports/journey', - audience: 'public', - auth: 'bearer-or-share', - operation: { - operationId: 'runJourneyReport', - summary: 'Create or update reports journey', - tags: ['Reports'], - requestBody: { - required: true, - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - websiteId: { - type: 'string', - format: 'uuid', - }, - filters: { - type: 'object', - additionalProperties: true, - }, - type: { - type: 'string', - }, - parameters: { - type: 'object', - additionalProperties: true, - }, - }, - required: ['websiteId', 'filters', 'type', 'parameters'], - }, - }, - }, - }, - responses: { - '200': { - description: 'The operation completed successfully.', - content: { - 'application/json': { - schema: { - type: 'array', - items: { - type: 'object', - properties: { - e1: { - type: 'string', - }, - e2: { - type: 'string', - }, - e3: { - type: 'string', - }, - e4: { - type: 'string', - }, - e5: { - type: 'string', - }, - e6: { - type: 'string', - }, - e7: { - type: 'string', - }, - count: { - type: 'number', - }, - }, - required: ['e1', 'e2', 'e3', 'e4', 'e5', 'e6', 'e7', 'count'], - }, - }, - }, - }, - }, - '400': { - description: 'Bad request.', - content: { - 'application/json': { - example: { - error: { - message: 'Bad request.', - code: 'bad-request', - status: 400, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - '401': { - description: 'Unauthorized.', - content: { - 'application/json': { - example: { - error: { - message: 'Unauthorized.', - code: 'unauthorized', - status: 401, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - }, - } as ZodOpenApiOperationObject, -}); - -export const operations = [operation1] as const; diff --git a/src/app/api/reports/performance/contract.generated.ts b/src/app/api/reports/performance/contract.generated.ts deleted file mode 100644 index acd5bf742..000000000 --- a/src/app/api/reports/performance/contract.generated.ts +++ /dev/null @@ -1,101 +0,0 @@ -// Generated by `pnpm openapi:contracts`. Do not edit directly. - -import type { ZodOpenApiOperationObject } from 'zod-openapi'; -import { defineOperation } from '@/openapi/operation'; - -const operation1 = defineOperation({ - method: 'post', - path: '/api/reports/performance', - audience: 'public', - auth: 'bearer-or-share', - operation: { - operationId: 'runPerformanceReport', - summary: 'Create or update reports performance', - tags: ['Reports'], - requestBody: { - required: true, - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - websiteId: { - type: 'string', - format: 'uuid', - }, - filters: { - type: 'object', - additionalProperties: true, - }, - type: { - type: 'string', - }, - parameters: { - type: 'object', - additionalProperties: true, - }, - }, - required: ['websiteId', 'filters', 'type', 'parameters'], - }, - }, - }, - }, - responses: { - '200': { - description: 'The operation completed successfully.', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - chart: {}, - summary: {}, - pages: {}, - pageTitles: {}, - devices: {}, - browsers: {}, - }, - required: ['chart', 'summary', 'pages', 'pageTitles', 'devices', 'browsers'], - }, - }, - }, - }, - '400': { - description: 'Bad request.', - content: { - 'application/json': { - example: { - error: { - message: 'Bad request.', - code: 'bad-request', - status: 400, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - '401': { - description: 'Unauthorized.', - content: { - 'application/json': { - example: { - error: { - message: 'Unauthorized.', - code: 'unauthorized', - status: 401, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - }, - } as ZodOpenApiOperationObject, -}); - -export const operations = [operation1] as const; diff --git a/src/app/api/reports/retention/contract.generated.ts b/src/app/api/reports/retention/contract.generated.ts deleted file mode 100644 index 0027df323..000000000 --- a/src/app/api/reports/retention/contract.generated.ts +++ /dev/null @@ -1,113 +0,0 @@ -// Generated by `pnpm openapi:contracts`. Do not edit directly. - -import type { ZodOpenApiOperationObject } from 'zod-openapi'; -import { defineOperation } from '@/openapi/operation'; - -const operation1 = defineOperation({ - method: 'post', - path: '/api/reports/retention', - audience: 'public', - auth: 'bearer-or-share', - operation: { - operationId: 'runRetentionReport', - summary: 'Create or update reports retention', - tags: ['Reports'], - requestBody: { - required: true, - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - websiteId: { - type: 'string', - format: 'uuid', - }, - filters: { - type: 'object', - additionalProperties: true, - }, - type: { - type: 'string', - }, - parameters: { - type: 'object', - additionalProperties: true, - }, - }, - required: ['websiteId', 'filters', 'type', 'parameters'], - }, - }, - }, - }, - responses: { - '200': { - description: 'The operation completed successfully.', - content: { - 'application/json': { - schema: { - type: 'array', - items: { - type: 'object', - properties: { - date: { - type: 'string', - }, - day: { - type: 'number', - }, - visitors: { - type: 'number', - }, - returnVisitors: { - type: 'number', - }, - percentage: { - type: 'number', - }, - }, - required: ['date', 'day', 'visitors', 'returnVisitors', 'percentage'], - }, - }, - }, - }, - }, - '400': { - description: 'Bad request.', - content: { - 'application/json': { - example: { - error: { - message: 'Bad request.', - code: 'bad-request', - status: 400, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - '401': { - description: 'Unauthorized.', - content: { - 'application/json': { - example: { - error: { - message: 'Unauthorized.', - code: 'unauthorized', - status: 401, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - }, - } as ZodOpenApiOperationObject, -}); - -export const operations = [operation1] as const; diff --git a/src/app/api/reports/revenue/contract.generated.ts b/src/app/api/reports/revenue/contract.generated.ts deleted file mode 100644 index e9af7cb70..000000000 --- a/src/app/api/reports/revenue/contract.generated.ts +++ /dev/null @@ -1,180 +0,0 @@ -// Generated by `pnpm openapi:contracts`. Do not edit directly. - -import type { ZodOpenApiOperationObject } from 'zod-openapi'; -import { defineOperation } from '@/openapi/operation'; - -const operation1 = defineOperation({ - method: 'post', - path: '/api/reports/revenue', - audience: 'public', - auth: 'bearer-or-share', - operation: { - operationId: 'runRevenueReport', - summary: 'Create or update reports revenue', - tags: ['Reports'], - requestBody: { - required: true, - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - websiteId: { - type: 'string', - format: 'uuid', - }, - filters: { - type: 'object', - additionalProperties: true, - }, - type: { - type: 'string', - }, - parameters: { - type: 'object', - additionalProperties: true, - }, - }, - required: ['websiteId', 'filters', 'type', 'parameters'], - }, - }, - }, - }, - responses: { - '200': { - description: 'The operation completed successfully.', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - chart: { - type: 'array', - items: { - type: 'object', - properties: { - x: { - type: 'string', - }, - t: { - type: 'string', - }, - y: { - type: 'number', - }, - count: { - type: 'number', - }, - }, - required: ['x', 't', 'y', 'count'], - }, - }, - total: {}, - country: { - type: 'array', - items: { - type: 'object', - properties: { - name: { - type: 'string', - }, - value: { - type: 'number', - }, - }, - required: ['name', 'value'], - }, - }, - region: { - type: 'array', - items: { - type: 'object', - properties: { - name: { - type: 'string', - }, - value: { - type: 'number', - }, - country: { - type: 'string', - }, - }, - required: ['name', 'value', 'country'], - }, - }, - referrer: { - type: 'array', - items: { - type: 'object', - properties: { - name: { - type: 'string', - }, - value: { - type: 'number', - }, - }, - required: ['name', 'value'], - }, - }, - channel: { - type: 'array', - items: { - type: 'object', - properties: { - name: { - type: 'string', - }, - value: { - type: 'number', - }, - }, - required: ['name', 'value'], - }, - }, - }, - required: ['chart', 'total', 'country', 'region', 'referrer', 'channel'], - }, - }, - }, - }, - '400': { - description: 'Bad request.', - content: { - 'application/json': { - example: { - error: { - message: 'Bad request.', - code: 'bad-request', - status: 400, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - '401': { - description: 'Unauthorized.', - content: { - 'application/json': { - example: { - error: { - message: 'Unauthorized.', - code: 'unauthorized', - status: 401, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - }, - } as ZodOpenApiOperationObject, -}); - -export const operations = [operation1] as const; diff --git a/src/app/api/reports/utm/contract.generated.ts b/src/app/api/reports/utm/contract.generated.ts deleted file mode 100644 index b2e277975..000000000 --- a/src/app/api/reports/utm/contract.generated.ts +++ /dev/null @@ -1,115 +0,0 @@ -// Generated by `pnpm openapi:contracts`. Do not edit directly. - -import type { ZodOpenApiOperationObject } from 'zod-openapi'; -import { defineOperation } from '@/openapi/operation'; - -const operation1 = defineOperation({ - method: 'post', - path: '/api/reports/utm', - audience: 'public', - auth: 'bearer-or-share', - operation: { - operationId: 'runUtmReport', - summary: 'Create or update reports utm', - tags: ['Reports'], - requestBody: { - required: true, - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - websiteId: { - type: 'string', - format: 'uuid', - }, - filters: { - type: 'object', - additionalProperties: true, - }, - type: { - type: 'string', - }, - parameters: { - type: 'object', - additionalProperties: true, - }, - }, - required: ['websiteId', 'filters', 'type', 'parameters'], - }, - }, - }, - }, - responses: { - '200': { - description: 'The operation completed successfully.', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - utm_source: { - type: 'array', - items: {}, - }, - utm_medium: { - type: 'array', - items: {}, - }, - utm_campaign: { - type: 'array', - items: {}, - }, - utm_term: { - type: 'array', - items: {}, - }, - utm_content: { - type: 'array', - items: {}, - }, - }, - required: ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'], - }, - }, - }, - }, - '400': { - description: 'Bad request.', - content: { - 'application/json': { - example: { - error: { - message: 'Bad request.', - code: 'bad-request', - status: 400, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - '401': { - description: 'Unauthorized.', - content: { - 'application/json': { - example: { - error: { - message: 'Unauthorized.', - code: 'unauthorized', - status: 401, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - }, - } as ZodOpenApiOperationObject, -}); - -export const operations = [operation1] as const; diff --git a/src/app/api/websites/[websiteId]/reports/contract.generated.ts b/src/app/api/websites/[websiteId]/reports/contract.generated.ts deleted file mode 100644 index 8c03eb566..000000000 --- a/src/app/api/websites/[websiteId]/reports/contract.generated.ts +++ /dev/null @@ -1,254 +0,0 @@ -// Generated by `pnpm openapi:contracts`. Do not edit directly. - -import type { ZodOpenApiOperationObject } from 'zod-openapi'; -import { defineOperation } from '@/openapi/operation'; - -const operation1 = defineOperation({ - method: 'get', - path: '/api/websites/{websiteId}/reports', - audience: 'public', - auth: 'bearer-or-share', - operation: { - operationId: 'getWebsiteReports', - summary: 'Get websites website id reports', - tags: ['Websites'], - parameters: [ - { - name: 'websiteId', - in: 'path', - required: true, - schema: { - type: 'string', - }, - }, - { - name: 'type', - in: 'query', - required: false, - schema: { - type: 'string', - }, - }, - { - name: 'page', - in: 'query', - required: false, - schema: { - type: 'integer', - minimum: 1, - }, - }, - { - name: 'pageSize', - in: 'query', - required: false, - schema: { - type: 'integer', - minimum: 1, - }, - }, - { - name: 'maxResults', - in: 'query', - required: false, - schema: { - type: 'integer', - minimum: 1, - }, - }, - ], - responses: { - '200': { - description: 'The operation completed successfully.', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - data: { - type: 'array', - items: { - allOf: [ - { - type: 'object', - properties: { - id: { - type: 'string', - }, - userId: { - type: 'string', - }, - createdAt: { - type: 'string', - format: 'date-time', - }, - updatedAt: { - type: 'string', - format: 'date-time', - }, - name: { - type: 'string', - }, - type: { - type: 'string', - }, - description: { - type: 'string', - }, - parameters: { - anyOf: [ - { - type: 'string', - }, - { - type: 'number', - }, - { - const: false, - }, - { - const: true, - }, - { - type: 'object', - properties: {}, - additionalProperties: { - anyOf: [ - { - type: 'string', - }, - { - type: 'number', - }, - { - const: false, - }, - { - const: true, - }, - {}, - { - type: 'object', - properties: { - length: { - type: 'number', - }, - }, - required: ['length'], - }, - ], - }, - }, - { - type: 'object', - properties: { - length: { - type: 'number', - }, - }, - required: ['length'], - }, - ], - }, - websiteId: { - type: 'string', - }, - }, - required: [ - 'id', - 'userId', - 'createdAt', - 'updatedAt', - 'name', - 'type', - 'description', - 'parameters', - 'websiteId', - ], - }, - { - type: 'object', - properties: { - website: { - type: 'object', - properties: { - domain: { - type: 'string', - }, - userId: { - type: 'string', - }, - }, - required: ['domain', 'userId'], - }, - }, - }, - ], - }, - }, - count: { - type: 'number', - }, - page: { - type: 'number', - }, - pageSize: { - type: 'number', - }, - orderBy: { - type: 'string', - }, - sortDescending: { - type: 'boolean', - }, - search: { - type: 'string', - }, - isCapped: { - type: 'boolean', - }, - }, - required: ['data', 'count', 'page', 'pageSize'], - }, - }, - }, - }, - '400': { - description: 'Bad request.', - content: { - 'application/json': { - example: { - error: { - message: 'Bad request.', - code: 'bad-request', - status: 400, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - '401': { - description: 'Unauthorized.', - content: { - 'application/json': { - example: { - error: { - message: 'Unauthorized.', - code: 'unauthorized', - status: 401, - }, - }, - schema: { - $ref: '#/components/schemas/ApiError', - }, - }, - }, - }, - }, - } as ZodOpenApiOperationObject, -}); - -export const operations = [operation1] as const; diff --git a/src/openapi/compatibility-routing.test.ts b/src/openapi/compatibility-routing.test.ts new file mode 100644 index 000000000..4c77a66f5 --- /dev/null +++ b/src/openapi/compatibility-routing.test.ts @@ -0,0 +1,57 @@ +import { + getRewrittenUrl, + isRewrite, + unstable_getResponseFromNextConfig, +} from 'next/experimental/testing/server'; +import { describe, expect, test } from 'vitest'; +import nextConfig from '../../next.config'; + +describe('legacy report compatibility routing', () => { + test.each([ + '/api/reports', + '/api/reports/saved-report-id', + ...[ + 'attribution', + 'breakdown', + 'funnel', + 'goal', + 'heatmap', + 'journey', + 'performance', + 'retention', + 'revenue', + 'utm', + ].map(type => `/api/reports/${type}`), + '/api/websites/website-id/reports', + ])('rewrites %s while preserving query parameters', async path => { + const query = '?websiteId=website-id&type=funnel&page=2'; + const response = await unstable_getResponseFromNextConfig({ + url: `https://example.com${path}${query}`, + nextConfig: { ...nextConfig, basePath: '' }, + }); + + expect(isRewrite(response)).toBe(true); + expect(response.headers.get('location')).toBeNull(); + expect(getRewrittenUrl(response)).toBe(`https://example.com/compat${path}${query}`); + }); + + test('leaves feature routes unchanged', async () => { + const response = await unstable_getResponseFromNextConfig({ + url: 'https://example.com/api/websites/website-id/funnels', + nextConfig: { ...nextConfig, basePath: '' }, + }); + + expect(isRewrite(response)).toBe(false); + }); + + test('preserves a configured base path', async () => { + const response = await unstable_getResponseFromNextConfig({ + url: 'https://example.com/umami/api/reports/funnel?websiteId=website-id', + nextConfig: { ...nextConfig, basePath: '/umami' }, + }); + + expect(getRewrittenUrl(response)).toBe( + 'https://example.com/umami/compat/api/reports/funnel?websiteId=website-id', + ); + }); +}); diff --git a/src/openapi/document.ts b/src/openapi/document.ts index b7f761d45..f048faf59 100644 --- a/src/openapi/document.ts +++ b/src/openapi/document.ts @@ -12,7 +12,6 @@ import { applyFieldDescriptions } from '@/openapi/field-descriptions'; import { inferApiContracts } from '@/openapi/infer'; import { type ApiAudience, getOperationKey } from '@/openapi/operation'; import { operationDescriptions } from '@/openapi/operation-descriptions'; -import { getReportDeprecation } from '@/openapi/report-deprecations'; import { errorResponseComponents } from '@/openapi/schemas'; import { getSecurityRequirements, securitySchemes } from '@/openapi/security'; @@ -87,16 +86,9 @@ export async function buildOpenApiDocument( paths[contract.path] = pathItem; } - const deprecation = getReportDeprecation(contract.method, contract.path); - const description = - operationDescriptions[getOperationKey(contract)]?.description ?? - contract.operation.description; const operation = { ...contract.operation, ...operationDescriptions[getOperationKey(contract)], - ...(deprecation - ? { deprecated: true, description: [description, deprecation].filter(Boolean).join('\n\n') } - : {}), security: getSecurityRequirements(contract.auth), 'x-umami-audience': contract.audience, 'x-umami-contract': contract.origin, diff --git a/src/openapi/operation-descriptions.ts b/src/openapi/operation-descriptions.ts index d9c25b924..b4fe0501a 100644 --- a/src/openapi/operation-descriptions.ts +++ b/src/openapi/operation-descriptions.ts @@ -277,78 +277,6 @@ export const operationDescriptions: Record = { description: 'Stores session replay events or heatmap clicks and scrolls for a website, using a valid tracking cache token to identify the session and visit.', }, - 'GET /api/reports': { - summary: 'List saved reports', - description: - 'Returns a paginated list of saved reports for the requested website, optionally filtered by report type.', - }, - 'POST /api/reports': { - summary: 'Save a report', - description: - "Saves a report's name, description, type, and parameters for a website so it can be opened again later.", - }, - 'DELETE /api/reports/{reportId}': { - summary: 'Delete a saved report', - description: 'Deletes the specified saved report definition.', - }, - 'GET /api/reports/{reportId}': { - summary: 'Get a saved report', - description: "Returns the specified report's saved name, description, type, and parameters.", - }, - 'POST /api/reports/{reportId}': { - summary: 'Update a saved report', - description: "Updates the specified report's website, name, description, type, and parameters.", - }, - 'POST /api/reports/attribution': { - summary: 'Run an attribution report', - description: - 'Calculates how traffic sources contribute to conversions using the supplied attribution settings, date range, and filters.', - }, - 'POST /api/reports/breakdown': { - summary: 'Run a breakdown report', - description: - 'Groups website activity by the selected dimensions for the requested date range and filters.', - }, - 'POST /api/reports/funnel': { - summary: 'Run a funnel report', - description: - 'Calculates visitor progression through a sequence of pages or events using the supplied funnel steps and filters.', - }, - 'POST /api/reports/goal': { - summary: 'Run a goal report', - description: - 'Counts visitors who reached a matching page or triggered a matching event and returns the total visitor count for comparison.', - }, - 'POST /api/reports/heatmap': { - summary: 'Get page heatmap data', - description: - 'Returns recorded click or scroll data for the selected page and date range to display as a heatmap.', - }, - 'POST /api/reports/journey': { - summary: 'Run a visitor journey report', - description: - 'Returns paths through pages or events using the supplied journey settings and website filters.', - }, - 'POST /api/reports/performance': { - summary: 'Run a performance report', - description: - 'Returns performance trends, summary metrics, and breakdowns by page, page title, device, and browser for the selected website and date range.', - }, - 'POST /api/reports/retention': { - summary: 'Run a retention report', - description: - 'Calculates how groups of visitors return over time using the supplied retention settings, date range, and filters.', - }, - 'POST /api/reports/revenue': { - summary: 'Run a revenue report', - description: - 'Returns revenue trends, totals with a comparison period, and breakdowns by country, region, referrer, and channel.', - }, - 'POST /api/reports/utm': { - summary: 'Run a campaign report', - description: - 'Returns traffic breakdowns for UTM source, medium, campaign, term, and content using the supplied date range and filters.', - }, 'GET /api/scripts/telemetry': { summary: 'Get the installation telemetry script', description: @@ -661,11 +589,6 @@ export const operationDescriptions: Record = { description: "Adds a replay to the website's saved replays with an optional name, or removes it when isSaved is false.", }, - 'GET /api/websites/{websiteId}/reports': { - summary: "List a website's saved reports", - description: - 'Returns a paginated list of saved report definitions for the website, optionally filtered by report type.', - }, 'POST /api/websites/{websiteId}/reset': { summary: 'Reset website analytics', description: diff --git a/src/openapi/operation-ids.ts b/src/openapi/operation-ids.ts index cf6ebabe1..5974d4b56 100644 --- a/src/openapi/operation-ids.ts +++ b/src/openapi/operation-ids.ts @@ -34,21 +34,6 @@ export const OPERATION_ID_OVERRIDES: Record = { 'GET /api/realtime/{websiteId}': 'getRealtime', // Reports - 'GET /api/reports': 'getReports', - 'POST /api/reports': 'createReport', - 'GET /api/reports/{reportId}': 'getReport', - 'POST /api/reports/{reportId}': 'updateReport', - 'DELETE /api/reports/{reportId}': 'deleteReport', - 'POST /api/reports/attribution': 'runAttributionReport', - 'POST /api/reports/breakdown': 'runBreakdownReport', - 'POST /api/reports/funnel': 'runFunnelReport', - 'POST /api/reports/goal': 'runGoalReport', - 'POST /api/reports/heatmap': 'runHeatmapReport', - 'POST /api/reports/journey': 'runJourneyReport', - 'POST /api/reports/performance': 'runPerformanceReport', - 'POST /api/reports/retention': 'runRetentionReport', - 'POST /api/reports/revenue': 'runRevenueReport', - 'POST /api/reports/utm': 'runUtmReport', // Teams 'POST /api/teams': 'createTeam', @@ -118,7 +103,6 @@ export const OPERATION_ID_OVERRIDES: Record = { 'GET /api/websites/{websiteId}/export': 'exportWebsite', 'POST /api/websites/{websiteId}/reset': 'resetWebsite', 'POST /api/websites/{websiteId}/transfer': 'transferWebsite', - 'GET /api/websites/{websiteId}/reports': 'getWebsiteReports', 'GET /api/websites/{websiteId}/shares': 'getWebsiteShares', 'POST /api/websites/{websiteId}/shares': 'createWebsiteShare', 'GET /api/websites/{websiteId}/recorder': 'getWebsiteRecorderConfig', diff --git a/src/openapi/report-deprecations.test.ts b/src/openapi/report-deprecations.test.ts deleted file mode 100644 index a35846668..000000000 --- a/src/openapi/report-deprecations.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { expect, test } from 'vitest'; -import { getReportDeprecation } from './report-deprecations'; - -test('marks all legacy calculation and persistence routes with replacements', () => { - for (const type of [ - 'attribution', - 'breakdown', - 'funnel', - 'goal', - 'heatmap', - 'journey', - 'performance', - 'retention', - 'revenue', - 'utm', - ]) { - expect(getReportDeprecation('post', `/api/reports/${type}`)).toContain( - 'GET /api/websites/{websiteId}/', - ); - } - expect(getReportDeprecation('get', '/api/reports')).toContain('persisted-data audit'); - expect(getReportDeprecation('delete', '/api/reports/{reportId}')).toContain( - 'Existing IDs are preserved', - ); - expect(getReportDeprecation('get', '/api/websites/{websiteId}/reports')).toContain( - 'no removal date', - ); -}); - -test('does not deprecate the replacement feature APIs', () => { - expect(getReportDeprecation('get', '/api/websites/{websiteId}/funnels/stats')).toBeUndefined(); - expect(getReportDeprecation('post', '/api/websites/{websiteId}/funnels')).toBeUndefined(); - expect(getReportDeprecation('get', '/api/websites/{websiteId}/revenue/stats')).toBeUndefined(); -}); diff --git a/src/openapi/report-deprecations.ts b/src/openapi/report-deprecations.ts deleted file mode 100644 index 49b7baa93..000000000 --- a/src/openapi/report-deprecations.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** Keep the legacy surface discoverable until an announced breaking release. */ -const calculations: Record = { - attribution: 'GET /api/websites/{websiteId}/attribution', - breakdown: 'GET /api/websites/{websiteId}/breakdown', - funnel: 'GET /api/websites/{websiteId}/funnels/stats or /funnels/{funnelId}/stats', - goal: 'GET /api/websites/{websiteId}/goals/stats or /goals/{goalId}/stats', - heatmap: 'GET /api/websites/{websiteId}/heatmaps', - journey: 'GET /api/websites/{websiteId}/journeys', - performance: - 'GET /api/websites/{websiteId}/performance/stats, /performance/chart, and /performance/metrics', - retention: 'GET /api/websites/{websiteId}/retention', - revenue: 'GET /api/websites/{websiteId}/revenue/stats, /revenue/chart, and /revenue/metrics', - utm: 'GET /api/websites/{websiteId}/utm/metrics for each UTM dimension', -}; - -export function getReportDeprecation(method: string, path: string): string | undefined { - const feature = path.startsWith('/api/reports/') ? path.slice('/api/reports/'.length) : ''; - const replacement = method === 'post' ? calculations[feature] : undefined; - if (replacement) { - return `Deprecated: use ${replacement}. The legacy request and response remain supported during migration.`; - } - if ( - ['/api/reports', '/api/reports/{reportId}', '/api/websites/{websiteId}/reports'].includes(path) - ) { - return 'Deprecated: use website-scoped /funnels or /goals resources for saved definitions. Existing IDs are preserved. Other legacy report types remain accessible here pending a persisted-data audit; no removal date has been scheduled.'; - } -}