diff --git a/.github/workflows/copy-clickhouse-udfs.yml b/.github/workflows/copy-clickhouse-udfs.yml index c6862b0345c67..3dc6fce3ade07 100644 --- a/.github/workflows/copy-clickhouse-udfs.yml +++ b/.github/workflows/copy-clickhouse-udfs.yml @@ -1,21 +1,20 @@ name: Trigger UDFs Workflow on: - push: - branches: - - master - paths: - - 'posthog/user_scripts/**' + push: + branches: + - master + paths: + - 'posthog/user_scripts/**' jobs: - trigger_udfs_workflow: - runs-on: ubuntu-latest - steps: - - name: Trigger UDFs Workflow - uses: benc-uk/workflow-dispatch@v1 - with: - workflow: .github/workflows/clickhouse-udfs.yml - repo: posthog/posthog-cloud-infra - token: ${{ secrets.POSTHOG_BOT_GITHUB_TOKEN }} - ref: refs/heads/main - + trigger_udfs_workflow: + runs-on: ubuntu-latest + steps: + - name: Trigger UDFs Workflow + uses: benc-uk/workflow-dispatch@v1 + with: + workflow: .github/workflows/clickhouse-udfs.yml + repo: posthog/posthog-cloud-infra + token: ${{ secrets.POSTHOG_BOT_GITHUB_TOKEN }} + ref: refs/heads/main diff --git a/.storybook/decorators/withKea/kea-story.tsx b/.storybook/decorators/withKea/kea-story.tsx index 3525a4befbe0d..0e04d991a82fb 100644 --- a/.storybook/decorators/withKea/kea-story.tsx +++ b/.storybook/decorators/withKea/kea-story.tsx @@ -8,6 +8,7 @@ import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { worker } from '~/mocks/browser' import { teamLogic } from 'scenes/teamLogic' import { userLogic } from 'scenes/userLogic' +import { projectLogic } from 'scenes/projectLogic' export function resetKeaStory(): void { worker.resetHandlers() @@ -18,6 +19,7 @@ export function resetKeaStory(): void { initKea({ routerLocation: history.location, routerHistory: history }) featureFlagLogic.mount() teamLogic.mount() + projectLogic.mount() userLogic.mount() router.mount() const { store } = getContext() diff --git a/cypress/e2e/dashboard-duplication.ts b/cypress/e2e/dashboard-duplication.ts index 75c1dea0c3998..e19bbd1ad046f 100644 --- a/cypress/e2e/dashboard-duplication.ts +++ b/cypress/e2e/dashboard-duplication.ts @@ -7,7 +7,7 @@ describe('duplicating dashboards', () => { let dashboardName, insightName, expectedCopiedDashboardName, expectedCopiedInsightName beforeEach(() => { - cy.intercept('POST', /\/api\/projects\/\d+\/dashboards/).as('createDashboard') + cy.intercept('POST', /\/api\/environments\/\d+\/dashboards/).as('createDashboard') dashboardName = randomString('dashboard-') expectedCopiedDashboardName = `${dashboardName} (Copy)` diff --git a/cypress/e2e/dashboard-shared.cy.ts b/cypress/e2e/dashboard-shared.cy.ts index 4e46554160424..755297f5c52dd 100644 --- a/cypress/e2e/dashboard-shared.cy.ts +++ b/cypress/e2e/dashboard-shared.cy.ts @@ -2,9 +2,9 @@ import { dashboards } from '../productAnalytics' describe('Shared dashboard', () => { beforeEach(() => { - cy.intercept('GET', /api\/projects\/\d+\/insights\/\?.*/).as('loadInsightList') - cy.intercept('PATCH', /api\/projects\/\d+\/insights\/\d+\/.*/).as('patchInsight') - cy.intercept('POST', /\/api\/projects\/\d+\/dashboards/).as('createDashboard') + cy.intercept('GET', /api\/environments\/\d+\/insights\/\?.*/).as('loadInsightList') + cy.intercept('PATCH', /api\/environments\/\d+\/insights\/\d+\/.*/).as('patchInsight') + cy.intercept('POST', /\/api\/environments\/\d+\/dashboards/).as('createDashboard') cy.useSubscriptionStatus('unsubscribed') cy.clickNavMenu('dashboards') diff --git a/cypress/e2e/dashboard.cy.ts b/cypress/e2e/dashboard.cy.ts index 954bc390759d3..b5f62097ebee1 100644 --- a/cypress/e2e/dashboard.cy.ts +++ b/cypress/e2e/dashboard.cy.ts @@ -3,9 +3,9 @@ import { randomString } from '../support/random' describe('Dashboard', () => { beforeEach(() => { - cy.intercept('GET', /api\/projects\/\d+\/insights\/\?.*/).as('loadInsightList') - cy.intercept('PATCH', /api\/projects\/\d+\/insights\/\d+\/.*/).as('patchInsight') - cy.intercept('POST', /\/api\/projects\/\d+\/dashboards/).as('createDashboard') + cy.intercept('GET', /api\/environments\/\d+\/insights\/\?.*/).as('loadInsightList') + cy.intercept('PATCH', /api\/environments\/\d+\/insights\/\d+\/.*/).as('patchInsight') + cy.intercept('POST', /\/api\/environments\/\d+\/dashboards/).as('createDashboard') cy.clickNavMenu('dashboards') cy.location('pathname').should('include', '/dashboard') @@ -306,7 +306,7 @@ describe('Dashboard', () => { }) it('Move dashboard item', () => { - cy.intercept('PATCH', /api\/projects\/\d+\/dashboards\/\d+\/move_tile.*/).as('moveTile') + cy.intercept('PATCH', /api\/environments\/\d+\/dashboards\/\d+\/move_tile.*/).as('moveTile') const sourceDashboard = randomString('source-dashboard') const targetDashboard = randomString('target-dashboard') diff --git a/cypress/e2e/insights-saved.cy.ts b/cypress/e2e/insights-saved.cy.ts index c5a498edf63af..748c0984543f3 100644 --- a/cypress/e2e/insights-saved.cy.ts +++ b/cypress/e2e/insights-saved.cy.ts @@ -30,7 +30,7 @@ describe('Insights - saved', () => { }) it('If cache empty, initiate async refresh', () => { - cy.intercept('GET', /\/api\/projects\/\d+\/insights\/?\?[^/]*?refresh=async/).as('getInsightsRefreshAsync') + cy.intercept('GET', /\/api\/environments\/\d+\/insights\/?\?[^/]*?refresh=async/).as('getInsightsRefreshAsync') let newInsightId: string createInsight('saved insight').then((insightId) => { newInsightId = insightId diff --git a/cypress/e2e/insights.cy.ts b/cypress/e2e/insights.cy.ts index e7e05fa4e0491..a7dc8924c6e09 100644 --- a/cypress/e2e/insights.cy.ts +++ b/cypress/e2e/insights.cy.ts @@ -53,7 +53,7 @@ describe('Insights', () => { }) it('Create new insight and save and continue editing', () => { - cy.intercept('PATCH', /\/api\/projects\/\d+\/insights\/\d+\/?/).as('patchInsight') + cy.intercept('PATCH', /\/api\/environments\/\d+\/insights\/\d+\/?/).as('patchInsight') const insightName = randomString('insight-name-') createInsight(insightName) diff --git a/cypress/e2e/notebooks.cy.ts b/cypress/e2e/notebooks.cy.ts index 36c81378d2667..3022d621ba63d 100644 --- a/cypress/e2e/notebooks.cy.ts +++ b/cypress/e2e/notebooks.cy.ts @@ -3,13 +3,13 @@ import { urls } from 'scenes/urls' describe('Notebooks', () => { beforeEach(() => { cy.fixture('api/session-recordings/recordings.json').then((recordings) => { - cy.intercept('GET', /api\/projects\/\d+\/session_recordings\/?\?.*/, { body: recordings }).as( + cy.intercept('GET', /api\/environments\/\d+\/session_recordings\/?\?.*/, { body: recordings }).as( 'loadSessionRecordingsList' ) }) cy.fixture('api/session-recordings/recording.json').then((recording) => { - cy.intercept('GET', /api\/projects\/\d+\/session_recordings\/.*\?.*/, { body: recording }).as( + cy.intercept('GET', /api\/environments\/\d+\/session_recordings\/.*\?.*/, { body: recording }).as( 'loadSessionRecording' ) }) diff --git a/cypress/e2e/projectHomepage.cy.ts b/cypress/e2e/projectHomepage.cy.ts index 069041d039435..6d88abfe24c44 100644 --- a/cypress/e2e/projectHomepage.cy.ts +++ b/cypress/e2e/projectHomepage.cy.ts @@ -1,6 +1,6 @@ describe('Project Homepage', () => { beforeEach(() => { - cy.intercept('GET', /\/api\/projects\/\d+\/dashboards\/\d+\//).as('getDashboard') + cy.intercept('GET', /\/api\/environments\/\d+\/dashboards\/\d+\//).as('getDashboard') cy.clickNavMenu('projecthomepage') }) diff --git a/cypress/e2e/trends.cy.ts b/cypress/e2e/trends.cy.ts index ce8a6e8574b30..9f6d45236520a 100644 --- a/cypress/e2e/trends.cy.ts +++ b/cypress/e2e/trends.cy.ts @@ -6,7 +6,7 @@ describe('Trends', () => { }) it('Can load a graph from a URL directly', () => { - cy.intercept('POST', /api\/projects\/\d+\/query\//).as('loadNewQueryInsight') + cy.intercept('POST', /api\/environments\/\d+\/query\//).as('loadNewQueryInsight') // regression test, the graph wouldn't load when going directly to a URL cy.visit( diff --git a/cypress/productAnalytics/index.ts b/cypress/productAnalytics/index.ts index 46344f78a627a..0fc9972014116 100644 --- a/cypress/productAnalytics/index.ts +++ b/cypress/productAnalytics/index.ts @@ -42,7 +42,7 @@ export const insight = { cy.url().should('not.include', '/new') }, clickTab: (tabName: string): void => { - cy.intercept('POST', /api\/projects\/\d+\/query\//).as('loadNewQueryInsight') + cy.intercept('POST', /api\/environments\/\d+\/query\//).as('loadNewQueryInsight') cy.get(`[data-attr="insight-${(tabName === 'PATHS' ? 'PATH' : tabName).toLowerCase()}-tab"]`).click() if (tabName !== 'FUNNELS') { @@ -51,7 +51,7 @@ export const insight = { } }, newInsight: (insightType: string = 'TRENDS'): void => { - cy.intercept('POST', /api\/projects\/\d+\/query\//).as('loadNewQueryInsight') + cy.intercept('POST', /api\/environments\/\d+\/query\//).as('loadNewQueryInsight') if (insightType === 'JSON') { cy.clickNavMenu('savedinsights') @@ -86,7 +86,7 @@ export const insight = { cy.url().should('not.include', '/new') // wait for insight to complete and update URL }, addInsightToDashboard: (dashboardName: string, options: { visitAfterAdding: boolean }): void => { - cy.intercept('PATCH', /api\/projects\/\d+\/insights\/\d+\/.*/).as('patchInsight') + cy.intercept('PATCH', /api\/environments\/\d+\/insights\/\d+\/.*/).as('patchInsight') cy.get('[data-attr="save-to-dashboard-button"]').click() cy.get('[data-attr="dashboard-searchfield"]').type(dashboardName) @@ -158,7 +158,7 @@ export const dashboards = { export const dashboard = { addInsightToEmptyDashboard: (insightName: string): void => { - cy.intercept('POST', /api\/projects\/\d+\/insights\//).as('postInsight') + cy.intercept('POST', /api\/environments\/\d+\/insights\//).as('postInsight') cy.get('[data-attr=dashboard-add-graph-header]').contains('Add insight').click() cy.get('[data-attr=toast-close-button]').click({ multiple: true }) diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts index 3b3d74a2287ea..fe164bf074b3a 100644 --- a/cypress/support/e2e.ts +++ b/cypress/support/e2e.ts @@ -101,7 +101,7 @@ beforeEach(() => { req.reply({ statusCode: 404, body: 'Cypress forced 404' }) ) - cy.intercept('GET', /\/api\/projects\/\d+\/insights\/?\?/).as('getInsights') + cy.intercept('GET', /\/api\/environments\/\d+\/insights\/?\?/).as('getInsights') cy.request('POST', '/api/login/', { email: 'test@posthog.com', diff --git a/ee/urls.py b/ee/urls.py index 633766add1439..f0cf168acffb0 100644 --- a/ee/urls.py +++ b/ee/urls.py @@ -30,7 +30,8 @@ def extend_api_router() -> None: projects_router, organizations_router, project_feature_flags_router, - project_dashboards_router, + environment_dashboards_router, + legacy_project_dashboards_router, ) root_router.register(r"billing", billing.BillingViewset, "billing") @@ -67,7 +68,14 @@ def extend_api_router() -> None: "environment_explicit_members", ["team_id"], ) - project_dashboards_router.register( + + environment_dashboards_router.register( + r"collaborators", + dashboard_collaborator.DashboardCollaboratorViewSet, + "environment_dashboard_collaborators", + ["project_id", "dashboard_id"], + ) + legacy_project_dashboards_router.register( r"collaborators", dashboard_collaborator.DashboardCollaboratorViewSet, "project_dashboard_collaborators", diff --git a/frontend/__snapshots__/scenes-app-max-ai--welcome--dark.png b/frontend/__snapshots__/scenes-app-max-ai--welcome--dark.png index a439dd9822a19..72976d20f6979 100644 Binary files a/frontend/__snapshots__/scenes-app-max-ai--welcome--dark.png and b/frontend/__snapshots__/scenes-app-max-ai--welcome--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-max-ai--welcome--light.png b/frontend/__snapshots__/scenes-app-max-ai--welcome--light.png index e46c5b1b9ded0..6047a48da0df7 100644 Binary files a/frontend/__snapshots__/scenes-app-max-ai--welcome--light.png and b/frontend/__snapshots__/scenes-app-max-ai--welcome--light.png differ diff --git a/frontend/__snapshots__/scenes-app-max-ai--welcome-loading-suggestions--dark.png b/frontend/__snapshots__/scenes-app-max-ai--welcome-loading-suggestions--dark.png index 8fd19ae835a64..72976d20f6979 100644 Binary files a/frontend/__snapshots__/scenes-app-max-ai--welcome-loading-suggestions--dark.png and b/frontend/__snapshots__/scenes-app-max-ai--welcome-loading-suggestions--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-max-ai--welcome-loading-suggestions--light.png b/frontend/__snapshots__/scenes-app-max-ai--welcome-loading-suggestions--light.png index 96b45dc3059b4..64ba4b1cac7bb 100644 Binary files a/frontend/__snapshots__/scenes-app-max-ai--welcome-loading-suggestions--light.png and b/frontend/__snapshots__/scenes-app-max-ai--welcome-loading-suggestions--light.png differ diff --git a/frontend/__snapshots__/scenes-app-persons-modal--empty--dark.png b/frontend/__snapshots__/scenes-app-persons-modal--empty--dark.png index 4643f3192e890..22afc5bd775e2 100644 Binary files a/frontend/__snapshots__/scenes-app-persons-modal--empty--dark.png and b/frontend/__snapshots__/scenes-app-persons-modal--empty--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-persons-modal--empty--light.png b/frontend/__snapshots__/scenes-app-persons-modal--empty--light.png index f651a76388281..080e592257657 100644 Binary files a/frontend/__snapshots__/scenes-app-persons-modal--empty--light.png and b/frontend/__snapshots__/scenes-app-persons-modal--empty--light.png differ diff --git a/frontend/__snapshots__/scenes-app-persons-modal--server-error--dark.png b/frontend/__snapshots__/scenes-app-persons-modal--server-error--dark.png index be5d0970ca208..ee5eb6d97e9fd 100644 Binary files a/frontend/__snapshots__/scenes-app-persons-modal--server-error--dark.png and b/frontend/__snapshots__/scenes-app-persons-modal--server-error--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-persons-modal--server-error--light.png b/frontend/__snapshots__/scenes-app-persons-modal--server-error--light.png index 495ede9027f52..daa7e4e9adab1 100644 Binary files a/frontend/__snapshots__/scenes-app-persons-modal--server-error--light.png and b/frontend/__snapshots__/scenes-app-persons-modal--server-error--light.png differ diff --git a/frontend/src/layout/navigation-3000/Navigation.stories.tsx b/frontend/src/layout/navigation-3000/Navigation.stories.tsx index 3b9671e50deec..a5cbadec01f73 100644 --- a/frontend/src/layout/navigation-3000/Navigation.stories.tsx +++ b/frontend/src/layout/navigation-3000/Navigation.stories.tsx @@ -13,11 +13,11 @@ const meta: Meta = { decorators: [ mswDecorator({ get: { - '/api/projects/:team_id/dashboards/': require('../../scenes/dashboard/__mocks__/dashboards.json'), - '/api/projects/:team_id/dashboards/1/': require('../../scenes/dashboard/__mocks__/dashboard1.json'), - '/api/projects/:team_id/dashboards/1/collaborators/': [], - '/api/projects/:team_id/insights/my_last_viewed/': require('../../scenes/saved-insights/__mocks__/insightsMyLastViewed.json'), - '/api/projects/:team_id/session_recordings/': EMPTY_PAGINATED_RESPONSE, + '/api/environments/:team_id/dashboards/': require('../../scenes/dashboard/__mocks__/dashboards.json'), + '/api/environments/:team_id/dashboards/1/': require('../../scenes/dashboard/__mocks__/dashboard1.json'), + '/api/environments/:team_id/dashboards/1/collaborators/': [], + '/api/environments/:team_id/insights/my_last_viewed/': require('../../scenes/saved-insights/__mocks__/insightsMyLastViewed.json'), + '/api/environments/:team_id/session_recordings/': EMPTY_PAGINATED_RESPONSE, }, }), ], diff --git a/frontend/src/layout/navigation-3000/components/Sidebar.stories.tsx b/frontend/src/layout/navigation-3000/components/Sidebar.stories.tsx index c1bb27c69182c..bdb07b697a5c2 100644 --- a/frontend/src/layout/navigation-3000/components/Sidebar.stories.tsx +++ b/frontend/src/layout/navigation-3000/components/Sidebar.stories.tsx @@ -37,7 +37,7 @@ const multipliedFeatureFlagsJson = { export function Dashboards(): JSX.Element { useStorybookMocks({ get: { - '/api/projects/:team_id/dashboards/': dashboardsJson, + '/api/environments/:team_id/dashboards/': dashboardsJson, }, }) const { showSidebar } = useActions(navigation3000Logic) diff --git a/frontend/src/layout/navigation-3000/sidepanel/SidePanel.stories.tsx b/frontend/src/layout/navigation-3000/sidepanel/SidePanel.stories.tsx index 2cbd7574fa1fb..e83dde7f8f5a5 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/SidePanel.stories.tsx +++ b/frontend/src/layout/navigation-3000/sidepanel/SidePanel.stories.tsx @@ -29,7 +29,7 @@ const meta: Meta = { '/api/projects/:id/integrations': { results: [] }, }, post: { - '/api/projects/:team_id/query': {}, + '/api/environments/:team_id/query': {}, }, }), ], diff --git a/frontend/src/lib/api.test.ts b/frontend/src/lib/api.test.ts index bea56e12d350f..a7da16722813c 100644 --- a/frontend/src/lib/api.test.ts +++ b/frontend/src/lib/api.test.ts @@ -37,7 +37,7 @@ describe('API helper', () => { ) expect(fakeFetch).toHaveBeenCalledWith( - '/api/projects/2/events?properties=%5B%7B%22key%22%3A%22something%22%2C%22value%22%3A%22is_set%22%2C%22operator%22%3A%22is_set%22%2C%22type%22%3A%22event%22%7D%5D&limit=10&orderBy=%5B%22-timestamp%22%5D', + '/api/environments/2/events?properties=%5B%7B%22key%22%3A%22something%22%2C%22value%22%3A%22is_set%22%2C%22operator%22%3A%22is_set%22%2C%22type%22%3A%22event%22%7D%5D&limit=10&orderBy=%5B%22-timestamp%22%5D', { signal: undefined, headers: { diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index c078d8b91cb96..a8e6024171bee 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -83,6 +83,7 @@ import { PluginConfigTypeNew, PluginConfigWithPluginInfoNew, PluginLogEntry, + ProjectType, PropertyDefinition, PropertyDefinitionType, QueryBasedInsightModel, @@ -195,6 +196,7 @@ export async function getJSONOrNull(response: Response): Promise { export class ApiConfig { private static _currentOrganizationId: OrganizationType['id'] | null = null + private static _currentProjectId: ProjectType['id'] | null = null private static _currentTeamId: TeamType['id'] | null = null static getCurrentOrganizationId(): OrganizationType['id'] { @@ -218,6 +220,17 @@ export class ApiConfig { static setCurrentTeamId(id: TeamType['id']): void { this._currentTeamId = id } + + static getCurrentProjectId(): ProjectType['id'] { + if (!this._currentProjectId) { + throw new Error('Project ID is not known.') + } + return this._currentProjectId + } + + static setCurrentProjectId(id: ProjectType['id']): void { + this._currentProjectId = id + } } class ApiRequest { @@ -304,13 +317,22 @@ class ApiRequest { return this.addPathComponent('projects') } - public projectsDetail(id: TeamType['id'] = ApiConfig.getCurrentTeamId()): ApiRequest { + public projectsDetail(id: ProjectType['id'] = ApiConfig.getCurrentProjectId()): ApiRequest { return this.projects().addPathComponent(id) } + // # Projects + public environments(): ApiRequest { + return this.addPathComponent('environments') + } + + public environmentsDetail(id: TeamType['id'] = ApiConfig.getCurrentTeamId()): ApiRequest { + return this.environments().addPathComponent(id) + } + // # Insights public insights(teamId?: TeamType['id']): ApiRequest { - return this.projectsDetail(teamId).addPathComponent('insights') + return this.environmentsDetail(teamId).addPathComponent('insights') } public insight(id: QueryBasedInsightModel['id'], teamId?: TeamType['id']): ApiRequest { @@ -335,7 +357,7 @@ class ApiRequest { } public pluginConfigs(teamId?: TeamType['id']): ApiRequest { - return this.projectsDetail(teamId).addPathComponent('plugin_configs') + return this.environmentsDetail(teamId).addPathComponent('plugin_configs') } public pluginConfig(id: number, teamId?: TeamType['id']): ApiRequest { @@ -381,7 +403,7 @@ class ApiRequest { // # Exports public exports(teamId?: TeamType['id']): ApiRequest { - return this.projectsDetail(teamId).addPathComponent('exports') + return this.environmentsDetail(teamId).addPathComponent('exports') } public export(id: number, teamId?: TeamType['id']): ApiRequest { @@ -390,7 +412,7 @@ class ApiRequest { // # Events public events(teamId?: TeamType['id']): ApiRequest { - return this.projectsDetail(teamId).addPathComponent('events') + return this.environmentsDetail(teamId).addPathComponent('events') } public event(id: EventType['id'], teamId?: TeamType['id']): ApiRequest { @@ -402,16 +424,16 @@ class ApiRequest { } // # Data management - public eventDefinitions(teamId?: TeamType['id']): ApiRequest { - return this.projectsDetail(teamId).addPathComponent('event_definitions') + public eventDefinitions(projectId?: ProjectType['id']): ApiRequest { + return this.projectsDetail(projectId).addPathComponent('event_definitions') } - public eventDefinitionDetail(eventDefinitionId: EventDefinition['id'], teamId?: TeamType['id']): ApiRequest { - return this.projectsDetail(teamId).addPathComponent('event_definitions').addPathComponent(eventDefinitionId) + public eventDefinitionDetail(eventDefinitionId: EventDefinition['id'], projectId?: ProjectType['id']): ApiRequest { + return this.projectsDetail(projectId).addPathComponent('event_definitions').addPathComponent(eventDefinitionId) } - public propertyDefinitions(teamId?: TeamType['id']): ApiRequest { - return this.projectsDetail(teamId).addPathComponent('property_definitions') + public propertyDefinitions(projectId?: ProjectType['id']): ApiRequest { + return this.projectsDetail(projectId).addPathComponent('property_definitions') } public propertyDefinitionDetail( @@ -458,13 +480,15 @@ class ApiRequest { // Recordings public recording(recordingId: SessionRecordingType['id'], teamId?: TeamType['id']): ApiRequest { - return this.projectsDetail(teamId).addPathComponent('session_recordings').addPathComponent(recordingId) + return this.environmentsDetail(teamId).addPathComponent('session_recordings').addPathComponent(recordingId) } public recordings(teamId?: TeamType['id']): ApiRequest { - return this.projectsDetail(teamId).addPathComponent('session_recordings') + return this.environmentsDetail(teamId).addPathComponent('session_recordings') } public recordingMatchingEvents(teamId?: TeamType['id']): ApiRequest { - return this.projectsDetail(teamId).addPathComponent('session_recordings').addPathComponent('matching_events') + return this.environmentsDetail(teamId) + .addPathComponent('session_recordings') + .addPathComponent('matching_events') } public recordingPlaylists(teamId?: TeamType['id']): ApiRequest { return this.projectsDetail(teamId).addPathComponent('session_recording_playlists') @@ -484,7 +508,7 @@ class ApiRequest { // # Dashboards public dashboards(teamId?: TeamType['id']): ApiRequest { - return this.projectsDetail(teamId).addPathComponent('dashboards') + return this.environmentsDetail(teamId).addPathComponent('dashboards') } public dashboardsDetail(dashboardId: DashboardType['id'], teamId?: TeamType['id']): ApiRequest { @@ -564,7 +588,7 @@ class ApiRequest { // # Persons public persons(teamId?: TeamType['id']): ApiRequest { - return this.projectsDetail(teamId).addPathComponent('persons') + return this.environmentsDetail(teamId).addPathComponent('persons') } public person(id: string | number, teamId?: TeamType['id']): ApiRequest { @@ -580,7 +604,7 @@ class ApiRequest { // # Groups public groups(teamId?: TeamType['id']): ApiRequest { - return this.projectsDetail(teamId).addPathComponent('groups') + return this.environmentsDetail(teamId).addPathComponent('groups') } // # Search @@ -719,11 +743,11 @@ class ApiRequest { // # Subscriptions public subscriptions(teamId?: TeamType['id']): ApiRequest { - return this.projectsDetail(teamId).addPathComponent('subscriptions') + return this.environmentsDetail(teamId).addPathComponent('subscriptions') } public subscription(id: SubscriptionType['id'], teamId?: TeamType['id']): ApiRequest { - return this.subscriptions(teamId).addPathComponent(id) + return this.environmentsDetail(teamId).addPathComponent(id) } // # Integrations @@ -746,12 +770,15 @@ class ApiRequest { // # Alerts public alerts(alertId?: AlertType['id'], insightId?: InsightModel['id'], teamId?: TeamType['id']): ApiRequest { if (alertId) { - return this.projectsDetail(teamId).addPathComponent('alerts').addPathComponent(alertId).withQueryString({ - insight_id: insightId, - }) + return this.environmentsDetail(teamId) + .addPathComponent('alerts') + .addPathComponent(alertId) + .withQueryString({ + insight_id: insightId, + }) } - return this.projectsDetail(teamId).addPathComponent('alerts').withQueryString({ + return this.environmentsDetail(teamId).addPathComponent('alerts').withQueryString({ insight_id: insightId, }) } @@ -775,7 +802,7 @@ class ApiRequest { // # Queries public query(teamId?: TeamType['id']): ApiRequest { - return this.projectsDetail(teamId).addPathComponent('query') + return this.environmentsDetail(teamId).addPathComponent('query') } public queryStatus(queryId: string, showProgress: boolean, teamId?: TeamType['id']): ApiRequest { @@ -788,7 +815,7 @@ class ApiRequest { // Chat public chat(teamId?: TeamType['id']): ApiRequest { - return this.projectsDetail(teamId).addPathComponent('query').addPathComponent('chat') + return this.environmentsDetail(teamId).addPathComponent('query').addPathComponent('chat') } // Notebooks @@ -802,7 +829,7 @@ class ApiRequest { // Batch Exports public batchExports(teamId?: TeamType['id']): ApiRequest { - return this.projectsDetail(teamId).addPathComponent('batch_exports') + return this.environmentsDetail(teamId).addPathComponent('batch_exports') } public batchExport(id: BatchExportConfiguration['id'], teamId?: TeamType['id']): ApiRequest { @@ -907,14 +934,14 @@ const prepareUrl = (url: string): string => { return output } -const PROJECT_ID_REGEX = /\/api\/projects\/(\w+)(?:$|[/?#])/ +const PROJECT_ID_REGEX = /\/api\/(project|environment)s\/(\w+)(?:$|[/?#])/ const ensureProjectIdNotInvalid = (url: string): void => { const projectIdMatch = PROJECT_ID_REGEX.exec(url) if (projectIdMatch) { - const projectId = projectIdMatch[1].trim() + const projectId = projectIdMatch[2].trim() if (projectId === 'null' || projectId === 'undefined') { - throw { status: 0, detail: 'Cannot make request - project ID is unknown.' } + throw { status: 0, detail: `Cannot make request - ${projectIdMatch[1]} ID is unknown.` } } } } @@ -1249,7 +1276,7 @@ const api = { }, async list({ limit = EVENT_DEFINITIONS_PER_PAGE, - teamId = ApiConfig.getCurrentTeamId(), + teamId, ...params }: { limit?: number @@ -1265,7 +1292,7 @@ const api = { }, determineListEndpoint({ limit = EVENT_DEFINITIONS_PER_PAGE, - teamId = ApiConfig.getCurrentTeamId(), + teamId, ...params }: { limit?: number @@ -1314,7 +1341,7 @@ const api = { }, async list({ limit = EVENT_PROPERTY_DEFINITIONS_PER_PAGE, - teamId = ApiConfig.getCurrentTeamId(), + teamId, ...params }: { event_names?: string[] @@ -1340,7 +1367,7 @@ const api = { }, determineListEndpoint({ limit = EVENT_PROPERTY_DEFINITIONS_PER_PAGE, - teamId = ApiConfig.getCurrentTeamId(), + teamId, ...params }: { event_names?: string[] @@ -1368,7 +1395,7 @@ const api = { sessions: { async propertyDefinitions({ - teamId = ApiConfig.getCurrentTeamId(), + teamId, search, properties, }: { diff --git a/frontend/src/lib/components/ActivityLog/ActivityLog.stories.tsx b/frontend/src/lib/components/ActivityLog/ActivityLog.stories.tsx index 1a549691f9088..d850cd6258cdf 100644 --- a/frontend/src/lib/components/ActivityLog/ActivityLog.stories.tsx +++ b/frontend/src/lib/components/ActivityLog/ActivityLog.stories.tsx @@ -42,7 +42,7 @@ const meta: Meta = { ctx.status(200), ctx.json({ results: featureFlagsActivityResponseJson }), ], - '/api/projects/:team/insights/activity': (_, __, ctx) => [ + '/api/environments/:team_id/insights/activity': (_, __, ctx) => [ ctx.status(200), ctx.json({ results: insightsActivityResponseJson }), ], diff --git a/frontend/src/lib/components/ActivityLog/activityLogLogic.insight.test.tsx b/frontend/src/lib/components/ActivityLog/activityLogLogic.insight.test.tsx index 2f290332b7e19..a302f831743a2 100644 --- a/frontend/src/lib/components/ActivityLog/activityLogLogic.insight.test.tsx +++ b/frontend/src/lib/components/ActivityLog/activityLogLogic.insight.test.tsx @@ -13,7 +13,7 @@ describe('the activity log logic', () => { describe('humanizing insights', () => { const insightTestSetup = makeTestSetup( ActivityScope.INSIGHT, - `/api/projects/${MOCK_TEAM_ID}/insights/activity/` + `/api/environments/${MOCK_TEAM_ID}/insights/activity/` ) it('can handle change of name', async () => { diff --git a/frontend/src/lib/components/ActivityLog/activityLogLogic.person.test.tsx b/frontend/src/lib/components/ActivityLog/activityLogLogic.person.test.tsx index 8a34afd1c00a6..988b1b741f9c8 100644 --- a/frontend/src/lib/components/ActivityLog/activityLogLogic.person.test.tsx +++ b/frontend/src/lib/components/ActivityLog/activityLogLogic.person.test.tsx @@ -8,7 +8,10 @@ import { ActivityScope } from '~/types' describe('the activity log logic', () => { describe('humanizing persons', () => { - const personTestSetup = makeTestSetup(ActivityScope.PERSON, `/api/projects/${MOCK_TEAM_ID}/persons/7/activity/`) + const personTestSetup = makeTestSetup( + ActivityScope.PERSON, + `/api/environments/${MOCK_TEAM_ID}/persons/7/activity/` + ) it('can handle addition of a property', async () => { const logic = await personTestSetup('test person', 'updated', [ { diff --git a/frontend/src/lib/components/AnnotationsOverlay/annotationsOverlayLogic.test.ts b/frontend/src/lib/components/AnnotationsOverlay/annotationsOverlayLogic.test.ts index b6d2f1c5fc8eb..a8c984dc0e928 100644 --- a/frontend/src/lib/components/AnnotationsOverlay/annotationsOverlayLogic.test.ts +++ b/frontend/src/lib/components/AnnotationsOverlay/annotationsOverlayLogic.test.ts @@ -181,7 +181,7 @@ function useInsightMocks(interval: string = 'day', timezone: string = 'UTC'): vo } useMocks({ get: { - '/api/projects/:team_id/insights/': () => { + '/api/environments/:team_id/insights/': () => { return [ 200, { @@ -189,7 +189,7 @@ function useInsightMocks(interval: string = 'day', timezone: string = 'UTC'): vo }, ] }, - [`/api/projects/:team_id/insights/${MOCK_INSIGHT_NUMERIC_ID}`]: () => { + [`/api/environments/:team_id/insights/${MOCK_INSIGHT_NUMERIC_ID}`]: () => { return [200, insight] }, '/api/users/@me/': [200, {}], diff --git a/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.test.ts b/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.test.ts index b8678715352ea..6e9897b9e2693 100644 --- a/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.test.ts +++ b/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.test.ts @@ -20,7 +20,7 @@ describe('the authorized urls list logic', () => { beforeEach(() => { useMocks({ get: { - '/api/projects/:team/insights/trend/': (req) => { + '/api/environments/:team_id/insights/trend/': (req) => { if (JSON.parse(req.url.searchParams.get('events') || '[]')?.[0]?.throw) { return [500, { status: 0, detail: 'error from the API' }] } diff --git a/frontend/src/lib/components/PropertiesTimeline/PropertiesTimeline.stories.tsx b/frontend/src/lib/components/PropertiesTimeline/PropertiesTimeline.stories.tsx index 3d38ab578827d..cbfcbf10df59f 100644 --- a/frontend/src/lib/components/PropertiesTimeline/PropertiesTimeline.stories.tsx +++ b/frontend/src/lib/components/PropertiesTimeline/PropertiesTimeline.stories.tsx @@ -27,7 +27,7 @@ export function MultiplePointsForOnePersonProperty(): JSX.Element { const examplePerson: PersonActorType = { ...EXAMPLE_PERSON, id: 1, uuid: '012e89b5-4239-4319-8ae4-d3cae2f5deb1' } useStorybookMocks({ get: { - [`/api/projects/${MOCK_TEAM_ID}/persons/${examplePerson.uuid}/properties_timeline/`]: { + [`/api/environments/${MOCK_TEAM_ID}/persons/${examplePerson.uuid}/properties_timeline/`]: { points: [ { timestamp: '2021-01-01T00:00:00.000Z', @@ -88,7 +88,7 @@ export function OnePointForOnePersonProperty(): JSX.Element { const examplePerson: PersonActorType = { ...EXAMPLE_PERSON, id: 2, uuid: '012e89b5-4239-4319-8ae4-d3cae2f5deb2' } useStorybookMocks({ get: { - [`/api/projects/${MOCK_TEAM_ID}/persons/${examplePerson.uuid}/properties_timeline/`]: { + [`/api/environments/${MOCK_TEAM_ID}/persons/${examplePerson.uuid}/properties_timeline/`]: { points: [ { timestamp: '2021-05-01T00:00:00.000Z', @@ -125,7 +125,7 @@ export function NoPointsForNoPersonProperties(): JSX.Element { const examplePerson: PersonActorType = { ...EXAMPLE_PERSON, id: 3, uuid: '012e89b5-4239-4319-8ae4-d3cae2f5deb3' } useStorybookMocks({ get: { - [`/api/projects/${MOCK_TEAM_ID}/persons/${examplePerson.uuid}/properties_timeline/`]: { + [`/api/environments/${MOCK_TEAM_ID}/persons/${examplePerson.uuid}/properties_timeline/`]: { points: [ { timestamp: '2021-01-01T00:00:00.000Z', diff --git a/frontend/src/lib/components/PropertiesTimeline/propertiesTimelineLogic.ts b/frontend/src/lib/components/PropertiesTimeline/propertiesTimelineLogic.ts index adff5170c3cdf..b1f3c848cfd68 100644 --- a/frontend/src/lib/components/PropertiesTimeline/propertiesTimelineLogic.ts +++ b/frontend/src/lib/components/PropertiesTimeline/propertiesTimelineLogic.ts @@ -66,7 +66,7 @@ export const propertiesTimelineLogic = kea([ if (props.actor.type === 'person') { const queryId = uuid() const response = await apiGetWithTimeToSeeDataTracking( - `api/projects/${values.currentTeamId}/persons/${ + `api/environments/${values.currentTeamId}/persons/${ props.actor.uuid }/properties_timeline/?${toParams(props.filter)}`, values.currentTeamId, diff --git a/frontend/src/lib/components/PropertySelect/PropertySelect.stories.tsx b/frontend/src/lib/components/PropertySelect/PropertySelect.stories.tsx index 35021b789889b..b42bf7366157d 100644 --- a/frontend/src/lib/components/PropertySelect/PropertySelect.stories.tsx +++ b/frontend/src/lib/components/PropertySelect/PropertySelect.stories.tsx @@ -13,7 +13,7 @@ const meta: Meta = { decorators: [ mswDecorator({ get: { - '/api/projects/:team_id/persons/properties': [ + '/api/environments/:team_id/persons/properties': [ { name: 'Property A', count: 10 }, { name: 'Property B', count: 20 }, { name: 'Property C', count: 30 }, diff --git a/frontend/src/lib/components/ReverseProxyChecker/reverseProxyCheckerLogic.test.ts b/frontend/src/lib/components/ReverseProxyChecker/reverseProxyCheckerLogic.test.ts index 5ea635b7e4f90..8842402310efe 100644 --- a/frontend/src/lib/components/ReverseProxyChecker/reverseProxyCheckerLogic.test.ts +++ b/frontend/src/lib/components/ReverseProxyChecker/reverseProxyCheckerLogic.test.ts @@ -11,7 +11,7 @@ const doesNotHaveReverseProxyValues = [[null], [null]] const useMockedValues = (results: (string | null)[][]): void => { useMocks({ post: { - '/api/projects/:team/query': () => [ + '/api/environments/:team_id/query': () => [ 200, { results, diff --git a/frontend/src/lib/components/Sharing/SharingModal.stories.tsx b/frontend/src/lib/components/Sharing/SharingModal.stories.tsx index 8c088002c4cb7..75f527f56da73 100644 --- a/frontend/src/lib/components/Sharing/SharingModal.stories.tsx +++ b/frontend/src/lib/components/Sharing/SharingModal.stories.tsx @@ -36,9 +36,9 @@ const Template = (args: Partial & { licensed?: boolean }): JS useStorybookMocks({ get: { ...[ - '/api/projects/:id/insights/:insight_id/sharing/', - '/api/projects/:id/dashboards/:dashboard_id/sharing/', - '/api/projects/:id/session_recordings/:recording_id/sharing/', + '/api/environments/:id/insights/:insight_id/sharing/', + '/api/environments/:id/dashboards/:dashboard_id/sharing/', + '/api/environments/:id/session_recordings/:recording_id/sharing/', ].reduce( (acc, url) => ({ ...acc, @@ -50,13 +50,13 @@ const Template = (args: Partial & { licensed?: boolean }): JS }), {} ), - '/api/projects/:id/insights/': { results: [fakeInsight] }, + '/api/environments/:id/insights/': { results: [fakeInsight] }, }, patch: { ...[ - '/api/projects/:id/insights/:insight_id/sharing/', - '/api/projects/:id/dashboards/:dashboard_id/sharing/', - '/api/projects/:id/session_recordings/:recording_id/sharing/', + '/api/environments/:id/insights/:insight_id/sharing/', + '/api/environments/:id/dashboards/:dashboard_id/sharing/', + '/api/environments/:id/session_recordings/:recording_id/sharing/', ].reduce( (acc, url) => ({ ...acc, diff --git a/frontend/src/lib/components/Subscriptions/SubscriptionsModal.stories.tsx b/frontend/src/lib/components/Subscriptions/SubscriptionsModal.stories.tsx index ce6bca5ee6903..0dcb793df4383 100644 --- a/frontend/src/lib/components/Subscriptions/SubscriptionsModal.stories.tsx +++ b/frontend/src/lib/components/Subscriptions/SubscriptionsModal.stories.tsx @@ -40,7 +40,7 @@ const Template = ( slack_service: noIntegrations ? { available: false } : { available: true, client_id: 'test-client-id' }, site_url: noIntegrations ? 'bad-value' : window.location.origin, }, - '/api/projects/:id/subscriptions': { + '/api/environments/:id/subscriptions': { results: insightShortIdRef.current === 'empty' ? [] @@ -61,7 +61,7 @@ const Template = ( }), ], }, - '/api/projects/:id/subscriptions/:subId': createMockSubscription(), + '/api/environments/:id/subscriptions/:subId': createMockSubscription(), '/api/projects/:id/integrations': { results: !noIntegrations ? [mockIntegration] : [] }, '/api/projects/:id/integrations/:intId/channels': { channels: mockSlackChannels }, }, diff --git a/frontend/src/lib/components/Subscriptions/subscriptionsLogic.test.ts b/frontend/src/lib/components/Subscriptions/subscriptionsLogic.test.ts index 2cfbe7207bf52..092614abd0d67 100644 --- a/frontend/src/lib/components/Subscriptions/subscriptionsLogic.test.ts +++ b/frontend/src/lib/components/Subscriptions/subscriptionsLogic.test.ts @@ -54,15 +54,15 @@ describe('subscriptionsLogic', () => { subscriptions = [fixtureSubscriptionResponse(1), fixtureSubscriptionResponse(2)] useMocks({ get: { - '/api/projects/:team/insights/1': fixtureInsightResponse(1), - '/api/projects/:team/insights/2': fixtureInsightResponse(2), - '/api/projects/:team/insights': (req) => { + '/api/environments/:team_id/insights/1': fixtureInsightResponse(1), + '/api/environments/:team_id/insights/2': fixtureInsightResponse(2), + '/api/environments/:team_id/insights': (req) => { const insightShortId = req.url.searchParams.get('short_id') const res = insightShortId ? [fixtureInsightResponse(parseInt(insightShortId, 10))] : [] return [200, { results: res }] }, - '/api/projects/:team/subscriptions': (req) => { + '/api/environments/:team_id/subscriptions': (req) => { const insightId = req.url.searchParams.get('insight') let results: SubscriptionType[] = [] diff --git a/frontend/src/lib/components/TaxonomicFilter/__mocks__/taxonomicFilterMocksDecorator.ts b/frontend/src/lib/components/TaxonomicFilter/__mocks__/taxonomicFilterMocksDecorator.ts index 9c29ae50e755c..f5140db2985e0 100644 --- a/frontend/src/lib/components/TaxonomicFilter/__mocks__/taxonomicFilterMocksDecorator.ts +++ b/frontend/src/lib/components/TaxonomicFilter/__mocks__/taxonomicFilterMocksDecorator.ts @@ -4,7 +4,7 @@ import { mockActionDefinition } from '~/test/mocks' export const taxonomicFilterMocksDecorator = mswDecorator({ get: { '/api/projects/:team_id/actions': { results: [mockActionDefinition] }, - '/api/projects/:team_id/persons/properties': [ + '/api/environments/:team_id/persons/properties': [ { id: 1, name: 'location', count: 1 }, { id: 2, name: 'role', count: 2 }, { id: 3, name: 'height', count: 3 }, diff --git a/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.test.ts b/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.test.ts index cf04e58e218ae..25e2866afc2c3 100644 --- a/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.test.ts +++ b/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.test.ts @@ -9,7 +9,10 @@ import { AppContext, PropertyDefinition } from '~/types' import { infiniteListLogic } from './infiniteListLogic' -window.POSTHOG_APP_CONTEXT = { current_team: { id: MOCK_TEAM_ID } } as unknown as AppContext +window.POSTHOG_APP_CONTEXT = { + current_team: { id: MOCK_TEAM_ID }, + current_project: { id: MOCK_TEAM_ID }, +} as unknown as AppContext describe('infiniteListLogic', () => { let logic: ReturnType diff --git a/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.test.ts b/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.test.ts index 74278c510754e..90103bc8686ad 100644 --- a/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.test.ts +++ b/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.test.ts @@ -12,7 +12,10 @@ import { AppContext } from '~/types' import { infiniteListLogic } from './infiniteListLogic' -window.POSTHOG_APP_CONTEXT = { current_team: { id: MOCK_TEAM_ID } } as unknown as AppContext +window.POSTHOG_APP_CONTEXT = { + current_team: { id: MOCK_TEAM_ID }, + current_project: { id: MOCK_TEAM_ID }, +} as unknown as AppContext describe('taxonomicFilterLogic', () => { let logic: ReturnType @@ -33,7 +36,7 @@ describe('taxonomicFilterLogic', () => { }, ] }, - '/api/projects/:team/sessions/property_definitions': (res) => { + '/api/environments/:team/sessions/property_definitions': (res) => { const search = res.url.searchParams.get('search') const results = search ? mockSessionPropertyDefinitions.filter((e) => e.name.includes(search)) diff --git a/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.tsx b/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.tsx index a93ba76878fe0..4f78288aa2c75 100644 --- a/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.tsx +++ b/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.tsx @@ -20,6 +20,7 @@ import { dataWarehouseSceneLogic } from 'scenes/data-warehouse/settings/dataWare import { experimentsLogic } from 'scenes/experiments/experimentsLogic' import { featureFlagsLogic } from 'scenes/feature-flags/featureFlagsLogic' import { groupDisplayId } from 'scenes/persons/GroupActorDisplay' +import { projectLogic } from 'scenes/projectLogic' import { ReplayTaxonomicFilters } from 'scenes/session-recordings/filters/ReplayTaxonomicFilters' import { teamLogic } from 'scenes/teamLogic' @@ -79,6 +80,8 @@ export const taxonomicFilterLogic = kea([ values: [ teamLogic, ['currentTeamId'], + projectLogic, + ['currentProjectId'], groupsModel, ['groupTypes', 'aggregationLabel'], groupPropertiesModel, @@ -159,6 +162,7 @@ export const taxonomicFilterLogic = kea([ taxonomicGroups: [ (s) => [ s.currentTeamId, + s.currentProjectId, s.groupAnalyticsTaxonomicGroups, s.groupAnalyticsTaxonomicGroupNames, s.eventNames, @@ -169,6 +173,7 @@ export const taxonomicFilterLogic = kea([ ], ( teamId, + projectId, groupAnalyticsTaxonomicGroups, groupAnalyticsTaxonomicGroupNames, eventNames, @@ -185,7 +190,7 @@ export const taxonomicFilterLogic = kea([ options: [{ name: 'All events', value: null }].filter( (o) => !excludedProperties[TaxonomicFilterGroupType.Events]?.includes(o.value) ), - endpoint: combineUrl(`api/projects/${teamId}/event_definitions`, { + endpoint: combineUrl(`api/projects/${projectId}/event_definitions`, { event_type: EventDefinitionType.Event, }).url, getName: (eventDefinition: Record) => eventDefinition.name, @@ -261,7 +266,7 @@ export const taxonomicFilterLogic = kea([ name: 'Event properties', searchPlaceholder: 'event properties', type: TaxonomicFilterGroupType.EventProperties, - endpoint: combineUrl(`api/projects/${teamId}/property_definitions`, { + endpoint: combineUrl(`api/projects/${projectId}/property_definitions`, { is_feature_flag: false, ...(eventNames.length > 0 ? { event_names: eventNames } : {}), properties: propertyAllowList?.[TaxonomicFilterGroupType.EventProperties] @@ -270,7 +275,7 @@ export const taxonomicFilterLogic = kea([ }).url, scopedEndpoint: eventNames.length > 0 - ? combineUrl(`api/projects/${teamId}/property_definitions`, { + ? combineUrl(`api/projects/${projectId}/property_definitions`, { event_names: eventNames, is_feature_flag: false, filter_by_event_names: true, @@ -296,13 +301,13 @@ export const taxonomicFilterLogic = kea([ name: 'Feature flags', searchPlaceholder: 'feature flags', type: TaxonomicFilterGroupType.EventFeatureFlags, - endpoint: combineUrl(`api/projects/${teamId}/property_definitions`, { + endpoint: combineUrl(`api/projects/${projectId}/property_definitions`, { is_feature_flag: true, ...(eventNames.length > 0 ? { event_names: eventNames } : {}), }).url, scopedEndpoint: eventNames.length > 0 - ? combineUrl(`api/projects/${teamId}/property_definitions`, { + ? combineUrl(`api/projects/${projectId}/property_definitions`, { event_names: eventNames, is_feature_flag: true, filter_by_event_names: true, @@ -324,7 +329,7 @@ export const taxonomicFilterLogic = kea([ name: 'Numerical event properties', searchPlaceholder: 'numerical event properties', type: TaxonomicFilterGroupType.NumericalEventProperties, - endpoint: combineUrl(`api/projects/${teamId}/property_definitions`, { + endpoint: combineUrl(`api/projects/${projectId}/property_definitions`, { is_numerical: true, event_names: eventNames, }).url, @@ -336,7 +341,7 @@ export const taxonomicFilterLogic = kea([ name: 'Person properties', searchPlaceholder: 'person properties', type: TaxonomicFilterGroupType.PersonProperties, - endpoint: combineUrl(`api/projects/${teamId}/property_definitions`, { + endpoint: combineUrl(`api/projects/${projectId}/property_definitions`, { type: 'person', properties: propertyAllowList?.[TaxonomicFilterGroupType.PersonProperties] ? propertyAllowList[TaxonomicFilterGroupType.PersonProperties].join(',') @@ -377,7 +382,7 @@ export const taxonomicFilterLogic = kea([ name: 'Pageview URLs', searchPlaceholder: 'pageview URLs', type: TaxonomicFilterGroupType.PageviewUrls, - endpoint: `api/projects/${teamId}/events/values/?key=$current_url`, + endpoint: `api/environments/${teamId}/events/values/?key=$current_url`, searchAlias: 'value', getName: (option: SimpleOption) => option.name, getValue: (option: SimpleOption) => option.name, @@ -387,7 +392,7 @@ export const taxonomicFilterLogic = kea([ name: 'Screens', searchPlaceholder: 'screens', type: TaxonomicFilterGroupType.Screens, - endpoint: `api/projects/${teamId}/events/values/?key=$screen_name`, + endpoint: `api/environments/${teamId}/events/values/?key=$screen_name`, searchAlias: 'value', getName: (option: SimpleOption) => option.name, getValue: (option: SimpleOption) => option.name, @@ -397,7 +402,7 @@ export const taxonomicFilterLogic = kea([ name: 'Custom Events', searchPlaceholder: 'custom events', type: TaxonomicFilterGroupType.CustomEvents, - endpoint: combineUrl(`api/projects/${teamId}/event_definitions`, { + endpoint: combineUrl(`api/projects/${projectId}/event_definitions`, { event_type: EventDefinitionType.EventCustom, }).url, getName: (eventDefinition: EventDefinition) => eventDefinition.name, @@ -417,7 +422,7 @@ export const taxonomicFilterLogic = kea([ name: 'Persons', searchPlaceholder: 'persons', type: TaxonomicFilterGroupType.Persons, - endpoint: `api/projects/${teamId}/persons/`, + endpoint: `api/environments/${teamId}/persons/`, getName: (person: PersonType) => person.name || 'Anon user?', getValue: (person: PersonType) => person.distinct_ids[0], getPopoverHeader: () => `Person`, @@ -426,7 +431,7 @@ export const taxonomicFilterLogic = kea([ name: 'Insights', searchPlaceholder: 'insights', type: TaxonomicFilterGroupType.Insights, - endpoint: combineUrl(`api/projects/${teamId}/insights/`, { + endpoint: combineUrl(`api/environments/${teamId}/insights/`, { saved: true, }).url, getName: (insight: QueryBasedInsightModel) => insight.name, @@ -481,7 +486,7 @@ export const taxonomicFilterLogic = kea([ getName: (option: any) => option.name, getValue: (option) => option.name, getPopoverHeader: () => 'Session', - endpoint: `api/projects/${teamId}/sessions/property_definitions`, + endpoint: `api/environments/${teamId}/sessions/property_definitions`, getIcon: getPropertyDefinitionIcon, }, { @@ -500,6 +505,7 @@ export const taxonomicFilterLogic = kea([ valuesEndpoint: (key) => { if (key === 'visited_page') { return ( + `api/environments/${teamId}/events/values/?key=` + 'api/event/values/?key=' + encodeURIComponent('$current_url') + '&event_name=' + @@ -532,7 +538,7 @@ export const taxonomicFilterLogic = kea([ name: `${capitalizeFirstLetter(aggregationLabel(type.group_type_index).plural)}`, searchPlaceholder: `${aggregationLabel(type.group_type_index).plural}`, type: `${TaxonomicFilterGroupType.GroupNamesPrefix}_${type.group_type_index}` as unknown as TaxonomicFilterGroupType, - endpoint: combineUrl(`api/projects/${teamId}/groups/`, { + endpoint: combineUrl(`api/environments/${teamId}/groups/`, { group_type_index: type.group_type_index, }).url, searchAlias: 'group_key', @@ -543,13 +549,13 @@ export const taxonomicFilterLogic = kea([ })), ], groupAnalyticsTaxonomicGroups: [ - (s) => [s.groupTypes, s.currentTeamId, s.aggregationLabel], - (groupTypes, teamId, aggregationLabel): TaxonomicFilterGroup[] => + (s) => [s.groupTypes, s.currentProjectId, s.currentTeamId, s.aggregationLabel], + (groupTypes, projectId, teamId, aggregationLabel): TaxonomicFilterGroup[] => Array.from(groupTypes.values()).map((type) => ({ name: `${capitalizeFirstLetter(aggregationLabel(type.group_type_index).singular)} properties`, searchPlaceholder: `${aggregationLabel(type.group_type_index).singular} properties`, type: `${TaxonomicFilterGroupType.GroupsPrefix}_${type.group_type_index}` as unknown as TaxonomicFilterGroupType, - endpoint: combineUrl(`api/projects/${teamId}/property_definitions`, { + endpoint: combineUrl(`api/projects/${projectId}/property_definitions`, { type: 'group', group_type_index: type.group_type_index, }).url, diff --git a/frontend/src/lib/components/VersionChecker/versionCheckerLogic.test.ts b/frontend/src/lib/components/VersionChecker/versionCheckerLogic.test.ts index 2107640885e26..103e89b2bfed9 100644 --- a/frontend/src/lib/components/VersionChecker/versionCheckerLogic.test.ts +++ b/frontend/src/lib/components/VersionChecker/versionCheckerLogic.test.ts @@ -24,7 +24,7 @@ const useMockedVersions = ( ], }, post: { - '/api/projects/:team/query': () => [ + '/api/environments/:team_id/query': () => [ 200, { results: usedVersions.map((x) => [x.version, x.timestamp]), diff --git a/frontend/src/mocks/handlers.ts b/frontend/src/mocks/handlers.ts index 3b5898aa52e64..9d14c1b3c0acf 100644 --- a/frontend/src/mocks/handlers.ts +++ b/frontend/src/mocks/handlers.ts @@ -57,7 +57,8 @@ export const defaultMocks: Mocks = { '/api/projects/:team_id/annotations/': EMPTY_PAGINATED_RESPONSE, '/api/projects/:team_id/event_definitions/': EMPTY_PAGINATED_RESPONSE, '/api/projects/:team_id/cohorts/': toPaginatedResponse([MOCK_DEFAULT_COHORT]), - '/api/projects/:team_id/dashboards/': EMPTY_PAGINATED_RESPONSE, + '/api/environments/:team_id/dashboards/': EMPTY_PAGINATED_RESPONSE, + '/api/environments/:team_id/alerts/': EMPTY_PAGINATED_RESPONSE, '/api/projects/:team_id/dashboard_templates': EMPTY_PAGINATED_RESPONSE, '/api/projects/:team_id/dashboard_templates/repository/': [], '/api/projects/:team_id/external_data_sources/': EMPTY_PAGINATED_RESPONSE, @@ -76,8 +77,8 @@ export const defaultMocks: Mocks = { }, '/api/projects/:team_id/groups/': EMPTY_PAGINATED_RESPONSE, '/api/projects/:team_id/groups_types/': [], - '/api/projects/:team_id/insights/': EMPTY_PAGINATED_RESPONSE, - '/api/projects/:team_id/insights/:insight_id/sharing/': { + '/api/environments/:team_id/insights/': EMPTY_PAGINATED_RESPONSE, + '/api/environments/:team_id/insights/:insight_id/sharing/': { enabled: false, access_token: 'foo', created_at: '2020-11-11T00:00:00Z', @@ -86,7 +87,7 @@ export const defaultMocks: Mocks = { '/api/projects/:team_id/feature_flags/': EMPTY_PAGINATED_RESPONSE, '/api/projects/:team_id/feature_flags/:feature_flag_id/role_access': EMPTY_PAGINATED_RESPONSE, '/api/projects/:team_id/experiments/': EMPTY_PAGINATED_RESPONSE, - '/api/projects/:team_id/explicit_members/': [], + '/api/environments/:team_id/explicit_members/': [], '/api/projects/:team_id/warehouse_view_link/': EMPTY_PAGINATED_RESPONSE, '/api/projects/:team_id/warehouse_saved_queries/': EMPTY_PAGINATED_RESPONSE, '/api/projects/:team_id/warehouse_tables/': EMPTY_PAGINATED_RESPONSE, @@ -104,9 +105,9 @@ export const defaultMocks: Mocks = { '/api/organizations/@current/plugins/repository/': [], '/api/organizations/@current/plugins/unused/': [], '/api/plugin_config/': toPaginatedResponse([MOCK_DEFAULT_PLUGIN_CONFIG]), - [`/api/projects/:team_id/plugin_configs/${MOCK_DEFAULT_PLUGIN_CONFIG.id}/`]: MOCK_DEFAULT_PLUGIN_CONFIG, - '/api/projects/:team_id/persons': EMPTY_PAGINATED_RESPONSE, - '/api/projects/:team_id/persons/properties/': toPaginatedResponse(MOCK_PERSON_PROPERTIES), + [`/api/environments/:team_id/plugin_configs/${MOCK_DEFAULT_PLUGIN_CONFIG.id}/`]: MOCK_DEFAULT_PLUGIN_CONFIG, + '/api/environments/:team_id/persons': EMPTY_PAGINATED_RESPONSE, + '/api/environments/:team_id/persons/properties/': toPaginatedResponse(MOCK_PERSON_PROPERTIES), '/api/personal_api_keys/': [], '/api/users/@me/': (): MockSignature => [ 200, @@ -118,6 +119,7 @@ export const defaultMocks: Mocks = { }, }, ], + '/api/environments/@current/': MOCK_DEFAULT_TEAM, '/api/projects/@current/': MOCK_DEFAULT_TEAM, '/api/projects/:team_id/comments/count': { count: 0 }, '/api/projects/:team_id/comments': { results: [] }, @@ -157,8 +159,8 @@ export const defaultMocks: Mocks = { 'https://us.i.posthog.com/decide/': (req, res, ctx): MockSignature => posthogCORSResponse(req, res, ctx), '/decide/': (req, res, ctx): MockSignature => posthogCORSResponse(req, res, ctx), 'https://us.i.posthog.com/engage/': (req, res, ctx): MockSignature => posthogCORSResponse(req, res, ctx), - '/api/projects/:team_id/insights/:insight_id/viewed/': (): MockSignature => [201, null], - 'api/projects/:team_id/query': [200, { results: [] }], + '/api/environments/:team_id/insights/:insight_id/viewed/': (): MockSignature => [201, null], + 'api/environments/:team_id/query': [200, { results: [] }], }, patch: { '/api/projects/:team_id/session_recording_playlists/:playlist_id/': {}, diff --git a/frontend/src/models/dashboardsModel.test.ts b/frontend/src/models/dashboardsModel.test.ts index 8c021609e5ded..c3d42a78b83b7 100644 --- a/frontend/src/models/dashboardsModel.test.ts +++ b/frontend/src/models/dashboardsModel.test.ts @@ -62,7 +62,7 @@ describe('the dashboards model', () => { beforeEach(async () => { useMocks({ get: { - '/api/projects/:team_id/dashboards/': () => { + '/api/environments/:team_id/dashboards/': () => { return [ 200, { diff --git a/frontend/src/models/dashboardsModel.tsx b/frontend/src/models/dashboardsModel.tsx index f90650d8bb831..99c4cdf01ea96 100644 --- a/frontend/src/models/dashboardsModel.tsx +++ b/frontend/src/models/dashboardsModel.tsx @@ -92,7 +92,7 @@ export const dashboardsModel = kea([ return { count: 0, next: null, previous: null, results: [] } } const dashboards: PaginatedResponse = await api.get( - url || `api/projects/${teamLogic.values.currentTeamId}/dashboards/?limit=2000` + url || `api/environments/${teamLogic.values.currentTeamId}/dashboards/?limit=2000` ) return { @@ -115,7 +115,7 @@ export const dashboardsModel = kea([ const beforeChange = { ...values.rawDashboards[id] } const response = (await api.update( - `api/projects/${teamLogic.values.currentTeamId}/dashboards/${id}`, + `api/environments/${teamLogic.values.currentTeamId}/dashboards/${id}`, payload )) as DashboardType const updatedAttribute = Object.keys(payload)[0] @@ -135,7 +135,7 @@ export const dashboardsModel = kea([ label: 'Undo', action: async () => { const reverted = (await api.update( - `api/projects/${teamLogic.values.currentTeamId}/dashboards/${id}`, + `api/environments/${teamLogic.values.currentTeamId}/dashboards/${id}`, beforeChange )) as DashboardType actions.updateDashboardSuccess(getQueryBasedDashboard(reverted)) @@ -148,33 +148,39 @@ export const dashboardsModel = kea([ }, deleteDashboard: async ({ id, deleteInsights }) => getQueryBasedDashboard( - await api.update(`api/projects/${teamLogic.values.currentTeamId}/dashboards/${id}`, { + await api.update(`api/environments/${teamLogic.values.currentTeamId}/dashboards/${id}`, { deleted: true, delete_insights: deleteInsights, }) ) as DashboardType, restoreDashboard: async ({ id }) => getQueryBasedDashboard( - await api.update(`api/projects/${teamLogic.values.currentTeamId}/dashboards/${id}`, { + await api.update(`api/environments/${teamLogic.values.currentTeamId}/dashboards/${id}`, { deleted: false, }) ) as DashboardType, pinDashboard: async ({ id, source }) => { - const response = (await api.update(`api/projects/${teamLogic.values.currentTeamId}/dashboards/${id}`, { - pinned: true, - })) as DashboardType + const response = (await api.update( + `api/environments/${teamLogic.values.currentTeamId}/dashboards/${id}`, + { + pinned: true, + } + )) as DashboardType eventUsageLogic.actions.reportDashboardPinToggled(true, source) return getQueryBasedDashboard(response)! }, unpinDashboard: async ({ id, source }) => { - const response = (await api.update(`api/projects/${teamLogic.values.currentTeamId}/dashboards/${id}`, { - pinned: false, - })) as DashboardType + const response = (await api.update( + `api/environments/${teamLogic.values.currentTeamId}/dashboards/${id}`, + { + pinned: false, + } + )) as DashboardType eventUsageLogic.actions.reportDashboardPinToggled(false, source) return getQueryBasedDashboard(response)! }, duplicateDashboard: async ({ id, name, show, duplicateTiles }) => { - const result = (await api.create(`api/projects/${teamLogic.values.currentTeamId}/dashboards/`, { + const result = (await api.create(`api/environments/${teamLogic.values.currentTeamId}/dashboards/`, { use_dashboard: id, name: `${name} (Copy)`, duplicate_tiles: duplicateTiles, diff --git a/frontend/src/queries/nodes/DataNode/DataNode.stories.tsx b/frontend/src/queries/nodes/DataNode/DataNode.stories.tsx index 7b20caa2b8037..4868de5e4fe3e 100644 --- a/frontend/src/queries/nodes/DataNode/DataNode.stories.tsx +++ b/frontend/src/queries/nodes/DataNode/DataNode.stories.tsx @@ -19,8 +19,8 @@ const meta: Meta = { decorators: [ mswDecorator({ get: { - '/api/projects/:team_id/events': events, - '/api/projects/:team_id/persons': persons, + '/api/environments/:team_id/events': events, + '/api/environments/:team_id/persons': persons, }, }), ], diff --git a/frontend/src/queries/nodes/DataNode/dataNodeLogic.queryCancellation.test.ts b/frontend/src/queries/nodes/DataNode/dataNodeLogic.queryCancellation.test.ts index be84e5ad61e14..9401a11605d7c 100644 --- a/frontend/src/queries/nodes/DataNode/dataNodeLogic.queryCancellation.test.ts +++ b/frontend/src/queries/nodes/DataNode/dataNodeLogic.queryCancellation.test.ts @@ -17,7 +17,7 @@ describe('dataNodeLogic - query cancellation', () => { featureFlagLogic.mount() useMocks({ get: { - '/api/projects/:team/insights/trend/': async (req) => { + '/api/environments/:team_id/insights/trend/': async (req) => { if (req.url.searchParams.get('date_from') === '-180d') { // delay for a second before response without pausing return new Promise((resolve) => @@ -30,8 +30,8 @@ describe('dataNodeLogic - query cancellation', () => { }, }, post: { - '/api/projects/997/insights/cancel/': [201], - '/api/projects/997/query/': async () => { + '/api/environments/997/insights/cancel/': [201], + '/api/environments/997/query/': async () => { return new Promise((resolve) => setTimeout(() => { resolve([200, { result: ['slow result from api'] }]) @@ -40,7 +40,7 @@ describe('dataNodeLogic - query cancellation', () => { }, }, delete: { - '/api/projects/:team_id/query/uuid-first': [200, {}], + '/api/environments/:team_id/query/uuid-first': [200, {}], }, }) }) diff --git a/frontend/src/queries/nodes/DataNode/dataNodeLogic.ts b/frontend/src/queries/nodes/DataNode/dataNodeLogic.ts index 997857b202f9c..6d1bdfae9ff6e 100644 --- a/frontend/src/queries/nodes/DataNode/dataNodeLogic.ts +++ b/frontend/src/queries/nodes/DataNode/dataNodeLogic.ts @@ -652,7 +652,7 @@ export const dataNodeLogic = kea([ abortQuery: async ({ queryId }) => { try { const { currentTeamId } = values - await api.delete(`api/projects/${currentTeamId}/query/${queryId}/`) + await api.delete(`api/environments/${currentTeamId}/query/${queryId}/`) } catch (e) { console.warn('Failed cancelling query', e) } diff --git a/frontend/src/queries/nodes/DataTable/DataTable.stories.tsx b/frontend/src/queries/nodes/DataTable/DataTable.stories.tsx index 64a978e45f95d..e909710177813 100644 --- a/frontend/src/queries/nodes/DataTable/DataTable.stories.tsx +++ b/frontend/src/queries/nodes/DataTable/DataTable.stories.tsx @@ -19,8 +19,8 @@ const meta: Meta = { decorators: [ mswDecorator({ get: { - '/api/projects/:team_id/events': events, - '/api/projects/:team_id/persons': persons, + '/api/environments/:team_id/events': events, + '/api/environments/:team_id/persons': persons, }, }), ], diff --git a/frontend/src/queries/query.test.ts b/frontend/src/queries/query.test.ts index f5783bebd2113..060333b75be19 100644 --- a/frontend/src/queries/query.test.ts +++ b/frontend/src/queries/query.test.ts @@ -11,7 +11,7 @@ describe('query', () => { beforeEach(() => { useMocks({ post: { - '/api/projects/:team/query': (req) => { + '/api/environments/:team_id/query': (req) => { const data = req.body as any if (data.query?.kind === 'HogQLQuery') { return [200, { results: [], clickhouse: 'clickhouse string', hogql: 'hogql string' }] diff --git a/frontend/src/scenes/activity/explore/Events.stories.tsx b/frontend/src/scenes/activity/explore/Events.stories.tsx index d38e1686b4ec9..0545ece9a5640 100644 --- a/frontend/src/scenes/activity/explore/Events.stories.tsx +++ b/frontend/src/scenes/activity/explore/Events.stories.tsx @@ -14,7 +14,7 @@ const meta: Meta = { decorators: [ mswDecorator({ post: { - '/api/projects/:team_id/query': eventsQuery, + '/api/environments/:team_id/query': eventsQuery, }, }), ], diff --git a/frontend/src/scenes/appContextLogic.ts b/frontend/src/scenes/appContextLogic.ts index ce3a6a11a317c..3e17e10876136 100644 --- a/frontend/src/scenes/appContextLogic.ts +++ b/frontend/src/scenes/appContextLogic.ts @@ -7,6 +7,7 @@ import { UserType } from '~/types' import type { appContextLogicType } from './appContextLogicType' import { organizationLogic } from './organizationLogic' +import { projectLogic } from './projectLogic' import { teamLogic } from './teamLogic' import { userLogic } from './userLogic' @@ -19,7 +20,9 @@ export const appContextLogic = kea([ organizationLogic, ['loadCurrentOrganizationSuccess'], teamLogic, - ['loadCurrentTeam', 'loadCurrentTeamSuccess'], + ['loadCurrentTeam'], + projectLogic, + ['loadCurrentProject'], ], }), afterMount(({ actions }) => { @@ -43,6 +46,7 @@ export const appContextLogic = kea([ // NOTE: This doesn't fix the issue but removes the confusion of seeing incorrect user info in the UI actions.loadUserSuccess(remoteUser) actions.loadCurrentOrganizationSuccess(remoteUser.organization) + actions.loadCurrentProject() actions.loadCurrentTeam() } }) diff --git a/frontend/src/scenes/dashboard/DashboardInsightCardLegend.stories.tsx b/frontend/src/scenes/dashboard/DashboardInsightCardLegend.stories.tsx index 5c9caef93e5d2..bf3eebbc439e1 100644 --- a/frontend/src/scenes/dashboard/DashboardInsightCardLegend.stories.tsx +++ b/frontend/src/scenes/dashboard/DashboardInsightCardLegend.stories.tsx @@ -11,8 +11,8 @@ const meta: Meta = { decorators: [ mswDecorator({ get: { - '/api/projects/:team_id/dashboards/1/': require('./__mocks__/dashboard_insight_card_legend_query.json'), - '/api/projects/:team_id/dashboards/2/': require('./__mocks__/dashboard_insight_card_legend_legacy.json'), + '/api/environments/:team_id/dashboards/1/': require('./__mocks__/dashboard_insight_card_legend_query.json'), + '/api/environments/:team_id/dashboards/2/': require('./__mocks__/dashboard_insight_card_legend_legacy.json'), }, }), ], diff --git a/frontend/src/scenes/dashboard/Dashboards.stories.tsx b/frontend/src/scenes/dashboard/Dashboards.stories.tsx index 34e6be5f20878..16d30235fa2a2 100644 --- a/frontend/src/scenes/dashboard/Dashboards.stories.tsx +++ b/frontend/src/scenes/dashboard/Dashboards.stories.tsx @@ -18,13 +18,13 @@ const meta: Meta = { decorators: [ mswDecorator({ get: { - '/api/projects/:team_id/dashboards/': require('./__mocks__/dashboards.json'), - '/api/projects/:team_id/dashboards/1/': require('./__mocks__/dashboard1.json'), - '/api/projects/:team_id/dashboards/1/collaborators/': [], - '/api/projects/:team_id/dashboards/2/': [500, { detail: 'Server error' }], + '/api/environments/:team_id/dashboards/': require('./__mocks__/dashboards.json'), + '/api/environments/:team_id/dashboards/1/': require('./__mocks__/dashboard1.json'), + '/api/environments/:team_id/dashboards/1/collaborators/': [], + '/api/environments/:team_id/dashboards/2/': [500, { detail: 'Server error' }], '/api/projects/:team_id/dashboard_templates/': require('./__mocks__/dashboard_templates.json'), '/api/projects/:team_id/dashboard_templates/json_schema/': require('./__mocks__/dashboard_template_schema.json'), - '/api/projects/:team_id/dashboards/:dash_id/sharing/': { + '/api/environments/:team_id/dashboards/:dash_id/sharing/': { created_at: '2023-02-25T13:28:20.454940Z', enabled: false, access_token: 'a-secret-token', diff --git a/frontend/src/scenes/dashboard/dashboardLogic.test.ts b/frontend/src/scenes/dashboard/dashboardLogic.test.ts index fea16483894fd..cb60271cae3bf 100644 --- a/frontend/src/scenes/dashboard/dashboardLogic.test.ts +++ b/frontend/src/scenes/dashboard/dashboardLogic.test.ts @@ -179,7 +179,7 @@ describe('dashboardLogic', () => { } useMocks({ get: { - '/api/projects/:team/query/123/': () => [ + '/api/environments/:team_id/query/123/': () => [ 200, { query_status: { @@ -187,14 +187,14 @@ describe('dashboardLogic', () => { }, }, ], - '/api/projects/:team/dashboards/5/': { ...dashboards['5'] }, - '/api/projects/:team/dashboards/6/': { ...dashboards['6'] }, - '/api/projects/:team/dashboards/7/': () => [500, '💣'], - '/api/projects/:team/dashboards/8/': { ...dashboards['8'] }, - '/api/projects/:team/dashboards/9/': { ...dashboards['9'] }, - '/api/projects/:team/dashboards/10/': { ...dashboards['10'] }, - '/api/projects/:team/dashboards/11/': { ...dashboards['11'] }, - '/api/projects/:team/dashboards/': { + '/api/environments/:team_id/dashboards/5/': { ...dashboards['5'] }, + '/api/environments/:team_id/dashboards/6/': { ...dashboards['6'] }, + '/api/environments/:team_id/dashboards/7/': () => [500, '💣'], + '/api/environments/:team_id/dashboards/8/': { ...dashboards['8'] }, + '/api/environments/:team_id/dashboards/9/': { ...dashboards['9'] }, + '/api/environments/:team_id/dashboards/10/': { ...dashboards['10'] }, + '/api/environments/:team_id/dashboards/11/': { ...dashboards['11'] }, + '/api/environments/:team_id/dashboards/': { count: 6, next: null, previous: null, @@ -206,9 +206,9 @@ describe('dashboardLogic', () => { { ...dashboards['10'] }, ], }, - '/api/projects/:team/insights/1001/': () => [500, '💣'], - '/api/projects/:team/insights/800/': () => [200, { ...insights['800'] }], - '/api/projects/:team/insights/:id/': (req) => { + '/api/environments/:team_id/insights/1001/': () => [500, '💣'], + '/api/environments/:team_id/insights/800/': () => [200, { ...insights['800'] }], + '/api/environments/:team_id/insights/:id/': (req) => { const dashboard = req.url.searchParams.get('from_dashboard') if (!dashboard) { throw new Error('the logic must always add this param') @@ -221,15 +221,15 @@ describe('dashboardLogic', () => { }, }, post: { - '/api/projects/:team/insights/cancel/': [201], + '/api/environments/:team_id/insights/cancel/': [201], }, patch: { - '/api/projects/:team/dashboards/:id/': async (req) => { + '/api/environments/:team_id/dashboards/:id/': async (req) => { const dashboardId = typeof req.params['id'] === 'string' ? req.params['id'] : req.params['id'][0] const payload = await req.json() return [200, { ...dashboards[dashboardId], ...payload }] }, - '/api/projects/:team/dashboards/:id/move_tile/': async (req) => { + '/api/environments/:team_id/dashboards/:id/move_tile/': async (req) => { // backend updates the two dashboards and the insight const jsonPayload = await req.json() const { toDashboard, tile: tileToUpdate } = jsonPayload @@ -256,7 +256,7 @@ describe('dashboardLogic', () => { return [200, { ...from }] }, - '/api/projects/:team/insights/:id/': async (req) => { + '/api/environments/:team_id/insights/:id/': async (req) => { try { const updates = await req.json() if (typeof updates !== 'object') { @@ -309,7 +309,7 @@ describe('dashboardLogic', () => { logic.actions.updateFiltersAndLayouts() }).toFinishAllListeners() - expect(api.update).toHaveBeenCalledWith(`api/projects/${MOCK_TEAM_ID}/dashboards/5`, { + expect(api.update).toHaveBeenCalledWith(`api/environments/${MOCK_TEAM_ID}/dashboards/5`, { tiles: [ { id: 0, @@ -393,7 +393,7 @@ describe('dashboardLogic', () => { await expectLogic(dashboardEightlogic).toFinishAllListeners() expect(api.update).toHaveBeenCalledWith( - `api/projects/${MOCK_TEAM_ID}/dashboards/${9}/move_tile`, + `api/environments/${MOCK_TEAM_ID}/dashboards/${9}/move_tile`, expect.objectContaining({ tile: sourceTile, toDashboard: 8 }) ) }) diff --git a/frontend/src/scenes/dashboard/dashboardLogic.tsx b/frontend/src/scenes/dashboard/dashboardLogic.tsx index 2cbb301df28d3..17b478a1338dc 100644 --- a/frontend/src/scenes/dashboard/dashboardLogic.tsx +++ b/frontend/src/scenes/dashboard/dashboardLogic.tsx @@ -141,7 +141,7 @@ async function getSingleInsight( methodOptions?: ApiMethodOptions, filtersOverride?: DashboardFilter ): Promise { - const apiUrl = `api/projects/${currentTeamId}/insights/${insight.id}/?${toParams({ + const apiUrl = `api/environments/${currentTeamId}/insights/${insight.id}/?${toParams({ refresh, from_dashboard: dashboardId, // needed to load insight in correct context client_query_id: queryId, @@ -294,7 +294,7 @@ export const dashboardLogic = kea([ breakpoint() const dashboard: DashboardType = await api.update( - `api/projects/${values.currentTeamId}/dashboards/${props.id}`, + `api/environments/${values.currentTeamId}/dashboards/${props.id}`, { filters: values.filters, tiles: layoutsToUpdate, @@ -307,7 +307,7 @@ export const dashboardLogic = kea([ } }, updateTileColor: async ({ tileId, color }) => { - await api.update(`api/projects/${values.currentTeamId}/dashboards/${props.id}`, { + await api.update(`api/environments/${values.currentTeamId}/dashboards/${props.id}`, { tiles: [{ id: tileId, color }], }) const matchingTile = values.tiles.find((tile) => tile.id === tileId) @@ -318,7 +318,7 @@ export const dashboardLogic = kea([ }, removeTile: async ({ tile }) => { try { - await api.update(`api/projects/${values.currentTeamId}/dashboards/${props.id}`, { + await api.update(`api/environments/${values.currentTeamId}/dashboards/${props.id}`, { tiles: [{ id: tile.id, deleted: true }], }) dashboardsModel.actions.tileRemovedFromDashboard({ @@ -361,7 +361,7 @@ export const dashboardLogic = kea([ } const dashboard: DashboardType = await api.update( - `api/projects/${values.currentTeamId}/dashboards/${props.id}`, + `api/environments/${values.currentTeamId}/dashboards/${props.id}`, { tiles: [newTile], } @@ -381,7 +381,7 @@ export const dashboardLogic = kea([ return values.dashboard } const dashboard: DashboardType = await api.update( - `api/projects/${teamLogic.values.currentTeamId}/dashboards/${props.id}/move_tile`, + `api/environments/${teamLogic.values.currentTeamId}/dashboards/${props.id}/move_tile`, { tile, toDashboard, @@ -732,7 +732,7 @@ export const dashboardLogic = kea([ () => [(_, props) => props.id], (id) => { return (refresh?: RefreshType, filtersOverride?: DashboardFilter) => - `api/projects/${teamLogic.values.currentTeamId}/dashboards/${id}/?${toParams({ + `api/environments/${teamLogic.values.currentTeamId}/dashboards/${id}/?${toParams({ refresh, filters_override: filtersOverride, })}` @@ -1282,7 +1282,7 @@ export const dashboardLogic = kea([ abortQuery: async ({ dashboardQueryId, queryId, queryStartTime }) => { const { currentTeamId } = values - await api.create(`api/projects/${currentTeamId}/insights/cancel`, { client_query_id: dashboardQueryId }) + await api.create(`api/environments/${currentTeamId}/insights/cancel`, { client_query_id: dashboardQueryId }) // TRICKY: we cancel just once using the dashboard query id. // we can record the queryId that happened to capture the AbortError exception diff --git a/frontend/src/scenes/dashboard/dashboards/dashboardsLogic.test.ts b/frontend/src/scenes/dashboard/dashboards/dashboardsLogic.test.ts index 61d98a07a914f..d3723a18f7deb 100644 --- a/frontend/src/scenes/dashboard/dashboards/dashboardsLogic.test.ts +++ b/frontend/src/scenes/dashboard/dashboards/dashboardsLogic.test.ts @@ -39,7 +39,7 @@ describe('dashboardsLogic', () => { beforeEach(async () => { useMocks({ get: { - '/api/projects/:team/dashboards/': { + '/api/environments/:team_id/dashboards/': { count: 6, next: null, previous: null, diff --git a/frontend/src/scenes/dashboard/newDashboardLogic.ts b/frontend/src/scenes/dashboard/newDashboardLogic.ts index 44fa0252f5889..6749067872258 100644 --- a/frontend/src/scenes/dashboard/newDashboardLogic.ts +++ b/frontend/src/scenes/dashboard/newDashboardLogic.ts @@ -137,7 +137,7 @@ export const newDashboardLogic = kea([ actions.setIsLoading(true) try { const result: DashboardType = await api.create( - `api/projects/${teamLogic.values.currentTeamId}/dashboards/`, + `api/environments/${teamLogic.values.currentTeamId}/dashboards/`, { name: name, description: description, @@ -195,7 +195,7 @@ export const newDashboardLogic = kea([ try { const result: DashboardType = await api.create( - `api/projects/${teamLogic.values.currentTeamId}/dashboards/create_from_template_json`, + `api/environments/${teamLogic.values.currentTeamId}/dashboards/create_from_template_json`, { template: dashboardJSON, creation_context: creationContext } ) actions.hideNewDashboardModal() diff --git a/frontend/src/scenes/data-management/DataManagementScene.stories.tsx b/frontend/src/scenes/data-management/DataManagementScene.stories.tsx index 0df671d07f3bd..b8ce6c6ae967d 100644 --- a/frontend/src/scenes/data-management/DataManagementScene.stories.tsx +++ b/frontend/src/scenes/data-management/DataManagementScene.stories.tsx @@ -238,7 +238,7 @@ const meta: Meta = { }, }, post: { - '/api/projects/:team_id/query/': (req) => { + '/api/environments/:team_id/query/': (req) => { if ((req.body as any).query.kind === 'DatabaseSchemaQuery') { return [200, MOCK_DATABASE] } diff --git a/frontend/src/scenes/data-management/events/eventDefinitionsTableLogic.test.ts b/frontend/src/scenes/data-management/events/eventDefinitionsTableLogic.test.ts index 2767047a54cdc..461b35d5a3acc 100644 --- a/frontend/src/scenes/data-management/events/eventDefinitionsTableLogic.test.ts +++ b/frontend/src/scenes/data-management/events/eventDefinitionsTableLogic.test.ts @@ -98,7 +98,7 @@ describe('eventDefinitionsTableLogic', () => { ] } }, - '/api/projects/:team/events': (req) => { + '/api/environments/:team_id/events': (req) => { if ( req.url.searchParams.get('limit') === '1' && req.url.searchParams.get('event') === 'event_with_example' @@ -259,13 +259,13 @@ describe('eventDefinitionsTableLogic', () => { [propertiesStartingUrl]: partial({ count: 5, }), - [`api/projects/${MOCK_TEAM_ID}/events?event=event1&limit=1`]: partial(mockEvent.properties), + [`api/environments/${MOCK_TEAM_ID}/events?event=event1&limit=1`]: partial(mockEvent.properties), }), }) expect(api.get).toHaveBeenCalledTimes(3) expect(api.get).toHaveBeenNthCalledWith(1, propertiesStartingUrl) - expect(api.get).toHaveBeenNthCalledWith(2, `api/projects/${MOCK_TEAM_ID}/events?event=event1&limit=1`) + expect(api.get).toHaveBeenNthCalledWith(2, `api/environments/${MOCK_TEAM_ID}/events?event=event1&limit=1`) expect(api.get).toHaveBeenNthCalledWith(3, startingUrl) await expectLogic(logic, () => { diff --git a/frontend/src/scenes/error-tracking/ErrorTracking.stories.tsx b/frontend/src/scenes/error-tracking/ErrorTracking.stories.tsx index 348dc537034fd..2a97ca624ab98 100644 --- a/frontend/src/scenes/error-tracking/ErrorTracking.stories.tsx +++ b/frontend/src/scenes/error-tracking/ErrorTracking.stories.tsx @@ -20,7 +20,7 @@ const meta: Meta = { decorators: [ mswDecorator({ post: { - '/api/projects/:team_id/query': async (req, res, ctx) => { + '/api/environments/:team_id/query': async (req, res, ctx) => { const query = (await req.clone().json()).query if (query.kind === NodeKind.ErrorTrackingQuery) { return res(ctx.json(errorTrackingQueryResponse)) diff --git a/frontend/src/scenes/feature-flags/FeatureFlags.stories.tsx b/frontend/src/scenes/feature-flags/FeatureFlags.stories.tsx index debe6ee739398..acf32b9788ed5 100644 --- a/frontend/src/scenes/feature-flags/FeatureFlags.stories.tsx +++ b/frontend/src/scenes/feature-flags/FeatureFlags.stories.tsx @@ -35,7 +35,7 @@ const meta: Meta = { ], }, post: { - '/api/projects/:team_id/query': {}, + '/api/environments/:team_id/query': {}, // flag targeting has loaders, make sure they don't keep loading '/api/projects/:team_id/feature_flags/user_blast_radius/': () => [ 200, diff --git a/frontend/src/scenes/feature-flags/featureFlagLogic.ts b/frontend/src/scenes/feature-flags/featureFlagLogic.ts index f01c917bd8a9a..db2811ef1f44b 100644 --- a/frontend/src/scenes/feature-flags/featureFlagLogic.ts +++ b/frontend/src/scenes/feature-flags/featureFlagLogic.ts @@ -16,6 +16,7 @@ import { experimentLogic } from 'scenes/experiments/experimentLogic' import { featureFlagsLogic, FeatureFlagsTab } from 'scenes/feature-flags/featureFlagsLogic' import { filterTrendsClientSideParams } from 'scenes/insights/sharedUtils' import { cleanFilters } from 'scenes/insights/utils/cleanFilters' +import { projectLogic } from 'scenes/projectLogic' import { Scene } from 'scenes/sceneTypes' import { NEW_SURVEY, NewSurvey } from 'scenes/surveys/constants' import { urls } from 'scenes/urls' @@ -186,6 +187,8 @@ export const featureFlagLogic = kea([ values: [ teamLogic, ['currentTeamId'], + projectLogic, + ['currentProjectId'], groupsModel, ['aggregationLabel'], userLogic, @@ -472,13 +475,16 @@ export const featureFlagLogic = kea([ try { let savedFlag: FeatureFlagType if (!updatedFlag.id) { - savedFlag = await api.create(`api/projects/${values.currentTeamId}/feature_flags`, preparedFlag) + savedFlag = await api.create( + `api/projects/${values.currentProjectId}/feature_flags`, + preparedFlag + ) if (values.roleBasedAccessEnabled && savedFlag.id) { featureFlagPermissionsLogic({ flagId: null })?.actions.addAssociatedRoles(savedFlag.id) } } else { savedFlag = await api.update( - `api/projects/${values.currentTeamId}/feature_flags/${updatedFlag.id}`, + `api/projects/${values.currentProjectId}/feature_flags/${updatedFlag.id}`, preparedFlag ) } @@ -525,7 +531,7 @@ export const featureFlagLogic = kea([ loadRelatedInsights: async () => { if (props.id && props.id !== 'new' && values.featureFlag.key) { const response = await api.get>( - `api/projects/${values.currentTeamId}/insights/?feature_flag=${values.featureFlag.key}&order=-created_at` + `api/environments/${values.currentProjectId}/insights/?feature_flag=${values.featureFlag.key}&order=-created_at` ) return response.results.map((legacyInsight) => getQueryBasedInsightModel(legacyInsight)) } @@ -665,13 +671,13 @@ export const featureFlagLogic = kea([ })), listeners(({ actions, values, props }) => ({ submitNewDashboardSuccessWithResult: async ({ result }) => { - await api.update(`api/projects/${values.currentTeamId}/feature_flags/${values.featureFlag.id}`, { + await api.update(`api/projects/${values.currentProjectId}/feature_flags/${values.featureFlag.id}`, { analytics_dashboards: [result.id], }) }, generateUsageDashboard: async () => { if (props.id) { - await api.create(`api/projects/${values.currentTeamId}/feature_flags/${props.id}/dashboard`) + await api.create(`api/projects/${values.currentProjectId}/feature_flags/${props.id}/dashboard`) actions.loadFeatureFlag() } }, @@ -679,7 +685,7 @@ export const featureFlagLogic = kea([ if (props.id) { await breakpoint(1000) // in ms await api.create( - `api/projects/${values.currentTeamId}/feature_flags/${props.id}/enrich_usage_dashboard` + `api/projects/${values.currentProjectId}/feature_flags/${props.id}/enrich_usage_dashboard` ) } }, @@ -712,7 +718,7 @@ export const featureFlagLogic = kea([ }, deleteFeatureFlag: async ({ featureFlag }) => { await deleteWithUndo({ - endpoint: `projects/${values.currentTeamId}/feature_flags`, + endpoint: `projects/${values.currentProjectId}/feature_flags`, object: { name: featureFlag.key, id: featureFlag.id }, callback: () => { featureFlag.id && actions.deleteFlag(featureFlag.id) @@ -734,7 +740,7 @@ export const featureFlagLogic = kea([ loadInsightAtIndex: async ({ index, filters }) => { if (filters) { const response = await api.get( - `api/projects/${values.currentTeamId}/insights/trend/?${toParams( + `api/environments/${values.currentProjectId}/insights/trend/?${toParams( filterTrendsClientSideParams(filters) )}` ) diff --git a/frontend/src/scenes/funnels/funnelPersonsModalLogic.test.ts b/frontend/src/scenes/funnels/funnelPersonsModalLogic.test.ts index c649ccb59c42e..73d469b842e07 100644 --- a/frontend/src/scenes/funnels/funnelPersonsModalLogic.test.ts +++ b/frontend/src/scenes/funnels/funnelPersonsModalLogic.test.ts @@ -22,7 +22,7 @@ describe('funnelPersonsModalLogic', () => { beforeEach(() => { useMocks({ get: { - '/api/projects/:team/insights/': { + '/api/environments/:team_id/insights/': { results: [{}], }, }, diff --git a/frontend/src/scenes/funnels/funnelPropertyCorrelationLogic.test.ts b/frontend/src/scenes/funnels/funnelPropertyCorrelationLogic.test.ts index 0101a3c56e120..e2821b8499d92 100644 --- a/frontend/src/scenes/funnels/funnelPropertyCorrelationLogic.test.ts +++ b/frontend/src/scenes/funnels/funnelPropertyCorrelationLogic.test.ts @@ -29,10 +29,10 @@ describe('funnelPropertyCorrelationLogic', () => { correlation_config: correlationConfig, }, ], - '/api/projects/:team/insights/': { results: [{}] }, - '/api/projects/:team/insights/:id/': {}, + '/api/environments/:team_id/insights/': { results: [{}] }, + '/api/environments/:team_id/insights/:id/': {}, '/api/projects/:team/groups_types/': [], - '/api/projects/:team/persons/properties': [ + '/api/environments/:team_id/persons/properties': [ { name: 'some property', count: 20 }, { name: 'another property', count: 10 }, { name: 'third property', count: 5 }, @@ -59,7 +59,7 @@ describe('funnelPropertyCorrelationLogic', () => { ], }, post: { - '/api/projects/:team/insights/funnel/correlation': (req) => { + '/api/environments/:team_id/insights/funnel/correlation': (req) => { const data = req.body as any const excludePropertyFromProjectNames = data?.funnel_correlation_exclude_names || [] const includePropertyNames = data?.funnel_correlation_names || [] diff --git a/frontend/src/scenes/heatmaps/HeatmapsBrowser.stories.tsx b/frontend/src/scenes/heatmaps/HeatmapsBrowser.stories.tsx index e043ae3025e24..b89a5f653e429 100644 --- a/frontend/src/scenes/heatmaps/HeatmapsBrowser.stories.tsx +++ b/frontend/src/scenes/heatmaps/HeatmapsBrowser.stories.tsx @@ -21,7 +21,7 @@ const meta: Meta = { '/api/projects/:team_id/integrations': {}, }, post: { - '/api/projects/:team_id/query': async (req, res, ctx) => { + '/api/environments/:team_id/query': async (req, res, ctx) => { const qry = (await req.clone().json()).query.query // top urls query if (qry.startsWith('SELECT properties.$current_url AS url, count()')) { diff --git a/frontend/src/scenes/insights/EmptyStates/EmptyStates.stories.tsx b/frontend/src/scenes/insights/EmptyStates/EmptyStates.stories.tsx index 15e23154d2fef..8a0e5c91af302 100644 --- a/frontend/src/scenes/insights/EmptyStates/EmptyStates.stories.tsx +++ b/frontend/src/scenes/insights/EmptyStates/EmptyStates.stories.tsx @@ -27,7 +27,7 @@ export default meta export const Empty: StoryFn = () => { useStorybookMocks({ get: { - '/api/projects/:team_id/insights/': (_, __, ctx) => [ + '/api/environments/:team_id/insights/': (_, __, ctx) => [ ctx.delay(100), ctx.status(200), ctx.json({ count: 1, results: [{ ...insight, result: [] }] }), @@ -43,12 +43,12 @@ export const Empty: StoryFn = () => { export const ServerError: StoryFn = () => { useStorybookMocks({ get: { - '/api/projects/:team_id/insights/': (_, __, ctx) => [ + '/api/environments/:team_id/insights/': (_, __, ctx) => [ ctx.delay(100), ctx.status(200), ctx.json({ count: 1, results: [{ ...insight, result: null }] }), ], - '/api/projects/:team_id/insights/:id': (_, __, ctx) => [ + '/api/environments/:team_id/insights/:id': (_, __, ctx) => [ ctx.delay(100), ctx.status(500), ctx.json({ @@ -67,14 +67,14 @@ export const ServerError: StoryFn = () => { export const ValidationError: StoryFn = () => { useStorybookMocks({ get: { - '/api/projects/:team_id/insights/': (_, __, ctx) => [ + '/api/environments/:team_id/insights/': (_, __, ctx) => [ ctx.delay(100), ctx.status(200), ctx.json({ count: 1, results: [{ ...insight, result: null }] }), ], }, post: { - '/api/projects/:team_id/insights/:id': (_, __, ctx) => [ + '/api/environments/:team_id/insights/:id': (_, __, ctx) => [ ctx.delay(100), ctx.status(400), ctx.json({ @@ -93,13 +93,13 @@ export const ValidationError: StoryFn = () => { export const EstimatedQueryExecutionTimeTooLong: StoryFn = () => { useStorybookMocks({ get: { - '/api/projects/:team_id/insights/': (_, __, ctx) => [ + '/api/environments/:team_id/insights/': (_, __, ctx) => [ ctx.status(200), ctx.json({ count: 1, results: [{ ...insight, result: null }] }), ], }, post: { - '/api/projects/:team_id/query/': (_, __, ctx) => [ + '/api/environments/:team_id/query/': (_, __, ctx) => [ ctx.delay(100), ctx.status(512), ctx.json({ @@ -124,13 +124,13 @@ EstimatedQueryExecutionTimeTooLong.parameters = { export const LongLoading: StoryFn = () => { useStorybookMocks({ get: { - '/api/projects/:team_id/insights/': (_, __, ctx) => [ + '/api/environments/:team_id/insights/': (_, __, ctx) => [ ctx.status(200), ctx.json({ count: 1, results: [{ ...insight, result: null }] }), ], }, post: { - '/api/projects/:team_id/query/': (_, __, ctx) => [ctx.delay('infinite')], + '/api/environments/:team_id/query/': (_, __, ctx) => [ctx.delay('infinite')], }, }) useEffect(() => { diff --git a/frontend/src/scenes/insights/EmptyStates/EmptyStates.tsx b/frontend/src/scenes/insights/EmptyStates/EmptyStates.tsx index 95283388cb974..06f0928dc54f3 100644 --- a/frontend/src/scenes/insights/EmptyStates/EmptyStates.tsx +++ b/frontend/src/scenes/insights/EmptyStates/EmptyStates.tsx @@ -273,9 +273,11 @@ export function InsightErrorState({ excludeDetail, title, query, queryId }: Insi )} -
- Query ID: {queryId} -
+ {queryId && ( +
+ Query ID: {queryId} +
+ )} {query && ( { beforeEach(async () => { useMocks({ get: { - '/api/projects/:team/insights/trend/': async () => { + '/api/environments/:team_id/insights/trend/': async () => { return [200, { result: ['result from api'] }] }, }, post: { - '/api/projects/:team/insights/funnel/': { result: ['result from api'] }, + '/api/environments/:team_id/insights/funnel/': { result: ['result from api'] }, }, }) initKeaTests(true, { ...MOCK_DEFAULT_TEAM, test_account_filters_default_checked: true }) diff --git a/frontend/src/scenes/insights/Insights.stories.tsx b/frontend/src/scenes/insights/Insights.stories.tsx index c59e304f4be68..c97f692eb5bd0 100644 --- a/frontend/src/scenes/insights/Insights.stories.tsx +++ b/frontend/src/scenes/insights/Insights.stories.tsx @@ -24,8 +24,8 @@ const meta: Meta = { decorators: [ mswDecorator({ get: { - '/api/projects/:team_id/persons/retention': sampleRetentionPeopleResponse, - '/api/projects/:team_id/persons/properties': samplePersonProperties, + '/api/environments/:team_id/persons/retention': sampleRetentionPeopleResponse, + '/api/environments/:team_id/persons/properties': samplePersonProperties, '/api/projects/:team_id/groups_types': [], }, post: { diff --git a/frontend/src/scenes/insights/__mocks__/createInsightScene.tsx b/frontend/src/scenes/insights/__mocks__/createInsightScene.tsx index 0032cb2baafe6..819a35716abdd 100644 --- a/frontend/src/scenes/insights/__mocks__/createInsightScene.tsx +++ b/frontend/src/scenes/insights/__mocks__/createInsightScene.tsx @@ -16,7 +16,7 @@ export function createInsightStory( return function InsightStory() { useStorybookMocks({ get: { - '/api/projects/:team_id/insights/': (_, __, ctx) => [ + '/api/environments/:team_id/insights/': (_, __, ctx) => [ ctx.status(200), ctx.json({ count: 1, @@ -35,7 +35,7 @@ export function createInsightStory( ], }, post: { - '/api/projects/:team_id/query/': (req, __, ctx) => [ + '/api/environments/:team_id/query/': (req, __, ctx) => [ ctx.status(200), ctx.json({ cache_key: req.params.query, diff --git a/frontend/src/scenes/insights/insightDataLogic.test.ts b/frontend/src/scenes/insights/insightDataLogic.test.ts index 1d46ee553434d..11af2a186a279 100644 --- a/frontend/src/scenes/insights/insightDataLogic.test.ts +++ b/frontend/src/scenes/insights/insightDataLogic.test.ts @@ -20,7 +20,7 @@ describe('insightDataLogic', () => { beforeEach(() => { useMocks({ get: { - '/api/projects/:team_id/insights/trend': [], + '/api/environments/:team_id/insights/trend': [], }, }) initKeaTests() diff --git a/frontend/src/scenes/insights/insightLogic.test.ts b/frontend/src/scenes/insights/insightLogic.test.ts index 3eb7fa86bec3f..cdf3b53a54784 100644 --- a/frontend/src/scenes/insights/insightLogic.test.ts +++ b/frontend/src/scenes/insights/insightLogic.test.ts @@ -112,7 +112,7 @@ describe('insightLogic', () => { useMocks({ get: { '/api/projects/:team/tags': [], - '/api/projects/:team/insights/trend/': async (req) => { + '/api/environments/:team_id/insights/trend/': async (req) => { const clientQueryId = req.url.searchParams.get('client_query_id') if (clientQueryId !== null) { seenQueryIDs.push(clientQueryId) @@ -131,18 +131,18 @@ describe('insightLogic', () => { } return [200, { result: ['result from api'] }] }, - '/api/projects/:team/insights/path/': { result: ['result from api'] }, - '/api/projects/:team/insights/path': { result: ['result from api'] }, - '/api/projects/:team/insights/funnel/': { result: ['result from api'] }, - '/api/projects/:team/insights/retention/': { result: ['result from api'] }, - '/api/projects/:team/insights/43/': partialInsight43, - '/api/projects/:team/insights/44/': { + '/api/environments/:team_id/insights/path/': { result: ['result from api'] }, + '/api/environments/:team_id/insights/path': { result: ['result from api'] }, + '/api/environments/:team_id/insights/funnel/': { result: ['result from api'] }, + '/api/environments/:team_id/insights/retention/': { result: ['result from api'] }, + '/api/environments/:team_id/insights/43/': partialInsight43, + '/api/environments/:team_id/insights/44/': { id: 44, short_id: Insight44, result: ['result 44'], filters: API_FILTERS, }, - '/api/projects/:team/insights/': (req) => { + '/api/environments/:team_id/insights/': (req) => { if (req.url.searchParams.get('saved')) { return [ 200, @@ -181,7 +181,7 @@ describe('insightLogic', () => { }, ] }, - '/api/projects/:team/dashboards/33/': { + '/api/environments/:team_id/dashboards/33/': { id: 33, filters: {}, tiles: [ @@ -198,7 +198,7 @@ describe('insightLogic', () => { }, ], }, - '/api/projects/:team/dashboards/34/': { + '/api/environments/:team_id/dashboards/34/': { id: 33, filters: {}, tiles: [ @@ -217,16 +217,16 @@ describe('insightLogic', () => { }, }, post: { - '/api/projects/:team/insights/funnel/': { result: ['result from api'] }, - '/api/projects/:team/insights/:id/viewed': [201], - '/api/projects/:team/insights/': (req) => [ + '/api/environments/:team_id/insights/funnel/': { result: ['result from api'] }, + '/api/environments/:team_id/insights/:id/viewed': [201], + '/api/environments/:team_id/insights/': (req) => [ 200, { id: 12, short_id: Insight12, ...((req.body as any) || {}) }, ], - '/api/projects/997/insights/cancel/': [201], + '/api/environments/997/insights/cancel/': [201], }, patch: { - '/api/projects/:team/insights/:id': async (req) => { + '/api/environments/:team_id/insights/:id': async (req) => { const payload = await req.json() const response = patchResponseFor( payload, @@ -737,7 +737,7 @@ describe('insightLogic', () => { const mockCreateCalls = (api.create as jest.Mock).mock.calls expect(mockCreateCalls).toEqual([ [ - `api/projects/${MOCK_TEAM_ID}/insights`, + `api/environments/${MOCK_TEAM_ID}/insights`, expect.objectContaining({ derived_name: 'DataTableNode query', query: { diff --git a/frontend/src/scenes/insights/insightSceneLogic.test.ts b/frontend/src/scenes/insights/insightSceneLogic.test.ts index 24f6db1e8bb64..d6a1b4bdcf2ca 100644 --- a/frontend/src/scenes/insights/insightSceneLogic.test.ts +++ b/frontend/src/scenes/insights/insightSceneLogic.test.ts @@ -19,11 +19,11 @@ describe('insightSceneLogic', () => { beforeEach(async () => { useMocks({ get: { - '/api/projects/:team/insights/trend/': { result: ['result from api'] }, + '/api/environments/:team_id/insights/trend/': { result: ['result from api'] }, }, post: { - '/api/projects/:team/insights/funnel/': { result: ['result from api'] }, - '/api/projects/:team/insights/': (req) => [ + '/api/environments/:team_id/insights/funnel/': { result: ['result from api'] }, + '/api/environments/:team_id/insights/': (req) => [ 200, { id: 12, short_id: Insight12, ...((req.body as any) || {}) }, ], diff --git a/frontend/src/scenes/insights/insightUsageLogic.ts b/frontend/src/scenes/insights/insightUsageLogic.ts index 1c1ae8140df08..2b2a2517c0969 100644 --- a/frontend/src/scenes/insights/insightUsageLogic.ts +++ b/frontend/src/scenes/insights/insightUsageLogic.ts @@ -3,7 +3,7 @@ import { subscriptions } from 'kea-subscriptions' import api from 'lib/api' import { objectsEqual } from 'lib/utils' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { teamLogic } from 'scenes/teamLogic' +import { projectLogic } from 'scenes/projectLogic' import { dataNodeLogic, DataNodeLogicProps } from '~/queries/nodes/DataNode/dataNodeLogic' import { insightVizDataNodeKey } from '~/queries/nodes/InsightViz/InsightViz' @@ -23,6 +23,8 @@ export const insightUsageLogic = kea([ path((key) => ['scenes', 'insights', 'insightUsageLogic', key]), connect((props: InsightLogicProps) => ({ values: [ + projectLogic, + ['currentProjectId'], insightLogic(props), ['insight'], dataNodeLogic({ key: insightVizDataNodeKey(props) } as DataNodeLogicProps), @@ -58,7 +60,7 @@ export const insightUsageLogic = kea([ // Report the insight being viewed to our '/viewed' endpoint. Used for "recently viewed insights". if (values.insight.id) { - void api.create(`api/projects/${teamLogic.values.currentTeamId}/insights/${values.insight.id}/viewed`) + void api.create(`api/environments/${values.currentProjectId}/insights/${values.insight.id}/viewed`) } // Debounce to avoid noisy events from the query changing multiple times. diff --git a/frontend/src/scenes/insights/insightVizDataLogic.test.ts b/frontend/src/scenes/insights/insightVizDataLogic.test.ts index c3bc313e815a6..266fe56061263 100644 --- a/frontend/src/scenes/insights/insightVizDataLogic.test.ts +++ b/frontend/src/scenes/insights/insightVizDataLogic.test.ts @@ -22,8 +22,8 @@ describe('insightVizDataLogic', () => { beforeEach(() => { useMocks({ get: { - '/api/projects/:team_id/insights/trend': [], - '/api/projects/:team_id/insights/': { results: [{}] }, + '/api/environments/:team_id/insights/trend': [], + '/api/environments/:team_id/insights/': { results: [{}] }, }, }) initKeaTests() diff --git a/frontend/src/scenes/insights/utils.tsx b/frontend/src/scenes/insights/utils.tsx index e9dc44767d875..96d3129e47fa6 100644 --- a/frontend/src/scenes/insights/utils.tsx +++ b/frontend/src/scenes/insights/utils.tsx @@ -137,7 +137,7 @@ export async function getInsightId(shortId: InsightShortId): Promise = { decorators: [ mswDecorator({ post: { - 'api/projects/:team_id/insights/funnel/correlation/': funnelCorrelation, + 'api/environments/:team_id/insights/funnel/correlation/': funnelCorrelation, }, }), ], diff --git a/frontend/src/scenes/insights/views/Funnels/FunnelPropertyCorrelationTable.stories.tsx b/frontend/src/scenes/insights/views/Funnels/FunnelPropertyCorrelationTable.stories.tsx index 5f7275b4b8818..4bff8e9a36356 100644 --- a/frontend/src/scenes/insights/views/Funnels/FunnelPropertyCorrelationTable.stories.tsx +++ b/frontend/src/scenes/insights/views/Funnels/FunnelPropertyCorrelationTable.stories.tsx @@ -21,7 +21,7 @@ const meta: Meta = { decorators: [ mswDecorator({ post: { - 'api/projects/:team_id/insights/funnel/correlation/': funnelCorrelation, + 'api/environments/:team_id/insights/funnel/correlation/': funnelCorrelation, }, }), taxonomicFilterMocksDecorator, diff --git a/frontend/src/scenes/max/Max.stories.tsx b/frontend/src/scenes/max/Max.stories.tsx index 1e9761f352370..65106d4ae4420 100644 --- a/frontend/src/scenes/max/Max.stories.tsx +++ b/frontend/src/scenes/max/Max.stories.tsx @@ -13,7 +13,7 @@ const meta: Meta = { decorators: [ mswDecorator({ post: { - '/api/projects/:team_id/query/chat/': chatResponse, + '/api/environments/:team_id/query/chat/': chatResponse, }, }), ], @@ -86,7 +86,7 @@ export const Thread: StoryFn = () => { export const EmptyThreadLoading: StoryFn = () => { useStorybookMocks({ post: { - '/api/projects/:team_id/query/chat/': (_req, _res, ctx) => [ctx.delay('infinite')], + '/api/environments/:team_id/query/chat/': (_req, _res, ctx) => [ctx.delay('infinite')], }, }) diff --git a/frontend/src/scenes/notebooks/Notebook/Notebook.stories.tsx b/frontend/src/scenes/notebooks/Notebook/Notebook.stories.tsx index e67b51d7ad639..9b760fe72eb86 100644 --- a/frontend/src/scenes/notebooks/Notebook/Notebook.stories.tsx +++ b/frontend/src/scenes/notebooks/Notebook/Notebook.stories.tsx @@ -206,7 +206,7 @@ const meta: Meta = { decorators: [ mswDecorator({ post: { - 'api/projects/:team_id/query': { + 'api/environments/:team_id/query': { clickhouse: "SELECT nullIf(nullIf(events.`$session_id`, ''), 'null') AS session_id, any(events.properties) AS properties FROM events WHERE and(equals(events.team_id, 1), in(events.event, [%(hogql_val_0)s, %(hogql_val_1)s]), ifNull(in(session_id, [%(hogql_val_2)s]), 0), ifNull(greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_3)s), %(hogql_val_4)s), 0), ifNull(lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_5)s), %(hogql_val_6)s), 0)) GROUP BY session_id LIMIT 100 SETTINGS readonly=2, max_execution_time=60, allow_experimental_object_type=True", columns: ['session_id', 'properties'], @@ -275,7 +275,7 @@ const meta: Meta = { ], }, 'api/projects/:team_id/notebooks/12345': notebook12345Json, - 'api/projects/:team_id/session_recordings': { + 'api/environments/:team_id/session_recordings': { results: [ { id: '018a8a51-a39d-7b18-897f-94054eec5f61', diff --git a/frontend/src/scenes/persons/personsLogic.test.ts b/frontend/src/scenes/persons/personsLogic.test.ts index 6e8127842f135..602ff71a2992b 100644 --- a/frontend/src/scenes/persons/personsLogic.test.ts +++ b/frontend/src/scenes/persons/personsLogic.test.ts @@ -15,7 +15,7 @@ describe('personsLogic', () => { beforeEach(() => { useMocks({ get: { - '/api/projects/:team_id/persons/': (req) => { + '/api/environments/:team_id/persons/': (req) => { if (['+', 'abc', 'xyz'].includes(req.url.searchParams.get('distinct_id') ?? '')) { return [200, { results: ['person from api'] }] } @@ -103,7 +103,10 @@ describe('personsLogic', () => { await expectLogic(logic, () => { logic.actions.loadPerson('+') // has encoded from + in the action to %2B in the API call - expect(api.get).toHaveBeenCalledWith(`api/projects/${MOCK_TEAM_ID}/persons?distinct_id=%2B`, undefined) + expect(api.get).toHaveBeenCalledWith( + `api/environments/${MOCK_TEAM_ID}/persons?distinct_id=%2B`, + undefined + ) }) .toDispatchActions(['loadPerson', 'loadPersonSuccess']) .toMatchValues({ diff --git a/frontend/src/scenes/pipeline/Pipeline.stories.tsx b/frontend/src/scenes/pipeline/Pipeline.stories.tsx index ce45d28639c7c..ac01c5b62cd53 100644 --- a/frontend/src/scenes/pipeline/Pipeline.stories.tsx +++ b/frontend/src/scenes/pipeline/Pipeline.stories.tsx @@ -55,8 +55,8 @@ export default { '/api/organizations/:organization_id/plugins/repository': [], '/api/organizations/:organization_id/plugins/unused': [], '/api/organizations/:organization_id/plugins/:id': pluginRetrieveMock, - '/api/projects/:team_id/plugin_configs/': pluginConfigs, - '/api/projects/:team_id/plugin_configs/:id': pluginConfigRetrieveMock, + '/api/environments/:team_id/plugin_configs/': pluginConfigs, + '/api/environments/:team_id/plugin_configs/:id': pluginConfigRetrieveMock, // TODO: Differentiate between transformation and destination mocks for nicer mocks '/api/organizations/:organization_id/pipeline_transformations/': plugins, '/api/projects/:team_id/pipeline_transformation_configs/': pluginConfigs, @@ -278,7 +278,7 @@ export function PipelineNodeMetricsErrorModal(): JSX.Element { export function PipelineNodeLogs(): JSX.Element { useStorybookMocks({ get: { - '/api/projects/:team_id/plugin_configs/:plugin_config_id/logs': require('./__mocks__/pluginLogs.json'), + '/api/environments/:team_id/plugin_configs/:plugin_config_id/logs': require('./__mocks__/pluginLogs.json'), }, }) useEffect(() => { @@ -290,7 +290,7 @@ export function PipelineNodeLogs(): JSX.Element { export function PipelineNodeLogsBatchExport(): JSX.Element { useStorybookMocks({ get: { - '/api/projects/:team_id/batch_exports/:export_id/logs': require('./__mocks__/batchExportLogs.json'), + '/api/environments/:team_id/batch_exports/:export_id/logs': require('./__mocks__/batchExportLogs.json'), }, }) useEffect(() => { diff --git a/frontend/src/scenes/pipeline/destinations/destinationsLogic.tsx b/frontend/src/scenes/pipeline/destinations/destinationsLogic.tsx index 38fa9a80270a9..3d13151059182 100644 --- a/frontend/src/scenes/pipeline/destinations/destinationsLogic.tsx +++ b/frontend/src/scenes/pipeline/destinations/destinationsLogic.tsx @@ -109,7 +109,7 @@ export const pipelineDestinationsLogic = kea([ deleteNodeWebhook: async ({ destination }) => { await deleteWithUndo({ - endpoint: `projects/${teamLogic.values.currentTeamId}/plugin_configs`, + endpoint: `environments/${teamLogic.values.currentTeamId}/plugin_configs`, object: { id: destination.id, name: destination.name, diff --git a/frontend/src/scenes/project-homepage/ProjectHomepage.stories.tsx b/frontend/src/scenes/project-homepage/ProjectHomepage.stories.tsx index a3857dc2c8c2b..922c3de9476be 100644 --- a/frontend/src/scenes/project-homepage/ProjectHomepage.stories.tsx +++ b/frontend/src/scenes/project-homepage/ProjectHomepage.stories.tsx @@ -12,11 +12,11 @@ const meta: Meta = { decorators: [ mswDecorator({ get: { - '/api/projects/:team_id/dashboards/': require('../dashboard/__mocks__/dashboards.json'), - '/api/projects/:team_id/dashboards/1/': require('../dashboard/__mocks__/dashboard1.json'), - '/api/projects/:team_id/dashboards/1/collaborators/': [], - '/api/projects/:team_id/session_recordings/': EMPTY_PAGINATED_RESPONSE, - '/api/projects/:team_id/insights/my_last_viewed/': [], + '/api/environments/:team_id/dashboards/': require('../dashboard/__mocks__/dashboards.json'), + '/api/environments/:team_id/dashboards/1/': require('../dashboard/__mocks__/dashboard1.json'), + '/api/environments/:team_id/dashboards/1/collaborators/': [], + '/api/environments/:team_id/session_recordings/': EMPTY_PAGINATED_RESPONSE, + '/api/environments/:team_id/insights/my_last_viewed/': [], }, }), ], diff --git a/frontend/src/scenes/project-homepage/projectHomepageLogic.test.ts b/frontend/src/scenes/project-homepage/projectHomepageLogic.test.ts index 0818d735c1536..d778680ced854 100644 --- a/frontend/src/scenes/project-homepage/projectHomepageLogic.test.ts +++ b/frontend/src/scenes/project-homepage/projectHomepageLogic.test.ts @@ -15,9 +15,9 @@ describe('projectHomepageLogic', () => { beforeEach(() => { useMocks({ get: { - '/api/projects/:team/dashboards/1/': dashboardJson, - '/api/projects/:team/insights/': { results: ['result from api'] }, - '/api/projects/:team/persons/': { results: ['result from api'] }, + '/api/environments/:team_id/dashboards/1/': dashboardJson, + '/api/environments/:team_id/insights/': { results: ['result from api'] }, + '/api/environments/:team_id/persons/': { results: ['result from api'] }, }, }) initKeaTests() diff --git a/frontend/src/scenes/project-homepage/projectHomepageLogic.tsx b/frontend/src/scenes/project-homepage/projectHomepageLogic.tsx index af349e3aa86bc..619e6765fcc14 100644 --- a/frontend/src/scenes/project-homepage/projectHomepageLogic.tsx +++ b/frontend/src/scenes/project-homepage/projectHomepageLogic.tsx @@ -2,6 +2,7 @@ import { afterMount, connect, kea, path, selectors } from 'kea' import { loaders } from 'kea-loaders' import api from 'lib/api' import { DashboardLogicProps } from 'scenes/dashboard/dashboardLogic' +import { projectLogic } from 'scenes/projectLogic' import { teamLogic } from 'scenes/teamLogic' import { getQueryBasedInsightModel } from '~/queries/nodes/InsightViz/utils' @@ -12,7 +13,7 @@ import type { projectHomepageLogicType } from './projectHomepageLogicType' export const projectHomepageLogic = kea([ path(['scenes', 'project-homepage', 'projectHomepageLogic']), connect({ - values: [teamLogic, ['currentTeamId', 'currentTeam']], + values: [teamLogic, ['currentTeam'], projectLogic, ['currentProjectId']], }), selectors({ @@ -35,7 +36,7 @@ export const projectHomepageLogic = kea([ { loadRecentInsights: async () => { const insights = await api.get( - `api/projects/${values.currentTeamId}/insights/my_last_viewed` + `api/environments/${values.currentProjectId}/insights/my_last_viewed` ) return insights.map((legacyInsight) => getQueryBasedInsightModel(legacyInsight)) }, diff --git a/frontend/src/scenes/projectLogic.ts b/frontend/src/scenes/projectLogic.ts index fca5367075fb6..8712f9d4d8279 100644 --- a/frontend/src/scenes/projectLogic.ts +++ b/frontend/src/scenes/projectLogic.ts @@ -1,6 +1,6 @@ -import { actions, afterMount, connect, kea, listeners, path, reducers } from 'kea' +import { actions, afterMount, connect, kea, listeners, path, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' -import api from 'lib/api' +import api, { ApiConfig } from 'lib/api' import { lemonToast } from 'lib/lemon-ui/LemonToast' import { identifierToHuman, isUserLoggedIn } from 'lib/utils' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' @@ -80,7 +80,15 @@ export const projectLogic = kea([ }, ], })), + selectors({ + currentProjectId: [(s) => [s.currentProject], (currentProject) => currentProject?.id || null], + }), listeners(({ actions }) => ({ + loadCurrentProjectSuccess: ({ currentProject }) => { + if (currentProject) { + ApiConfig.setCurrentProjectId(currentProject.id) + } + }, deleteProject: async ({ project }) => { try { await api.delete(`api/projects/${project.id}`) diff --git a/frontend/src/scenes/saved-insights/SavedInsights.stories.tsx b/frontend/src/scenes/saved-insights/SavedInsights.stories.tsx index 8f9ff797ee74b..673a3076fb654 100644 --- a/frontend/src/scenes/saved-insights/SavedInsights.stories.tsx +++ b/frontend/src/scenes/saved-insights/SavedInsights.stories.tsx @@ -23,7 +23,7 @@ const meta: Meta = { decorators: [ mswDecorator({ get: { - '/api/projects/:team_id/insights': toPaginatedResponse( + '/api/environments/:team_id/insights': toPaginatedResponse( insightsJson.results.slice(0, 6).map((result, i) => ({ // Keep size of response in check ...result, @@ -56,7 +56,7 @@ CardView.parameters = { export const EmptyState: Story = () => { useStorybookMocks({ get: { - '/api/projects/:team_id/insights': EMPTY_PAGINATED_RESPONSE, + '/api/environments/:team_id/insights': EMPTY_PAGINATED_RESPONSE, }, }) useEffect(() => { diff --git a/frontend/src/scenes/saved-insights/savedInsightsLogic.test.ts b/frontend/src/scenes/saved-insights/savedInsightsLogic.test.ts index 308be468581cf..47729fab591fa 100644 --- a/frontend/src/scenes/saved-insights/savedInsightsLogic.test.ts +++ b/frontend/src/scenes/saved-insights/savedInsightsLogic.test.ts @@ -52,18 +52,18 @@ describe('savedInsightsLogic', () => { beforeEach(() => { useMocks({ get: { - '/api/projects/:team/insights/': (req) => [ + '/api/environments/:team_id/insights/': (req) => [ 200, createSavedInsights( req.url.searchParams.get('search') ?? '', parseInt(req.url.searchParams.get('offset') ?? '0') ), ], - '/api/projects/:team/insights/42': createInsight(42), - '/api/projects/:team/insights/123': createInsight(123), + '/api/environments/:team_id/insights/42': createInsight(42), + '/api/environments/:team_id/insights/123': createInsight(123), }, post: { - '/api/projects/:team/insights/': () => [200, createInsight(42)], + '/api/environments/:team_id/insights/': () => [200, createInsight(42)], }, }) initKeaTests() @@ -192,7 +192,7 @@ describe('savedInsightsLogic', () => { sourceInsight.derived_name = 'should be copied' await logic.asyncActions.duplicateInsight(sourceInsight) expect(api.create).toHaveBeenCalledWith( - `api/projects/${MOCK_TEAM_ID}/insights`, + `api/environments/${MOCK_TEAM_ID}/insights`, expect.objectContaining({ name: '' }), expect.objectContaining({}) ) @@ -204,7 +204,7 @@ describe('savedInsightsLogic', () => { sourceInsight.derived_name = '' await logic.asyncActions.duplicateInsight(sourceInsight) expect(api.create).toHaveBeenCalledWith( - `api/projects/${MOCK_TEAM_ID}/insights`, + `api/environments/${MOCK_TEAM_ID}/insights`, expect.objectContaining({ name: 'should be copied (copy)' }), expect.objectContaining({}) ) diff --git a/frontend/src/scenes/saved-insights/savedInsightsLogic.ts b/frontend/src/scenes/saved-insights/savedInsightsLogic.ts index 923b13bc2999f..1b958165f4f3b 100644 --- a/frontend/src/scenes/saved-insights/savedInsightsLogic.ts +++ b/frontend/src/scenes/saved-insights/savedInsightsLogic.ts @@ -104,7 +104,7 @@ export const savedInsightsLogic = kea([ } const legacyResponse: CountedPaginatedResponse = await api.get( - `api/projects/${teamLogic.values.currentTeamId}/insights/?${toParams(params)}` + `api/environments/${teamLogic.values.currentTeamId}/insights/?${toParams(params)}` ) const response = { ...legacyResponse, diff --git a/frontend/src/scenes/session-recordings/SessionsRecordings-player-failure.stories.tsx b/frontend/src/scenes/session-recordings/SessionsRecordings-player-failure.stories.tsx index 6b8f14a78cc20..bb9063ed6c933 100644 --- a/frontend/src/scenes/session-recordings/SessionsRecordings-player-failure.stories.tsx +++ b/frontend/src/scenes/session-recordings/SessionsRecordings-player-failure.stories.tsx @@ -23,7 +23,7 @@ const meta: Meta = { // API is set up so that everything except the call to load session recording metadata succeeds mswDecorator({ get: { - '/api/projects/:team_id/session_recordings': (req) => { + '/api/environments/:team_id/session_recordings': (req) => { const version = req.url.searchParams.get('version') return [ 200, @@ -88,7 +88,7 @@ const meta: Meta = { const response = playlistId === '1234567' ? recordings : [] return [200, { has_next: false, results: response, version: 1 }] }, - '/api/projects/:team/session_recordings/:id/snapshots': (req, res, ctx) => { + '/api/environments/:team_id/session_recordings/:id/snapshots': (req, res, ctx) => { // with no sources, returns sources... if (req.url.searchParams.get('source') === 'blob') { return res(ctx.text(snapshotsAsJSONLines())) @@ -108,7 +108,7 @@ const meta: Meta = { }, ] }, - '/api/projects/:team/session_recordings/:id': () => { + '/api/environments/:team_id/session_recordings/:id': () => { return [404, {}] }, 'api/projects/:team/notebooks': { @@ -119,7 +119,7 @@ const meta: Meta = { }, }, post: { - '/api/projects/:team/query': recordingEventsJson, + '/api/environments/:team_id/query': recordingEventsJson, }, }), ], diff --git a/frontend/src/scenes/session-recordings/SessionsRecordings-player-success.stories.tsx b/frontend/src/scenes/session-recordings/SessionsRecordings-player-success.stories.tsx index 39b6bbd3d9980..c4f13b003c2c7 100644 --- a/frontend/src/scenes/session-recordings/SessionsRecordings-player-success.stories.tsx +++ b/frontend/src/scenes/session-recordings/SessionsRecordings-player-success.stories.tsx @@ -23,7 +23,7 @@ const meta: Meta = { decorators: [ mswDecorator({ get: { - '/api/projects/:team_id/session_recordings': (req) => { + '/api/environments/:team_id/session_recordings': (req) => { const version = req.url.searchParams.get('version') return [ 200, @@ -88,7 +88,7 @@ const meta: Meta = { const response = playlistId === '1234567' ? recordings : [] return [200, { has_next: false, results: response, version: 1 }] }, - '/api/projects/:team/session_recordings/:id/snapshots': (req, res, ctx) => { + '/api/environments/:team_id/session_recordings/:id/snapshots': (req, res, ctx) => { // with no sources, returns sources... if (req.url.searchParams.get('source') === 'blob') { return res(ctx.text(snapshotsAsJSONLines())) @@ -108,7 +108,7 @@ const meta: Meta = { }, ] }, - '/api/projects/:team/session_recordings/:id': recordingMetaJson, + '/api/environments/:team_id/session_recordings/:id': recordingMetaJson, 'api/projects/:team/notebooks': { count: 0, next: null, @@ -117,7 +117,7 @@ const meta: Meta = { }, }, post: { - '/api/projects/:team/query': (req, res, ctx) => { + '/api/environments/:team_id/query': (req, res, ctx) => { const body = req.body as Record if ( diff --git a/frontend/src/scenes/session-recordings/SessionsRecordings-playlist-listing.stories.tsx b/frontend/src/scenes/session-recordings/SessionsRecordings-playlist-listing.stories.tsx index f90ee0ed1285a..86ef8078ac397 100644 --- a/frontend/src/scenes/session-recordings/SessionsRecordings-playlist-listing.stories.tsx +++ b/frontend/src/scenes/session-recordings/SessionsRecordings-playlist-listing.stories.tsx @@ -22,7 +22,7 @@ const meta: Meta = { mswDecorator({ get: { '/api/projects/:team_id/session_recording_playlists': recording_playlists, - '/api/projects/:team_id/session_recordings': (req) => { + '/api/environments/:team_id/session_recordings': (req) => { const version = req.url.searchParams.get('version') return [ 200, @@ -35,7 +35,7 @@ const meta: Meta = { }, }, post: { - '/api/projects/:team/query': recordingEventsJson, + '/api/environments/:team_id/query': recordingEventsJson, }, }), ], diff --git a/frontend/src/scenes/session-recordings/player/inspector/playerInspectorLogic.test.ts b/frontend/src/scenes/session-recordings/player/inspector/playerInspectorLogic.test.ts index 5f9c7bb8ffa77..dafd05c304ff3 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/playerInspectorLogic.test.ts +++ b/frontend/src/scenes/session-recordings/player/inspector/playerInspectorLogic.test.ts @@ -15,7 +15,7 @@ describe('playerInspectorLogic', () => { beforeEach(() => { useMocks({ get: { - 'api/projects/:team_id/session_recordings/1/': {}, + 'api/environments/:team_id/session_recordings/1/': {}, 'api/projects/:team/notebooks/recording_comments': { results: [ { diff --git a/frontend/src/scenes/session-recordings/player/modal/sessionPlayerModalLogic.test.ts b/frontend/src/scenes/session-recordings/player/modal/sessionPlayerModalLogic.test.ts index 838f51c78e6f5..77fc0fde434e0 100644 --- a/frontend/src/scenes/session-recordings/player/modal/sessionPlayerModalLogic.test.ts +++ b/frontend/src/scenes/session-recordings/player/modal/sessionPlayerModalLogic.test.ts @@ -13,7 +13,7 @@ describe('sessionPlayerModalLogic', () => { beforeEach(() => { useMocks({ get: { - '/api/projects/:team/session_recordings': [ + '/api/environments/:team_id/session_recordings': [ 200, { results: listOfSessionRecordings, diff --git a/frontend/src/scenes/session-recordings/player/playerMetaLogic.test.ts b/frontend/src/scenes/session-recordings/player/playerMetaLogic.test.ts index f21c3f7189f6e..4fccafcfb856a 100644 --- a/frontend/src/scenes/session-recordings/player/playerMetaLogic.test.ts +++ b/frontend/src/scenes/session-recordings/player/playerMetaLogic.test.ts @@ -19,12 +19,12 @@ describe('playerMetaLogic', () => { beforeEach(() => { useMocks({ get: { - '/api/projects/:team/session_recordings/:id': recordingMetaJson, - '/api/projects/:team/session_recordings/:id/snapshots/': (_, res, ctx) => + '/api/environments/:team_id/session_recordings/:id': recordingMetaJson, + '/api/environments/:team_id/session_recordings/:id/snapshots/': (_, res, ctx) => res(ctx.text(snapshotsAsJSONLines())), }, post: { - '/api/projects/:team/query': recordingEventsJson, + '/api/environments/:team_id/query': recordingEventsJson, }, }) initKeaTests() diff --git a/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.test.ts b/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.test.ts index d42f36b416a99..353a2aa04236c 100644 --- a/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.test.ts +++ b/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.test.ts @@ -43,7 +43,7 @@ describe('sessionRecordingDataLogic', () => { useAvailableFeatures([AvailableFeature.RECORDINGS_PERFORMANCE]) useMocks({ get: { - '/api/projects/:team/session_recordings/:id/snapshots': async (req, res, ctx) => { + '/api/environments/:team_id/session_recordings/:id/snapshots': async (req, res, ctx) => { // with no sources, returns sources... if (req.url.searchParams.get('source') === 'blob') { return res(ctx.text(snapshotsAsJSONLines())) @@ -69,13 +69,13 @@ describe('sessionRecordingDataLogic', () => { }, ] }, - '/api/projects/:team/session_recordings/:id': recordingMetaJson, + '/api/environments/:team_id/session_recordings/:id': recordingMetaJson, }, post: { - '/api/projects/:team/query': recordingEventsJson, + '/api/environments/:team_id/query': recordingEventsJson, }, patch: { - '/api/projects/:team/session_recordings/:id': { success: true }, + '/api/environments/:team_id/session_recordings/:id': { success: true }, }, }) initKeaTests() @@ -139,7 +139,7 @@ describe('sessionRecordingDataLogic', () => { logic.unmount() useMocks({ get: { - '/api/projects/:team/session_recordings/:id': () => [500, { status: 0 }], + '/api/environments/:team_id/session_recordings/:id': () => [500, { status: 0 }], }, }) logic.mount() @@ -170,7 +170,7 @@ describe('sessionRecordingDataLogic', () => { logic.unmount() useMocks({ get: { - '/api/projects/:team/session_recordings/:id/snapshots': () => [500, { status: 0 }], + '/api/environments/:team_id/session_recordings/:id/snapshots': () => [500, { status: 0 }], }, }) logic.mount() @@ -224,7 +224,7 @@ describe('sessionRecordingDataLogic', () => { }).toDispatchActions(['loadEvents', 'loadEventsSuccess']) expect(api.create).toHaveBeenCalledWith( - `api/projects/${MOCK_TEAM_ID}/query`, + `api/environments/${MOCK_TEAM_ID}/query`, { client_query_id: undefined, query: { diff --git a/frontend/src/scenes/session-recordings/player/sessionRecordingPlayerLogic.test.ts b/frontend/src/scenes/session-recordings/player/sessionRecordingPlayerLogic.test.ts index 4724e4369cdf2..3dde171f5c309 100644 --- a/frontend/src/scenes/session-recordings/player/sessionRecordingPlayerLogic.test.ts +++ b/frontend/src/scenes/session-recordings/player/sessionRecordingPlayerLogic.test.ts @@ -27,7 +27,7 @@ describe('sessionRecordingPlayerLogic', () => { useMocks({ get: { '/api/projects/:team_id/session_recordings/:id/comments/': { results: [] }, - '/api/projects/:team/session_recordings/:id/snapshots/': (req, res, ctx) => { + '/api/environments/:team_id/session_recordings/:id/snapshots/': (req, res, ctx) => { // with no sources, returns sources... if (req.url.searchParams.get('source') === 'blob') { return res(ctx.text(snapshotsAsJSONLines())) @@ -47,13 +47,13 @@ describe('sessionRecordingPlayerLogic', () => { }, ] }, - '/api/projects/:team/session_recordings/:id': recordingMetaJson, + '/api/environments/:team_id/session_recordings/:id': recordingMetaJson, }, delete: { - '/api/projects/:team/session_recordings/:id': { success: true }, + '/api/environments/:team_id/session_recordings/:id': { success: true }, }, post: { - '/api/projects/:team/query': recordingEventsJson, + '/api/environments/:team_id/query': recordingEventsJson, }, }) initKeaTests() @@ -128,7 +128,7 @@ describe('sessionRecordingPlayerLogic', () => { useMocks({ get: { - '/api/projects/:team/session_recordings/:id/snapshots': () => [500, { status: 0 }], + '/api/environments/:team_id/session_recordings/:id/snapshots': () => [500, { status: 0 }], }, }) logic.mount() @@ -194,7 +194,7 @@ describe('sessionRecordingPlayerLogic', () => { sessionRecordingsPlaylistLogic({ updateSearchParams: true }).actionTypes.loadAllRecordings, ]) - expect(api.delete).toHaveBeenCalledWith(`api/projects/${MOCK_TEAM_ID}/session_recordings/3`) + expect(api.delete).toHaveBeenCalledWith(`api/environments/${MOCK_TEAM_ID}/session_recordings/3`) resumeKeaLoadersErrors() }) @@ -218,7 +218,7 @@ describe('sessionRecordingPlayerLogic', () => { listLogic.actionCreators.setSelectedRecordingId(null), ]) - expect(api.delete).toHaveBeenCalledWith(`api/projects/${MOCK_TEAM_ID}/session_recordings/3`) + expect(api.delete).toHaveBeenCalledWith(`api/environments/${MOCK_TEAM_ID}/session_recordings/3`) resumeKeaLoadersErrors() }) @@ -240,7 +240,7 @@ describe('sessionRecordingPlayerLogic', () => { expect(router.values.location.pathname).toEqual(urls.replay()) - expect(api.delete).toHaveBeenCalledWith(`api/projects/${MOCK_TEAM_ID}/session_recordings/3`) + expect(api.delete).toHaveBeenCalledWith(`api/environments/${MOCK_TEAM_ID}/session_recordings/3`) resumeKeaLoadersErrors() }) @@ -261,7 +261,7 @@ describe('sessionRecordingPlayerLogic', () => { expect(router.values.location.pathname).toEqual('/') - expect(api.delete).toHaveBeenCalledWith(`api/projects/${MOCK_TEAM_ID}/session_recordings/3`) + expect(api.delete).toHaveBeenCalledWith(`api/environments/${MOCK_TEAM_ID}/session_recordings/3`) resumeKeaLoadersErrors() }) }) diff --git a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsListPropertiesLogic.test.ts b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsListPropertiesLogic.test.ts index 0b032a2f5f1e0..881f1202d120c 100644 --- a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsListPropertiesLogic.test.ts +++ b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsListPropertiesLogic.test.ts @@ -39,7 +39,7 @@ describe('sessionRecordingsListPropertiesLogic', () => { beforeEach(() => { useMocks({ post: { - '/api/projects/:team/query': { + '/api/environments/:team_id/query': { results: [ ['s1', JSON.stringify({ blah: 'blah1' })], ['s2', JSON.stringify({ blah: 'blah2' })], diff --git a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.test.ts b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.test.ts index 309362176d427..876ef29c38af9 100644 --- a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.test.ts +++ b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.test.ts @@ -43,7 +43,7 @@ describe('sessionRecordingsPlaylistLogic', () => { beforeEach(() => { useMocks({ get: { - '/api/projects/:team/session_recordings/properties': { + '/api/environments/:team_id/session_recordings/properties': { results: [ { id: 's1', properties: { blah: 'blah1' } }, { id: 's2', properties: { blah: 'blah2' } }, @@ -52,7 +52,7 @@ describe('sessionRecordingsPlaylistLogic', () => { 'api/projects/:team/property_definitions/seen_together': { $pageview: true }, - '/api/projects/:team/session_recordings': (req) => { + '/api/environments/:team_id/session_recordings': (req) => { const { searchParams } = req.url if ( (searchParams.get('events')?.length || 0) > 0 && diff --git a/frontend/src/scenes/settings/environment/teamMembersLogic.tsx b/frontend/src/scenes/settings/environment/teamMembersLogic.tsx index 650ea406daa05..23274ece6ae8d 100644 --- a/frontend/src/scenes/settings/environment/teamMembersLogic.tsx +++ b/frontend/src/scenes/settings/environment/teamMembersLogic.tsx @@ -32,12 +32,12 @@ export const teamMembersLogic = kea([ explicitMembers: { __default: [] as ExplicitTeamMemberType[], loadMembers: async () => { - return await api.get(`api/projects/${teamLogic.values.currentTeamId}/explicit_members/`) + return await api.get(`api/environments/${teamLogic.values.currentTeamId}/explicit_members/`) }, addMembers: async ({ userUuids, level }: AddMembersFields) => { const newMembers: ExplicitTeamMemberType[] = await Promise.all( userUuids.map((userUuid) => - api.create(`api/projects/${teamLogic.values.currentTeamId}/explicit_members/`, { + api.create(`api/environments/${teamLogic.values.currentTeamId}/explicit_members/`, { user_uuid: userUuid, level, }) @@ -49,7 +49,9 @@ export const teamMembersLogic = kea([ return [...values.explicitMembers, ...newMembers] }, removeMember: async ({ member }: { member: BaseMemberType }) => { - await api.delete(`api/projects/${teamLogic.values.currentTeamId}/explicit_members/${member.user.uuid}/`) + await api.delete( + `api/environments/${teamLogic.values.currentTeamId}/explicit_members/${member.user.uuid}/` + ) lemonToast.success( <> {member.user.uuid === userLogic.values.user?.uuid @@ -164,7 +166,7 @@ export const teamMembersLogic = kea([ })), listeners(({ actions }) => ({ changeUserAccessLevel: async ({ user, newLevel }) => { - await api.update(`api/projects/${teamLogic.values.currentTeamId}/explicit_members/${user.uuid}/`, { + await api.update(`api/environments/${teamLogic.values.currentTeamId}/explicit_members/${user.uuid}/`, { level: newLevel, }) lemonToast.success( diff --git a/frontend/src/scenes/surveys/Surveys.stories.tsx b/frontend/src/scenes/surveys/Surveys.stories.tsx index 8c88e69dcd3fa..624c993b3e559 100644 --- a/frontend/src/scenes/surveys/Surveys.stories.tsx +++ b/frontend/src/scenes/surveys/Surveys.stories.tsx @@ -221,7 +221,7 @@ const meta: Meta = { }`]: toPaginatedResponse([MOCK_SURVEY_WITH_RELEASE_CONS.targeting_flag]), }, post: { - '/api/projects/:team_id/query/': async (req, res, ctx) => { + '/api/environments/:team_id/query/': async (req, res, ctx) => { const body = await req.json() if (body.kind == 'EventsQuery') { return res(ctx.json(MOCK_SURVEY_RESULTS)) diff --git a/frontend/src/scenes/surveys/surveyLogic.test.ts b/frontend/src/scenes/surveys/surveyLogic.test.ts index 38223aa0c0fdc..9827f071ed65b 100644 --- a/frontend/src/scenes/surveys/surveyLogic.test.ts +++ b/frontend/src/scenes/surveys/surveyLogic.test.ts @@ -213,7 +213,7 @@ describe('multiple choice survey logic', () => { '/api/projects/:team/surveys/responses_count': () => [200, {}], }, post: { - '/api/projects/:team/query/': () => [ + '/api/environments/:team_id/query/': () => [ 200, { results: [ @@ -263,7 +263,7 @@ describe('single choice survey logic', () => { '/api/projects/:team/surveys/responses_count': () => [200, {}], }, post: { - '/api/projects/:team/query/': () => [ + '/api/environments/:team_id/query/': () => [ 200, { results: [ @@ -313,7 +313,7 @@ describe('multiple choice survey with open choice logic', () => { '/api/projects/:team/surveys/responses_count': () => [200, {}], }, post: { - '/api/projects/:team/query/': () => [ + '/api/environments/:team_id/query/': () => [ 200, { results: [ @@ -363,7 +363,7 @@ describe('single choice survey with open choice logic', () => { '/api/projects/:team/surveys/responses_count': () => [200, {}], }, post: { - '/api/projects/:team/query/': () => [ + '/api/environments/:team_id/query/': () => [ 200, { results: [ diff --git a/frontend/src/scenes/trends/persons-modal/PersonsModal.stories.tsx b/frontend/src/scenes/trends/persons-modal/PersonsModal.stories.tsx index f56637999e527..5d981f6004fec 100644 --- a/frontend/src/scenes/trends/persons-modal/PersonsModal.stories.tsx +++ b/frontend/src/scenes/trends/persons-modal/PersonsModal.stories.tsx @@ -86,7 +86,7 @@ export const Empty: StoryFn = () => { return (
- +
) } diff --git a/frontend/src/scenes/trends/persons-modal/peronsModalLogic.test.ts b/frontend/src/scenes/trends/persons-modal/peronsModalLogic.test.ts index f2666ba43f58f..ba343a2ffe02d 100644 --- a/frontend/src/scenes/trends/persons-modal/peronsModalLogic.test.ts +++ b/frontend/src/scenes/trends/persons-modal/peronsModalLogic.test.ts @@ -11,7 +11,7 @@ describe('personsModalLogic', () => { beforeEach(() => { useMocks({ get: { - 'api/projects/:team_id/persons/trends': {}, + 'api/environments/:team_id/persons/trends': {}, }, }) initKeaTests() diff --git a/frontend/src/scenes/web-analytics/SessionAttributionExplorer/sessionAttributionExplorer.stories.tsx b/frontend/src/scenes/web-analytics/SessionAttributionExplorer/sessionAttributionExplorer.stories.tsx index b98489824fd5f..6ba2259fd99ef 100644 --- a/frontend/src/scenes/web-analytics/SessionAttributionExplorer/sessionAttributionExplorer.stories.tsx +++ b/frontend/src/scenes/web-analytics/SessionAttributionExplorer/sessionAttributionExplorer.stories.tsx @@ -16,13 +16,13 @@ const meta: Meta = { decorators: [ mswDecorator({ get: { - '/api/projects/:team_id/query/:id/': async (_, res, ctx) => { + '/api/environments/:team_id/query/:id/': async (_, res, ctx) => { // eslint-disable-next-line @typescript-eslint/no-var-requires return res(ctx.json(require('./__mocks__/sessionAttributionQueryStatus.json'))) }, }, post: { - '/api/projects/:team_id/query/': async (_, res, ctx) => { + '/api/environments/:team_id/query/': async (_, res, ctx) => { // eslint-disable-next-line @typescript-eslint/no-var-requires return res(ctx.json(require('./__mocks__/sessionAttributionQuery.json'))) }, diff --git a/frontend/src/stories/How to mock requests.stories.mdx b/frontend/src/stories/How to mock requests.stories.mdx index dc2562938cbe8..6dd638be8da66 100644 --- a/frontend/src/stories/How to mock requests.stories.mdx +++ b/frontend/src/stories/How to mock requests.stories.mdx @@ -65,7 +65,7 @@ useStorybookMocks({ '/api/status_shorthand': () => [500, { error: 'Error text' }] // complicated param handling - '/api/projects/:team/insights': (req, _, ctx) => { + '/api/environments/:team_id/insights': (req, _, ctx) => { const team = req.params['team'] const shortId = req.url.searchParams.get('short_id') if (shortId === 'my_insight') { diff --git a/frontend/src/test/init.ts b/frontend/src/test/init.ts index 13597c8eebb25..c39467f2953fe 100644 --- a/frontend/src/test/init.ts +++ b/frontend/src/test/init.ts @@ -1,24 +1,30 @@ import { createMemoryHistory } from 'history' import { testUtilsPlugin } from 'kea-test-utils' -import { MOCK_DEFAULT_TEAM } from 'lib/api.mock' +import { MOCK_DEFAULT_PROJECT, MOCK_DEFAULT_TEAM } from 'lib/api.mock' import { dayjs } from 'lib/dayjs' import posthog from 'posthog-js' import { organizationLogic } from 'scenes/organizationLogic' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { projectLogic } from 'scenes/projectLogic' import { teamLogic } from 'scenes/teamLogic' import { initKea } from '~/initKea' -import { AppContext, TeamType } from '~/types' +import { AppContext, ProjectType, TeamType } from '~/types' process.on('unhandledRejection', (err) => { console.warn(err) }) -export function initKeaTests(mountCommonLogic = true, teamForWindowContext: TeamType = MOCK_DEFAULT_TEAM): void { +export function initKeaTests( + mountCommonLogic = true, + teamForWindowContext: TeamType = MOCK_DEFAULT_TEAM, + projectForWindowContext: ProjectType = MOCK_DEFAULT_PROJECT +): void { dayjs.tz.setDefault('UTC') window.POSTHOG_APP_CONTEXT = { ...window.POSTHOG_APP_CONTEXT, current_team: teamForWindowContext, + current_project: projectForWindowContext, } as unknown as AppContext posthog.init('no token', { autocapture: false, @@ -37,6 +43,7 @@ export function initKeaTests(mountCommonLogic = true, teamForWindowContext: Team if (mountCommonLogic) { preflightLogic.mount() teamLogic.mount() + projectLogic.mount() organizationLogic.mount() } } diff --git a/posthog/api/__init__.py b/posthog/api/__init__.py index c79582ed8d726..173909d404df6 100644 --- a/posthog/api/__init__.py +++ b/posthog/api/__init__.py @@ -171,8 +171,8 @@ def register_grandfathered_environment_nested_viewset( "project_dashboard_templates", ["project_id"], ) -project_dashboards_router = projects_router.register( - r"dashboards", dashboard.DashboardsViewSet, "project_dashboards", ["project_id"] +environment_dashboards_router, legacy_project_dashboards_router = register_grandfathered_environment_nested_viewset( + r"dashboards", dashboard.DashboardsViewSet, "environment_dashboards", ["team_id"] ) register_grandfathered_environment_nested_viewset( @@ -418,34 +418,54 @@ def register_grandfathered_environment_nested_viewset( projects_router.register(r"experiments", EnterpriseExperimentsViewSet, "project_experiments", ["project_id"]) register_grandfathered_environment_nested_viewset(r"groups", GroupsViewSet, "environment_groups", ["team_id"]) projects_router.register(r"groups_types", GroupsTypesViewSet, "project_groups_types", ["project_id"]) - project_insights_router = projects_router.register( - r"insights", EnterpriseInsightsViewSet, "project_insights", ["project_id"] + environment_insights_router, legacy_project_insights_router = register_grandfathered_environment_nested_viewset( + r"insights", EnterpriseInsightsViewSet, "environment_insights", ["team_id"] ) register_grandfathered_environment_nested_viewset( r"persons", EnterprisePersonViewSet, "environment_persons", ["team_id"] ) router.register(r"person", LegacyEnterprisePersonViewSet, "persons") else: - project_insights_router = projects_router.register(r"insights", InsightViewSet, "project_insights", ["project_id"]) + environment_insights_router, legacy_project_insights_router = register_grandfathered_environment_nested_viewset( + r"insights", InsightViewSet, "environment_insights", ["team_id"] + ) register_grandfathered_environment_nested_viewset(r"persons", PersonViewSet, "environment_persons", ["team_id"]) router.register(r"person", LegacyPersonViewSet, "persons") -project_dashboards_router.register( +environment_dashboards_router.register( r"sharing", sharing.SharingConfigurationViewSet, "environment_dashboard_sharing", ["team_id", "dashboard_id"], ) +legacy_project_dashboards_router.register( + r"sharing", + sharing.SharingConfigurationViewSet, + "project_dashboard_sharing", + ["team_id", "dashboard_id"], +) -project_insights_router.register( +environment_insights_router.register( r"sharing", sharing.SharingConfigurationViewSet, "environment_insight_sharing", ["team_id", "insight_id"], ) +legacy_project_insights_router.register( + r"sharing", + sharing.SharingConfigurationViewSet, + "project_insight_sharing", + ["team_id", "insight_id"], +) -project_insights_router.register( +environment_insights_router.register( + "thresholds", + alert.ThresholdViewSet, + "environment_insight_thresholds", + ["team_id", "insight_id"], +) +legacy_project_insights_router.register( "thresholds", alert.ThresholdViewSet, "project_insight_thresholds", diff --git a/posthog/api/dashboards/dashboard.py b/posthog/api/dashboards/dashboard.py index 86c2c568e9340..7541b6f00803b 100644 --- a/posthog/api/dashboards/dashboard.py +++ b/posthog/api/dashboards/dashboard.py @@ -2,7 +2,7 @@ from typing import Any, Optional, cast import structlog -from django.db.models import Prefetch, QuerySet +from django.db.models import Prefetch from django.shortcuts import get_object_or_404 from django.utils.timezone import now from rest_framework import exceptions, serializers, viewsets @@ -437,7 +437,12 @@ class DashboardsViewSet( def get_serializer_class(self) -> type[BaseSerializer]: return DashboardBasicSerializer if self.action == "list" else DashboardSerializer - def safely_get_queryset(self, queryset) -> QuerySet: + def dangerously_get_queryset(self): + # Dashboards are retrieved under /environments/ because they include team-specific query results, + # but they are in fact project-level, rather than environment-level + assert self.team.project_id is not None + queryset = self.queryset.filter(team__project_id=self.team.project_id) + include_deleted = ( self.action == "partial_update" and "deleted" in self.request.data @@ -488,7 +493,7 @@ def retrieve(self, request: Request, *args: Any, **kwargs: Any) -> Response: dashboard = get_object_or_404(queryset, pk=pk) dashboard.last_accessed_at = now() dashboard.save(update_fields=["last_accessed_at"]) - serializer = DashboardSerializer(dashboard, context={"view": self, "request": request}) + serializer = DashboardSerializer(dashboard, context=self.get_serializer_context()) return Response(serializer.data) @action(methods=["PATCH"], detail=True) @@ -504,7 +509,7 @@ def move_tile(self, request: Request, *args: Any, **kwargs: Any) -> Response: serializer = DashboardSerializer( Dashboard.objects.get(id=from_dashboard), - context={"view": self, "request": request}, + context=self.get_serializer_context(), ) return Response(serializer.data) @@ -544,7 +549,7 @@ def create_from_template_json(self, request: Request, *args: Any, **kwargs: Any) dashboard.delete() raise - return Response(DashboardSerializer(dashboard, context={"view": self, "request": request}).data) + return Response(DashboardSerializer(dashboard, context=self.get_serializer_context()).data) class LegacyDashboardsViewSet(DashboardsViewSet): diff --git a/posthog/api/event_definition.py b/posthog/api/event_definition.py index d8d1584c9c61e..4156d10f82793 100644 --- a/posthog/api/event_definition.py +++ b/posthog/api/event_definition.py @@ -88,7 +88,7 @@ def dangerously_get_queryset(self): search = self.request.GET.get("search", None) search_query, search_kwargs = term_search_filter_sql(self.search_fields, search) - params = {"team_id": self.team.project_id, "is_posthog_event": "$%", **search_kwargs} + params = {"project_id": self.project_id, "is_posthog_event": "$%", **search_kwargs} order_expressions = [self._ordering_params_from_request()] ingestion_taxonomy_is_available = self.organization.is_feature_available(AvailableFeature.INGESTION_TAXONOMY) @@ -136,11 +136,11 @@ def dangerously_get_object(self): ): from ee.models.event_definition import EnterpriseEventDefinition - enterprise_event = EnterpriseEventDefinition.objects.filter(id=id, team_id=self.team_id).first() + enterprise_event = EnterpriseEventDefinition.objects.filter(id=id, team__project_id=self.project_id).first() if enterprise_event: return enterprise_event - non_enterprise_event = EventDefinition.objects.get(id=id, team_id=self.team_id) + non_enterprise_event = EventDefinition.objects.get(id=id, team__project_id=self.project_id) new_enterprise_event = EnterpriseEventDefinition( eventdefinition_ptr_id=non_enterprise_event.id, description="" ) @@ -148,7 +148,7 @@ def dangerously_get_object(self): new_enterprise_event.save() return new_enterprise_event - return EventDefinition.objects.get(id=id, team_id=self.team_id) + return EventDefinition.objects.get(id=id, team__project_id=self.project_id) def get_serializer_class(self) -> type[serializers.ModelSerializer]: serializer_class = self.serializer_class diff --git a/posthog/api/feature_flag.py b/posthog/api/feature_flag.py index aa9aa8222b9b2..324eb87765441 100644 --- a/posthog/api/feature_flag.py +++ b/posthog/api/feature_flag.py @@ -175,7 +175,7 @@ def validate_key(self, value): exclude_kwargs = {"pk": cast(FeatureFlag, self.instance).pk} if ( - FeatureFlag.objects.filter(key=value, team_id=self.context["team_id"], deleted=False) + FeatureFlag.objects.filter(key=value, team__project_id=self.context["project_id"], deleted=False) .exclude(**exclude_kwargs) .exists() ): diff --git a/posthog/api/insight.py b/posthog/api/insight.py index f27a1f41e559f..a039b7cb1929b 100644 --- a/posthog/api/insight.py +++ b/posthog/api/insight.py @@ -645,6 +645,7 @@ def insight_result(self, insight: Insight) -> InsightResult: return calculate_for_query_based_insight( insight, + team=self.context["get_team"](), dashboard=dashboard, execution_mode=execution_mode, user=None if self.context["request"].user.is_anonymous else self.context["request"].user, @@ -726,7 +727,12 @@ def get_serializer_context(self) -> dict[str, Any]: context["is_shared"] = isinstance(self.request.successful_authenticator, SharingAccessTokenAuthentication) return context - def safely_get_queryset(self, queryset) -> QuerySet: + def dangerously_get_queryset(self): + # Insights are retrieved under /environments/ because they include team-specific query results, + # but they are in fact project-level, rather than environment-level + assert self.team.project_id is not None + queryset = self.queryset.filter(team__project_id=self.team.project_id) + include_deleted = False if isinstance(self.request.successful_authenticator, SharingAccessTokenAuthentication): diff --git a/posthog/api/organization_feature_flag.py b/posthog/api/organization_feature_flag.py index d91ec15ba1c41..3585f5f149849 100644 --- a/posthog/api/organization_feature_flag.py +++ b/posthog/api/organization_feature_flag.py @@ -179,6 +179,7 @@ def copy_flags(self, request, *args, **kwargs): context = { "request": request, "team_id": target_project_id, + "project_id": target_project_id, } existing_flag = FeatureFlag.objects.filter( diff --git a/posthog/api/sharing.py b/posthog/api/sharing.py index ef85e143152c2..d0cf5af56bafd 100644 --- a/posthog/api/sharing.py +++ b/posthog/api/sharing.py @@ -246,6 +246,7 @@ def retrieve(self, request: Request, *args: Any, **kwargs: Any) -> Any: "request": request, "user_permissions": UserPermissions(cast(User, request.user), resource.team), "is_shared": True, + "get_team": lambda: resource.team, } exported_data: dict[str, Any] = {"type": "embed" if embedded else "scene"} diff --git a/posthog/api/test/__snapshots__/test_api_docs.ambr b/posthog/api/test/__snapshots__/test_api_docs.ambr index 59745fc27236d..5f9fbda86651a 100644 --- a/posthog/api/test/__snapshots__/test_api_docs.ambr +++ b/posthog/api/test/__snapshots__/test_api_docs.ambr @@ -11,6 +11,9 @@ '/home/runner/work/posthog/posthog/posthog/batch_exports/http.py: Warning [BatchExportViewSet]: could not derive type of path parameter "project_id" because model "posthog.batch_exports.models.BatchExport" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', '/home/runner/work/posthog/posthog/posthog/batch_exports/http.py: Warning [BatchExportViewSet > BatchExportSerializer]: could not resolve serializer field "HogQLSelectQueryField(required=False)". Defaulting to "string"', '/home/runner/work/posthog/posthog/posthog/batch_exports/http.py: Warning [BatchExportRunViewSet]: could not derive type of path parameter "project_id" because model "posthog.batch_exports.models.BatchExportRun" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', + '/home/runner/work/posthog/posthog/posthog/api/dashboards/dashboard.py: Warning [DashboardsViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.dashboard.Dashboard" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', + '/home/runner/work/posthog/posthog/ee/api/dashboard_collaborator.py: Warning [DashboardCollaboratorViewSet]: could not derive type of path parameter "project_id" because model "ee.models.dashboard_privilege.DashboardPrivilege" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', + '/home/runner/work/posthog/posthog/posthog/api/sharing.py: Warning [SharingConfigurationViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.sharing_configuration.SharingConfiguration" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', '/home/runner/work/posthog/posthog/posthog/api/event.py: Warning [EventViewSet]: could not derive type of path parameter "project_id" because it is untyped and obtaining queryset from the viewset failed. Consider adding a type to the path (e.g. ) or annotating the parameter type with @extend_schema. Defaulting to "string".', '/home/runner/work/posthog/posthog/posthog/models/event/util.py: Warning [EventViewSet > ClickhouseEventSerializer]: unable to resolve type hint for function "get_id". Consider using a type hint or @extend_schema_field. Defaulting to string.', '/home/runner/work/posthog/posthog/posthog/models/event/util.py: Warning [EventViewSet > ClickhouseEventSerializer]: unable to resolve type hint for function "get_distinct_id". Consider using a type hint or @extend_schema_field. Defaulting to string.', @@ -26,6 +29,18 @@ '/home/runner/work/posthog/posthog/posthog/api/exports.py: Warning [ExportedAssetViewSet > ExportedAssetSerializer]: unable to resolve type hint for function "has_content". Consider using a type hint or @extend_schema_field. Defaulting to string.', '/home/runner/work/posthog/posthog/posthog/api/exports.py: Warning [ExportedAssetViewSet > ExportedAssetSerializer]: unable to resolve type hint for function "filename". Consider using a type hint or @extend_schema_field. Defaulting to string.', '/home/runner/work/posthog/posthog/ee/clickhouse/views/groups.py: Warning [GroupsViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.group.group.Group" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', + '/home/runner/work/posthog/posthog/ee/clickhouse/views/insights.py: Warning [EnterpriseInsightsViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.insight.Insight" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', + '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_last_refresh". Consider using a type hint or @extend_schema_field. Defaulting to string.', + '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_cache_target_age". Consider using a type hint or @extend_schema_field. Defaulting to string.', + '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_next_allowed_client_refresh". Consider using a type hint or @extend_schema_field. Defaulting to string.', + '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_result". Consider using a type hint or @extend_schema_field. Defaulting to string.', + '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_hasMore". Consider using a type hint or @extend_schema_field. Defaulting to string.', + '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_columns". Consider using a type hint or @extend_schema_field. Defaulting to string.', + '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_timezone". Consider using a type hint or @extend_schema_field. Defaulting to string.', + '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_is_cached". Consider using a type hint or @extend_schema_field. Defaulting to string.', + '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_query_status". Consider using a type hint or @extend_schema_field. Defaulting to string.', + '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_hogql". Consider using a type hint or @extend_schema_field. Defaulting to string.', + '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_types". Consider using a type hint or @extend_schema_field. Defaulting to string.', '/home/runner/work/posthog/posthog/ee/clickhouse/views/person.py: Warning [EnterprisePersonViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.person.person.Person" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', '/home/runner/work/posthog/posthog/posthog/api/plugin.py: Warning [PipelineDestinationsConfigsViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.plugin.PluginConfig" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', '/home/runner/work/posthog/posthog/posthog/api/plugin.py: Warning [PipelineDestinationsConfigsViewSet > PluginConfigSerializer]: unable to resolve type hint for function "get_config". Consider using a type hint or @extend_schema_field. Defaulting to string.', @@ -43,7 +58,6 @@ '/home/runner/work/posthog/posthog/posthog/session_recordings/session_recording_api.py: Warning [SessionRecordingViewSet]: could not derive type of path parameter "project_id" because model "posthog.session_recordings.models.session_recording.SessionRecording" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', '/home/runner/work/posthog/posthog/posthog/api/person.py: Warning [SessionRecordingViewSet > SessionRecordingSerializer > MinimalPersonSerializer]: unable to resolve type hint for function "get_distinct_ids". Consider using a type hint or @extend_schema_field. Defaulting to string.', '/home/runner/work/posthog/posthog/posthog/session_recordings/session_recording_api.py: Warning [SessionRecordingViewSet > SessionRecordingSerializer]: unable to resolve type hint for function "storage". Consider using a type hint or @extend_schema_field. Defaulting to string.', - '/home/runner/work/posthog/posthog/posthog/api/sharing.py: Warning [SharingConfigurationViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.sharing_configuration.SharingConfiguration" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', '/home/runner/work/posthog/posthog/posthog/api/session.py: Warning [SessionViewSet]: could not derive type of path parameter "project_id" because it is untyped and obtaining queryset from the viewset failed. Consider adding a type to the path (e.g. ) or annotating the parameter type with @extend_schema. Defaulting to "string".', '/home/runner/work/posthog/posthog/ee/api/subscription.py: Warning [SubscriptionViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.subscription.Subscription" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', '/home/runner/work/posthog/posthog/ee/api/subscription.py: Warning [SubscriptionViewSet > SubscriptionSerializer]: unable to resolve type hint for function "summary". Consider using a type hint or @extend_schema_field. Defaulting to string.', @@ -65,8 +79,6 @@ '/home/runner/work/posthog/posthog/posthog/api/annotation.py: Warning [AnnotationsViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.annotation.Annotation" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', '/home/runner/work/posthog/posthog/posthog/api/cohort.py: Warning [CohortViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.cohort.cohort.Cohort" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', '/home/runner/work/posthog/posthog/posthog/api/dashboards/dashboard_templates.py: Warning [DashboardTemplateViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.dashboard_templates.DashboardTemplate" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', - '/home/runner/work/posthog/posthog/posthog/api/dashboards/dashboard.py: Warning [DashboardsViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.dashboard.Dashboard" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', - '/home/runner/work/posthog/posthog/ee/api/dashboard_collaborator.py: Warning [DashboardCollaboratorViewSet]: could not derive type of path parameter "project_id" because model "ee.models.dashboard_privilege.DashboardPrivilege" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', '/home/runner/work/posthog/posthog/posthog/api/early_access_feature.py: Warning [EarlyAccessFeatureViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.early_access_feature.EarlyAccessFeature" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', '/home/runner/work/posthog/posthog/posthog/api/team.py: Warning [TeamViewSet > TeamSerializer]: unable to resolve type hint for function "get_product_intents". Consider using a type hint or @extend_schema_field. Defaulting to string.', "/home/runner/work/posthog/posthog/posthog/api/event_definition.py: Error [EventDefinitionViewSet]: exception raised while getting serializer. Hint: Is get_serializer_class() returning None or is get_queryset() not working without a request? Ignoring the view for now. (Exception: 'AnonymousUser' object has no attribute 'organization')", @@ -75,18 +87,6 @@ '/home/runner/work/posthog/posthog/posthog/api/feature_flag.py: Warning [FeatureFlagViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.feature_flag.feature_flag.FeatureFlag" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', '/home/runner/work/posthog/posthog/ee/api/feature_flag_role_access.py: Warning [FeatureFlagRoleAccessViewSet]: could not derive type of path parameter "project_id" because model "ee.models.feature_flag_role_access.FeatureFlagRoleAccess" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', '/home/runner/work/posthog/posthog/ee/clickhouse/views/groups.py: Warning [GroupsTypesViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.group_type_mapping.GroupTypeMapping" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', - '/home/runner/work/posthog/posthog/ee/clickhouse/views/insights.py: Warning [EnterpriseInsightsViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.insight.Insight" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', - '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_last_refresh". Consider using a type hint or @extend_schema_field. Defaulting to string.', - '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_cache_target_age". Consider using a type hint or @extend_schema_field. Defaulting to string.', - '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_next_allowed_client_refresh". Consider using a type hint or @extend_schema_field. Defaulting to string.', - '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_result". Consider using a type hint or @extend_schema_field. Defaulting to string.', - '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_hasMore". Consider using a type hint or @extend_schema_field. Defaulting to string.', - '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_columns". Consider using a type hint or @extend_schema_field. Defaulting to string.', - '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_timezone". Consider using a type hint or @extend_schema_field. Defaulting to string.', - '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_is_cached". Consider using a type hint or @extend_schema_field. Defaulting to string.', - '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_query_status". Consider using a type hint or @extend_schema_field. Defaulting to string.', - '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_hogql". Consider using a type hint or @extend_schema_field. Defaulting to string.', - '/home/runner/work/posthog/posthog/posthog/api/insight.py: Warning [EnterpriseInsightsViewSet > InsightSerializer]: unable to resolve type hint for function "get_types". Consider using a type hint or @extend_schema_field. Defaulting to string.', '/home/runner/work/posthog/posthog/posthog/api/notebook.py: Warning [NotebookViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.notebook.notebook.Notebook" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', "/home/runner/work/posthog/posthog/posthog/api/property_definition.py: Error [PropertyDefinitionViewSet]: exception raised while getting serializer. Hint: Is get_serializer_class() returning None or is get_queryset() not working without a request? Ignoring the view for now. (Exception: 'AnonymousUser' object has no attribute 'organization')", '/home/runner/work/posthog/posthog/posthog/api/property_definition.py: Warning [PropertyDefinitionViewSet]: could not derive type of path parameter "project_id" because model "posthog.models.property_definition.PropertyDefinition" contained no such field. Consider annotating parameter with @extend_schema. Defaulting to "string".', @@ -99,6 +99,9 @@ 'Warning: encountered multiple names for the same choice set (EffectivePrivilegeLevelEnum). This may be unwanted even though the generated schema is technically correct. Add an entry to ENUM_NAME_OVERRIDES to fix the naming.', 'Warning: encountered multiple names for the same choice set (MembershipLevelEnum). This may be unwanted even though the generated schema is technically correct. Add an entry to ENUM_NAME_OVERRIDES to fix the naming.', 'Warning: operationId "environments_app_metrics_historical_exports_retrieve" has collisions [(\'/api/environments/{project_id}/app_metrics/{plugin_config_id}/historical_exports/\', \'get\'), (\'/api/environments/{project_id}/app_metrics/{plugin_config_id}/historical_exports/{id}/\', \'get\')]. resolving with numeral suffixes.', + 'Warning: operationId "environments_insights_activity_retrieve" has collisions [(\'/api/environments/{project_id}/insights/{id}/activity/\', \'get\'), (\'/api/environments/{project_id}/insights/activity/\', \'get\')]. resolving with numeral suffixes.', + 'Warning: operationId "Funnels" has collisions [(\'/api/environments/{project_id}/insights/funnel/\', \'post\'), (\'/api/projects/{project_id}/insights/funnel/\', \'post\')]. resolving with numeral suffixes.', + 'Warning: operationId "Trends" has collisions [(\'/api/environments/{project_id}/insights/trend/\', \'post\'), (\'/api/projects/{project_id}/insights/trend/\', \'post\')]. resolving with numeral suffixes.', 'Warning: operationId "environments_persons_activity_retrieve" has collisions [(\'/api/environments/{project_id}/persons/{id}/activity/\', \'get\'), (\'/api/environments/{project_id}/persons/activity/\', \'get\')]. resolving with numeral suffixes.', 'Warning: operationId "list" has collisions [(\'/api/organizations/\', \'get\'), (\'/api/organizations/{organization_id}/projects/\', \'get\')]. resolving with numeral suffixes.', 'Warning: operationId "create" has collisions [(\'/api/organizations/\', \'post\'), (\'/api/organizations/{organization_id}/projects/\', \'post\')]. resolving with numeral suffixes.', diff --git a/posthog/api/test/__snapshots__/test_insight.ambr b/posthog/api/test/__snapshots__/test_insight.ambr index dce82cffcf694..6f8487c876f95 100644 --- a/posthog/api/test/__snapshots__/test_insight.ambr +++ b/posthog/api/test/__snapshots__/test_insight.ambr @@ -1228,8 +1228,8 @@ SELECT COUNT(*) AS "__count" FROM "posthog_dashboarditem" INNER JOIN "posthog_team" ON ("posthog_dashboarditem"."team_id" = "posthog_team"."id") - WHERE (NOT ("posthog_dashboarditem"."deleted") - AND "posthog_team"."project_id" = 2) + WHERE ("posthog_team"."project_id" = 2 + AND NOT ("posthog_dashboarditem"."deleted")) ''' # --- # name: TestInsight.test_listing_insights_does_not_nplus1.26 @@ -1376,8 +1376,8 @@ INNER JOIN "posthog_team" ON ("posthog_dashboarditem"."team_id" = "posthog_team"."id") LEFT OUTER JOIN "posthog_user" ON ("posthog_dashboarditem"."created_by_id" = "posthog_user"."id") LEFT OUTER JOIN "posthog_user" T5 ON ("posthog_dashboarditem"."last_modified_by_id" = T5."id") - WHERE (NOT ("posthog_dashboarditem"."deleted") - AND "posthog_team"."project_id" = 2) + WHERE ("posthog_team"."project_id" = 2 + AND NOT ("posthog_dashboarditem"."deleted")) ORDER BY "posthog_dashboarditem"."order" ASC LIMIT 100 ''' diff --git a/posthog/api/test/__snapshots__/test_organization_feature_flag.ambr b/posthog/api/test/__snapshots__/test_organization_feature_flag.ambr index f16a07a370c06..06e36c9b1ce79 100644 --- a/posthog/api/test/__snapshots__/test_organization_feature_flag.ambr +++ b/posthog/api/test/__snapshots__/test_organization_feature_flag.ambr @@ -1914,9 +1914,10 @@ ''' SELECT 1 AS "a" FROM "posthog_featureflag" + INNER JOIN "posthog_team" ON ("posthog_featureflag"."team_id" = "posthog_team"."id") WHERE (NOT "posthog_featureflag"."deleted" AND "posthog_featureflag"."key" = 'copied-flag-key' - AND "posthog_featureflag"."team_id" = 2) + AND "posthog_team"."project_id" = 2) LIMIT 1 ''' # --- diff --git a/posthog/api/test/dashboards/__init__.py b/posthog/api/test/dashboards/__init__.py index ad6505b5a61a7..dff375dcae21d 100644 --- a/posthog/api/test/dashboards/__init__.py +++ b/posthog/api/test/dashboards/__init__.py @@ -83,6 +83,8 @@ def list_dashboards( team_id: Optional[int] = None, expected_status: int = status.HTTP_200_OK, query_params: Optional[dict] = None, + *, + parent: Literal["project", "environment"] = "project", ) -> dict: if team_id is None: team_id = self.team.id @@ -90,7 +92,7 @@ def list_dashboards( if query_params is None: query_params = {} - response = self.client.get(f"/api/projects/{team_id}/dashboards/", query_params) + response = self.client.get(f"/api/{parent}s/{team_id}/dashboards/", query_params) self.assertEqual(response.status_code, expected_status) response_json = response.json() diff --git a/posthog/api/test/dashboards/__snapshots__/test_dashboard.ambr b/posthog/api/test/dashboards/__snapshots__/test_dashboard.ambr index e0a2eaef4dd4a..da2c2cec45a27 100644 --- a/posthog/api/test/dashboards/__snapshots__/test_dashboard.ambr +++ b/posthog/api/test/dashboards/__snapshots__/test_dashboard.ambr @@ -304,8 +304,8 @@ FROM "posthog_dashboard" INNER JOIN "posthog_team" ON ("posthog_dashboard"."team_id" = "posthog_team"."id") LEFT OUTER JOIN "posthog_user" ON ("posthog_dashboard"."created_by_id" = "posthog_user"."id") - WHERE (NOT ("posthog_dashboard"."deleted") - AND "posthog_team"."project_id" = 2 + WHERE ("posthog_team"."project_id" = 2 + AND NOT ("posthog_dashboard"."deleted") AND "posthog_dashboard"."id" = 2) LIMIT 21 ''' @@ -1941,8 +1941,8 @@ SELECT COUNT(*) AS "__count" FROM "posthog_dashboard" INNER JOIN "posthog_team" ON ("posthog_dashboard"."team_id" = "posthog_team"."id") - WHERE (NOT ("posthog_dashboard"."deleted") - AND "posthog_team"."project_id" = 2) + WHERE ("posthog_team"."project_id" = 2 + AND NOT ("posthog_dashboard"."deleted")) ''' # --- # name: TestDashboard.test_listing_dashboards_is_not_nplus1.27 @@ -1992,8 +1992,8 @@ FROM "posthog_dashboard" INNER JOIN "posthog_team" ON ("posthog_dashboard"."team_id" = "posthog_team"."id") LEFT OUTER JOIN "posthog_user" ON ("posthog_dashboard"."created_by_id" = "posthog_user"."id") - WHERE (NOT ("posthog_dashboard"."deleted") - AND "posthog_team"."project_id" = 2) + WHERE ("posthog_team"."project_id" = 2 + AND NOT ("posthog_dashboard"."deleted")) ORDER BY "posthog_dashboard"."pinned" DESC, "posthog_dashboard"."name" ASC LIMIT 300 @@ -3692,8 +3692,8 @@ FROM "posthog_dashboard" INNER JOIN "posthog_team" ON ("posthog_dashboard"."team_id" = "posthog_team"."id") LEFT OUTER JOIN "posthog_user" ON ("posthog_dashboard"."created_by_id" = "posthog_user"."id") - WHERE (NOT ("posthog_dashboard"."deleted") - AND "posthog_team"."project_id" = 2 + WHERE ("posthog_team"."project_id" = 2 + AND NOT ("posthog_dashboard"."deleted") AND "posthog_dashboard"."id" = 2) LIMIT 21 ''' @@ -8033,8 +8033,8 @@ FROM "posthog_dashboard" INNER JOIN "posthog_team" ON ("posthog_dashboard"."team_id" = "posthog_team"."id") LEFT OUTER JOIN "posthog_user" ON ("posthog_dashboard"."created_by_id" = "posthog_user"."id") - WHERE (NOT ("posthog_dashboard"."deleted") - AND "posthog_team"."project_id" = 2 + WHERE ("posthog_team"."project_id" = 2 + AND NOT ("posthog_dashboard"."deleted") AND "posthog_dashboard"."id" = 2) LIMIT 21 ''' @@ -9368,8 +9368,8 @@ SELECT COUNT(*) AS "__count" FROM "posthog_dashboard" INNER JOIN "posthog_team" ON ("posthog_dashboard"."team_id" = "posthog_team"."id") - WHERE (NOT ("posthog_dashboard"."deleted") - AND "posthog_team"."project_id" = 2) + WHERE ("posthog_team"."project_id" = 2 + AND NOT ("posthog_dashboard"."deleted")) ''' # --- # name: TestDashboard.test_retrieve_dashboard_list.29 @@ -9419,8 +9419,8 @@ FROM "posthog_dashboard" INNER JOIN "posthog_team" ON ("posthog_dashboard"."team_id" = "posthog_team"."id") LEFT OUTER JOIN "posthog_user" ON ("posthog_dashboard"."created_by_id" = "posthog_user"."id") - WHERE (NOT ("posthog_dashboard"."deleted") - AND "posthog_team"."project_id" = 2) + WHERE ("posthog_team"."project_id" = 2 + AND NOT ("posthog_dashboard"."deleted")) ORDER BY "posthog_dashboard"."pinned" DESC, "posthog_dashboard"."name" ASC LIMIT 100 diff --git a/posthog/api/test/dashboards/test_dashboard.py b/posthog/api/test/dashboards/test_dashboard.py index d3e7e43d7f200..1b9c98b029f01 100644 --- a/posthog/api/test/dashboards/test_dashboard.py +++ b/posthog/api/test/dashboards/test_dashboard.py @@ -14,6 +14,7 @@ from posthog.hogql_queries.legacy_compatibility.filter_to_query import filter_to_query from posthog.models import Dashboard, DashboardTile, Filter, Insight, Team, User from posthog.models.organization import Organization +from posthog.models.project import Project from posthog.models.sharing_configuration import SharingConfiguration from posthog.models.signals import mute_selected_signals from posthog.test.base import ( @@ -82,6 +83,33 @@ def test_retrieve_dashboard_list(self): dashboard_names, ) + def test_retrieve_dashboard_list_includes_other_environments(self): + other_team_in_project = Team.objects.create(organization=self.organization, project=self.project) + _, team_in_other_project = Project.objects.create_with_team( + organization=self.organization, initiating_user=self.user + ) + + dashboard_a_id, _ = self.dashboard_api.create_dashboard({"name": "A"}, team_id=self.team.id) + dashboard_b_id, _ = self.dashboard_api.create_dashboard({"name": "B"}, team_id=other_team_in_project.id) + self.dashboard_api.create_dashboard({"name": "C"}, team_id=team_in_other_project.id) + + response_project_data = self.dashboard_api.list_dashboards(self.project.id) + response_env_current_data = self.dashboard_api.list_dashboards(self.team.id, parent="environment") + response_env_other_data = self.dashboard_api.list_dashboards(other_team_in_project.id, parent="environment") + + self.assertEqual( + {dashboard["id"] for dashboard in response_project_data["results"]}, + {dashboard_a_id, dashboard_b_id}, + ) + self.assertEqual( + {dashboard["id"] for dashboard in response_env_current_data["results"]}, + {dashboard_a_id, dashboard_b_id}, + ) + self.assertEqual( + {dashboard["id"] for dashboard in response_env_other_data["results"]}, + {dashboard_a_id, dashboard_b_id}, + ) + @snapshot_postgres_queries def test_retrieve_dashboard(self): dashboard = Dashboard.objects.create(team=self.team, name="private dashboard", created_by=self.user) @@ -555,7 +583,9 @@ def test_dashboard_insights_out_of_synch_with_tiles_are_not_shown(self): mock_view.action = "retrieve" mock_request = MagicMock() mock_request.query_params.get.return_value = None - dashboard_data = DashboardSerializer(dashboard, context={"view": mock_view, "request": mock_request}).data + dashboard_data = DashboardSerializer( + dashboard, context={"view": mock_view, "request": mock_request, "get_team": lambda: self.team} + ).data assert len(dashboard_data["tiles"]) == 1 def test_dashboard_insight_tiles_can_be_loaded_correct_context(self): diff --git a/posthog/api/test/test_feature_flag.py b/posthog/api/test/test_feature_flag.py index 7667de035550b..f43e49aaee91c 100644 --- a/posthog/api/test/test_feature_flag.py +++ b/posthog/api/test/test_feature_flag.py @@ -5151,6 +5151,7 @@ def test_feature_flags_v3_with_group_properties(self, *args): self.user = User.objects.create_and_join(self.organization, "random@test.com", "password", "first_name") team_id = self.team.pk + project_id = self.team.project_id rf = RequestFactory() create_request = rf.post(f"api/projects/{self.team.pk}/feature_flags/", {"name": "xyz"}) create_request.user = self.user @@ -5185,7 +5186,7 @@ def test_feature_flags_v3_with_group_properties(self, *args): ], }, }, - context={"team_id": team_id, "request": create_request}, + context={"team_id": team_id, "project_id": project_id, "request": create_request}, ) self.assertTrue(serialized_data.is_valid()) serialized_data.save() @@ -5200,7 +5201,7 @@ def test_feature_flags_v3_with_group_properties(self, *args): "groups": [{"properties": [], "rollout_percentage": None}], }, }, - context={"team_id": team_id, "request": create_request}, + context={"team_id": team_id, "project_id": project_id, "request": create_request}, ) self.assertTrue(serialized_data.is_valid()) serialized_data.save() @@ -5256,6 +5257,7 @@ def test_feature_flags_v3_with_person_properties(self, mock_counter, *args): self.user = User.objects.create_and_join(self.organization, "random@test.com", "password", "first_name") team_id = self.team.pk + project_id = self.team.project_id rf = RequestFactory() create_request = rf.post(f"api/projects/{self.team.pk}/feature_flags/", {"name": "xyz"}) create_request.user = self.user @@ -5286,7 +5288,7 @@ def test_feature_flags_v3_with_person_properties(self, mock_counter, *args): ] }, }, - context={"team_id": team_id, "request": create_request}, + context={"team_id": team_id, "project_id": project_id, "request": create_request}, ) self.assertTrue(serialized_data.is_valid()) serialized_data.save() @@ -5298,7 +5300,7 @@ def test_feature_flags_v3_with_person_properties(self, mock_counter, *args): "key": "default-flag", "filters": {"groups": [{"properties": [], "rollout_percentage": None}]}, }, - context={"team_id": team_id, "request": create_request}, + context={"team_id": team_id, "project_id": project_id, "request": create_request}, ) self.assertTrue(serialized_data.is_valid()) serialized_data.save() @@ -5354,6 +5356,7 @@ def test_feature_flags_v3_with_a_working_slow_db(self, mock_postgres_check): self.user = User.objects.create_and_join(self.organization, "random@test.com", "password", "first_name") team_id = self.team.pk + project_id = self.team.project_id rf = RequestFactory() create_request = rf.post(f"api/projects/{self.team.pk}/feature_flags/", {"name": "xyz"}) create_request.user = self.user @@ -5384,7 +5387,7 @@ def test_feature_flags_v3_with_a_working_slow_db(self, mock_postgres_check): ] }, }, - context={"team_id": team_id, "request": create_request}, + context={"team_id": team_id, "project_id": project_id, "request": create_request}, ) self.assertTrue(serialized_data.is_valid()) serialized_data.save() @@ -5396,7 +5399,7 @@ def test_feature_flags_v3_with_a_working_slow_db(self, mock_postgres_check): "key": "default-flag", "filters": {"groups": [{"properties": [], "rollout_percentage": None}]}, }, - context={"team_id": team_id, "request": create_request}, + context={"team_id": team_id, "project_id": project_id, "request": create_request}, ) self.assertTrue(serialized_data.is_valid()) serialized_data.save() @@ -5454,6 +5457,7 @@ def test_feature_flags_v3_with_skip_database_setting(self, mock_postgres_check): self.user = User.objects.create_and_join(self.organization, "random@test.com", "password", "first_name") team_id = self.team.pk + project_id = self.team.project_id rf = RequestFactory() create_request = rf.post(f"api/projects/{self.team.pk}/feature_flags/", {"name": "xyz"}) create_request.user = self.user @@ -5484,7 +5488,7 @@ def test_feature_flags_v3_with_skip_database_setting(self, mock_postgres_check): ] }, }, - context={"team_id": team_id, "request": create_request}, + context={"team_id": team_id, "project_id": project_id, "request": create_request}, ) self.assertTrue(serialized_data.is_valid()) serialized_data.save() @@ -5496,7 +5500,7 @@ def test_feature_flags_v3_with_skip_database_setting(self, mock_postgres_check): "key": "default-flag", "filters": {"groups": [{"properties": [], "rollout_percentage": None}]}, }, - context={"team_id": team_id, "request": create_request}, + context={"team_id": team_id, "project_id": project_id, "request": create_request}, ) self.assertTrue(serialized_data.is_valid()) serialized_data.save() @@ -5661,6 +5665,7 @@ def test_feature_flags_v3_with_group_properties_and_slow_db(self, mock_counter, self.user = User.objects.create_and_join(self.organization, "randomXYZ@test.com", "password", "first_name") team_id = self.team.pk + project_id = self.team.project_id rf = RequestFactory() create_request = rf.post(f"api/projects/{self.team.pk}/feature_flags/", {"name": "xyz"}) create_request.user = self.user @@ -5695,7 +5700,7 @@ def test_feature_flags_v3_with_group_properties_and_slow_db(self, mock_counter, ], }, }, - context={"team_id": team_id, "request": create_request}, + context={"team_id": team_id, "project_id": project_id, "request": create_request}, ) self.assertTrue(serialized_data.is_valid()) serialized_data.save() @@ -5710,7 +5715,7 @@ def test_feature_flags_v3_with_group_properties_and_slow_db(self, mock_counter, "groups": [{"properties": [], "rollout_percentage": None}], }, }, - context={"team_id": team_id, "request": create_request}, + context={"team_id": team_id, "project_id": project_id, "request": create_request}, ) self.assertTrue(serialized_data.is_valid()) serialized_data.save() @@ -5782,6 +5787,7 @@ def test_feature_flags_v3_with_experience_continuity_working_slow_db(self, mock_ self.user = User.objects.create_and_join(self.organization, "random12@test.com", "password", "first_name") team_id = self.team.pk + project_id = self.team.project_id rf = RequestFactory() create_request = rf.post(f"api/projects/{self.team.pk}/feature_flags/", {"name": "xyz"}) create_request.user = self.user @@ -5813,7 +5819,7 @@ def test_feature_flags_v3_with_experience_continuity_working_slow_db(self, mock_ }, "ensure_experience_continuity": True, }, - context={"team_id": team_id, "request": create_request}, + context={"team_id": team_id, "project_id": project_id, "request": create_request}, ) self.assertTrue(serialized_data.is_valid()) serialized_data.save() @@ -5825,7 +5831,7 @@ def test_feature_flags_v3_with_experience_continuity_working_slow_db(self, mock_ "key": "default-flag", "filters": {"groups": [{"properties": [], "rollout_percentage": None}]}, }, - context={"team_id": team_id, "request": create_request}, + context={"team_id": team_id, "project_id": project_id, "request": create_request}, ) self.assertTrue(serialized_data.is_valid()) serialized_data.save() @@ -5878,6 +5884,7 @@ def test_feature_flags_v3_with_experience_continuity_and_incident_mode(self, moc self.user = User.objects.create_and_join(self.organization, "random12@test.com", "password", "first_name") team_id = self.team.pk + project_id = self.team.project_id rf = RequestFactory() create_request = rf.post(f"api/projects/{self.team.pk}/feature_flags/", {"name": "xyz"}) create_request.user = self.user @@ -5909,7 +5916,7 @@ def test_feature_flags_v3_with_experience_continuity_and_incident_mode(self, moc }, "ensure_experience_continuity": True, }, - context={"team_id": team_id, "request": create_request}, + context={"team_id": team_id, "project_id": project_id, "request": create_request}, ) self.assertTrue(serialized_data.is_valid()) serialized_data.save() @@ -5921,7 +5928,7 @@ def test_feature_flags_v3_with_experience_continuity_and_incident_mode(self, moc "key": "default-flag", "filters": {"groups": [{"properties": [], "rollout_percentage": None}]}, }, - context={"team_id": team_id, "request": create_request}, + context={"team_id": team_id, "project_id": project_id, "request": create_request}, ) self.assertTrue(serialized_data.is_valid()) serialized_data.save() diff --git a/posthog/api/test/test_insight.py b/posthog/api/test/test_insight.py index 6834ae2d6c40f..3aa7723fb9557 100644 --- a/posthog/api/test/test_insight.py +++ b/posthog/api/test/test_insight.py @@ -33,6 +33,7 @@ User, ) from posthog.models.insight_caching_state import InsightCachingState +from posthog.models.project import Project from posthog.schema import ( DataTableNode, DataVisualizationNode, @@ -91,6 +92,43 @@ def test_get_insight_items(self) -> None: self.assertEqual(len(response["results"]), 1) + def test_get_insight_items_all_environments_included(self) -> None: + filter_dict = { + "events": [{"id": "$pageview"}], + "properties": [{"key": "$browser", "value": "Mac OS X"}], + } + + other_team_in_project = Team.objects.create(organization=self.organization, project=self.project) + _, team_in_other_project = Project.objects.create_with_team( + organization=self.organization, initiating_user=self.user + ) + + insight_a = Insight.objects.create( + filters=Filter(data=filter_dict).to_dict(), + team=self.team, + created_by=self.user, + ) + insight_b = Insight.objects.create( + filters=Filter(data=filter_dict).to_dict(), + team=other_team_in_project, + created_by=self.user, + ) + Insight.objects.create( + filters=Filter(data=filter_dict).to_dict(), + team=team_in_other_project, + created_by=self.user, + ) + + # All of these three ways should return the same set of insights, + # i.e. all insights in the test project regardless of environment + response_project = self.client.get(f"/api/projects/{self.project.id}/insights/").json() + response_env_current = self.client.get(f"/api/environments/{self.team.id}/insights/").json() + response_env_other = self.client.get(f"/api/environments/{other_team_in_project.id}/insights/").json() + + self.assertEqual({insight["id"] for insight in response_project["results"]}, {insight_a.id, insight_b.id}) + self.assertEqual({insight["id"] for insight in response_env_current["results"]}, {insight_a.id, insight_b.id}) + self.assertEqual({insight["id"] for insight in response_env_other["results"]}, {insight_a.id, insight_b.id}) + @patch("posthoganalytics.capture") def test_created_updated_and_last_modified(self, mock_capture: mock.Mock) -> None: alt_user = User.objects.create_and_join(self.organization, "team2@posthog.com", None) @@ -339,6 +377,7 @@ def test_get_insight_in_shared_context(self) -> None: mock.ANY, dashboard=mock.ANY, execution_mode=ExecutionMode.EXTENDED_CACHE_CALCULATE_ASYNC_IF_STALE, + team=self.team, user=mock.ANY, filters_override=None, ) @@ -351,6 +390,7 @@ def test_get_insight_in_shared_context(self) -> None: mock.ANY, dashboard=mock.ANY, execution_mode=ExecutionMode.RECENT_CACHE_CALCULATE_BLOCKING_IF_STALE, + team=self.team, user=mock.ANY, filters_override=None, ) diff --git a/posthog/api/utils.py b/posthog/api/utils.py index 69abed44fd27f..514534990a8f0 100644 --- a/posthog/api/utils.py +++ b/posthog/api/utils.py @@ -312,7 +312,7 @@ def create_event_definitions_sql( SELECT {",".join(event_definition_fields)} FROM posthog_eventdefinition {enterprise_join} - WHERE team_id = %(team_id)s {conditions} + WHERE team_id = %(project_id)s {conditions} ORDER BY {additional_ordering}name ASC """ diff --git a/posthog/caching/calculate_results.py b/posthog/caching/calculate_results.py index 7da32bb9e88cd..985332c3c7206 100644 --- a/posthog/caching/calculate_results.py +++ b/posthog/caching/calculate_results.py @@ -125,6 +125,7 @@ def get_cache_type(cacheable: Optional[FilterType] | Optional[dict]) -> CacheTyp def calculate_for_query_based_insight( insight: Insight, *, + team: Team, dashboard: Optional[Dashboard] = None, execution_mode: ExecutionMode, user: Optional[User], @@ -133,12 +134,12 @@ def calculate_for_query_based_insight( from posthog.caching.fetch_from_cache import InsightResult, NothingInCacheResult from posthog.caching.insight_cache import update_cached_state - tag_queries(team_id=insight.team_id, insight_id=insight.pk) + tag_queries(team_id=team.id, insight_id=insight.pk) if dashboard: tag_queries(dashboard_id=dashboard.pk) response = process_response = process_query_dict( - insight.team, + team, insight.query, dashboard_filters_json=( filters_override if filters_override is not None else dashboard.filters if dashboard is not None else None @@ -161,7 +162,7 @@ def calculate_for_query_based_insight( last_refresh = response.get("last_refresh") if isinstance(cache_key, str) and isinstance(last_refresh, datetime): update_cached_state( # Updating the relevant InsightCachingState - insight.team_id, + team.id, cache_key, last_refresh, result=None, # Not caching the result here, since in HogQL this is the query runner's responsibility diff --git a/posthog/tasks/alerts/checks.py b/posthog/tasks/alerts/checks.py index 7af02e97a2ccd..7c66c1158c12b 100644 --- a/posthog/tasks/alerts/checks.py +++ b/posthog/tasks/alerts/checks.py @@ -274,6 +274,7 @@ def check_alert_atomically(alert: AlertConfiguration) -> None: calculation_result = calculate_for_query_based_insight( insight, + team=alert.team, execution_mode=ExecutionMode.RECENT_CACHE_CALCULATE_BLOCKING_IF_STALE, user=None, filters_override=filters_override,