mirror of
https://github.com/PostHog/posthog.git
synced 2024-11-28 09:16:49 +01:00
Migrate event-related logics to be project-based (#6566)
* Instrument legacy endpoint debugging * Add `currentTeamId` to `teamLogic` * Add logics utils * Update annotations endpoint in the frontend * Limit legacy endpoint logging to DEBUG * Fix `annotationsTableLogic` usage * Update test_annotation.py * Add `test_deleting_annotation_of_other_team_prevented` * Comment out debug code * Migrate actions-related code to project-based approach * Fix `infiniteTestLogic` * Rework approach * Remove redundant lines * Fix mixup * Fix non-logged-in scenes * Fix Python cast * Align approach to `teamLogic`-based * Fix logic tests * Clean up code * Fix stupid omission * Restore `props` in `connect`s * Fix capitalization * Fix action creation * Fix `ActionEdit` props type * Migrate events-related code to project-based approach * Remove `MOCK_ORGANIZATION_ID` * Update sessionsPlayLogic.test.ts * Fix logic tests * Fix duplicate imports * Reduce duplication in URL test instrumentation * Update API interception paths in tests * Fix `Person.cy-spec.js` * Add comments in `posthog/api/__init__.py` * reduce test noise * Update infiniteListLogic.ts * Simplify `teamLogic` seeding * Simplify `teamLogic` seeding better * Fix `organizationLogic` tests * Migrate feature flags-related logics to be project-based (#6603) * Migrate feature flags-related logics to be project-based * Add comment * Migrate insight-related logics to be project-based (#6574) * Migrate insight-related logics to be project-based * Migrate over the rest and fix logic tests * Update funnelLogic.ts * Fix URL formatting in tests * Update dashboardLogic.tsx * Remove now redundant `initTeamLogic` * Add tracking comments Co-authored-by: Marius Andra <marius.andra@gmail.com>
This commit is contained in:
parent
1f143d109e
commit
165ffcaffb
@ -1,3 +1,5 @@
|
||||
import json
|
||||
|
||||
from ee.api.test.base import APILicensedTest
|
||||
from posthog.models import User
|
||||
|
||||
@ -6,7 +8,9 @@ class TestQueryMiddleware(APILicensedTest):
|
||||
def test_query(self):
|
||||
self.user.is_staff = True
|
||||
self.user.save()
|
||||
response = self.client.get('/api/insight/trend/?events=[{"id": "$pageview"}]')
|
||||
response = self.client.get(
|
||||
f'/api/projects/{self.team.id}/insights/trend/?events={json.dumps([{"id": "$pageview"}])}'
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response = self.client.get("/api/debug_ch_queries/").json()
|
||||
self.assertIn("SELECT", response[0]["query"]) # type: ignore
|
||||
@ -14,7 +18,7 @@ class TestQueryMiddleware(APILicensedTest):
|
||||
# Test saving queries if we're impersonating a user
|
||||
user2 = User.objects.create_and_join(organization=self.organization, email="test", password="bla")
|
||||
self.client.post("/admin/login/user/{}/".format(user2.pk))
|
||||
self.client.get('/api/insight/trend/?events=[{"id": "$pageleave"}]')
|
||||
self.client.get(f'/api/projects/{self.team.id}/insights/trend/?events={json.dumps([{"id": "$pageleave"}])}')
|
||||
|
||||
response = self.client.get("/api/debug_ch_queries/").json()
|
||||
self.assertIn("SELECT", response[0]["query"]) # type: ignore
|
||||
|
@ -166,7 +166,7 @@ class ClickhouseEventsViewSet(EventViewSet):
|
||||
return Response({"result": SessionsListEvents().run(filter=filter, team=self.team)})
|
||||
|
||||
# ******************************************
|
||||
# /event/session_recording
|
||||
# /events/session_recording
|
||||
# params:
|
||||
# - session_recording_id: (string) id of the session recording
|
||||
# - save_view: (boolean) save view of the recording
|
||||
|
@ -110,7 +110,7 @@ class ClickhouseInsightsViewSet(InsightViewSet):
|
||||
return {"result": funnel_order_class(team=team, filter=filter).run()}
|
||||
|
||||
# ******************************************
|
||||
# /insight/funnel/correlation
|
||||
# /projects/:id/insights/funnel/correlation
|
||||
#
|
||||
# params:
|
||||
# - params are the same as for funnel
|
||||
|
@ -37,10 +37,10 @@ class ClickhouseTestEventApi(
|
||||
# For ClickHouse we normally only query the last day,
|
||||
# but if a user doesn't have many events we still want to return events that are older
|
||||
patch_sync_execute.return_value = [("event", "d", "{}", timezone.now(), "d", "d", "d")]
|
||||
response = self.client.get("/api/event/").json()
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events/").json()
|
||||
self.assertEqual(len(response["results"]), 1)
|
||||
self.assertEqual(patch_sync_execute.call_count, 2)
|
||||
|
||||
patch_sync_execute.return_value = [("event", "d", "{}", timezone.now(), "d", "d", "d") for _ in range(0, 100)]
|
||||
response = self.client.get("/api/event/").json()
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events/").json()
|
||||
self.assertEqual(patch_sync_execute.call_count, 3)
|
||||
|
@ -32,7 +32,9 @@ class ClickhouseTestInsights(
|
||||
self.organization_membership.save()
|
||||
self.team.access_control = False
|
||||
self.team.save()
|
||||
response = self.client.get("/api/insight/trend/?events={}".format(json.dumps([{"id": "$pageview"}])))
|
||||
response = self.client.get(
|
||||
f"/api/projects/{self.team.id}/insights/trend/?events={json.dumps([{'id': '$pageview'}])}"
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
def test_insight_trends_forbidden_if_project_private_and_org_member(self):
|
||||
@ -40,7 +42,9 @@ class ClickhouseTestInsights(
|
||||
self.organization_membership.save()
|
||||
self.team.access_control = True
|
||||
self.team.save()
|
||||
response = self.client.get("/api/insight/trend/?events={}".format(json.dumps([{"id": "$pageview"}])))
|
||||
response = self.client.get(
|
||||
f"/api/projects/{self.team.id}/insights/trend/?events={json.dumps([{'id': '$pageview'}])}"
|
||||
)
|
||||
self.assertDictEqual(self.permission_denied_response("You don't have access to the project."), response.json())
|
||||
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||
|
||||
@ -52,7 +56,9 @@ class ClickhouseTestInsights(
|
||||
self_team_membership = ExplicitTeamMembership.objects.create(
|
||||
team=self.team, parent_membership=self.organization_membership, level=ExplicitTeamMembership.Level.MEMBER
|
||||
)
|
||||
response = self.client.get("/api/insight/trend/?events={}".format(json.dumps([{"id": "$pageview"}])))
|
||||
response = self.client.get(
|
||||
f"/api/projects/{self.team.id}/insights/trend/?events={json.dumps([{'id': '$pageview'}])}"
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
|
||||
@ -67,7 +73,7 @@ class ClickhouseTestFunnelTypes(ClickhouseTestMixin, APIBaseTest):
|
||||
_create_event(team=self.team, event="step one", distinct_id="2")
|
||||
|
||||
response = self.client.post(
|
||||
"/api/insight/funnel/",
|
||||
f"/api/projects/{self.team.id}/insights/funnel/",
|
||||
{
|
||||
"events": [
|
||||
{"id": "step one", "type": "events", "order": 0},
|
||||
@ -96,7 +102,7 @@ class ClickhouseTestFunnelTypes(ClickhouseTestMixin, APIBaseTest):
|
||||
_create_event(team=self.team, event="step two", distinct_id="2")
|
||||
|
||||
response = self.client.post(
|
||||
"/api/insight/funnel/",
|
||||
f"/api/projects/{self.team.id}/insights/funnel/",
|
||||
{
|
||||
"events": [
|
||||
{"id": "step one", "type": "events", "order": 0},
|
||||
@ -129,7 +135,7 @@ class ClickhouseTestFunnelTypes(ClickhouseTestMixin, APIBaseTest):
|
||||
_create_event(event="step three", distinct_id="user_two", team=self.team, timestamp="2021-05-05 00:00:00")
|
||||
|
||||
response = self.client.post(
|
||||
"/api/insight/funnel/",
|
||||
f"/api/projects/{self.team.id}/insights/funnel/",
|
||||
{
|
||||
"events": [
|
||||
{"id": "step one", "type": "events", "order": 0},
|
||||
@ -161,7 +167,7 @@ class ClickhouseTestFunnelTypes(ClickhouseTestMixin, APIBaseTest):
|
||||
_create_event(event="step two", distinct_id="user_two", team=self.team, timestamp="2021-05-04 00:00:00")
|
||||
|
||||
response = self.client.post(
|
||||
"/api/insight/funnel/",
|
||||
f"/api/projects/{self.team.id}/insights/funnel/",
|
||||
{
|
||||
"events": [
|
||||
{"id": "step one", "type": "events", "order": 0},
|
||||
@ -195,7 +201,7 @@ class ClickhouseTestFunnelTypes(ClickhouseTestMixin, APIBaseTest):
|
||||
_create_event(event="step three", distinct_id="user_two", team=self.team, timestamp="2021-05-05 00:00:00")
|
||||
|
||||
response = self.client.post(
|
||||
"/api/insight/funnel/",
|
||||
f"/api/projects/{self.team.id}/insights/funnel/",
|
||||
{
|
||||
"events": [
|
||||
{"id": "step one", "type": "events", "order": 0},
|
||||
@ -235,7 +241,7 @@ class ClickhouseTestFunnelTypes(ClickhouseTestMixin, APIBaseTest):
|
||||
_create_event(event="step three", distinct_id="user_three", team=self.team, timestamp="2021-05-05 00:00:00")
|
||||
|
||||
response = self.client.post(
|
||||
"/api/insight/funnel/",
|
||||
f"/api/projects/{self.team.id}/insights/funnel/",
|
||||
{
|
||||
"events": [
|
||||
{"id": "step one", "type": "events", "order": 0},
|
||||
@ -274,7 +280,7 @@ class ClickhouseTestFunnelTypes(ClickhouseTestMixin, APIBaseTest):
|
||||
# Converted from 0 to 1 in 82_800 s
|
||||
|
||||
response = self.client.post(
|
||||
"/api/insight/funnel/",
|
||||
f"/api/projects/{self.team.id}/insights/funnel/",
|
||||
{
|
||||
"insight": "funnels",
|
||||
"funnel_viz_type": "time_to_convert",
|
||||
@ -324,7 +330,7 @@ class ClickhouseTestFunnelTypes(ClickhouseTestMixin, APIBaseTest):
|
||||
# Converted from 0 to 1 in 82_800 s
|
||||
|
||||
response = self.client.post(
|
||||
"/api/insight/funnel/",
|
||||
f"/api/projects/{self.team.id}/insights/funnel/",
|
||||
{
|
||||
"insight": "funnels",
|
||||
"funnel_viz_type": "time_to_convert",
|
||||
@ -375,7 +381,7 @@ class ClickhouseTestFunnelTypes(ClickhouseTestMixin, APIBaseTest):
|
||||
# Converted from 0 to 1 in 82_800 s
|
||||
|
||||
response = self.client.post(
|
||||
"/api/insight/funnel/",
|
||||
f"/api/projects/{self.team.id}/insights/funnel/",
|
||||
{
|
||||
"insight": "funnels",
|
||||
"funnel_viz_type": "time_to_convert",
|
||||
@ -408,7 +414,10 @@ class ClickhouseTestFunnelTypes(ClickhouseTestMixin, APIBaseTest):
|
||||
)
|
||||
|
||||
def test_funnel_invalid_action_handled(self):
|
||||
response = self.client.post("/api/insight/funnel/", {"actions": [{"id": 666, "type": "actions", "order": 0},]},)
|
||||
response = self.client.post(
|
||||
f"/api/projects/{self.team.id}/insights/funnel/",
|
||||
{"actions": [{"id": 666, "type": "actions", "order": 0},]},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(response.json(), self.validation_error_response("Action ID 666 does not exist!"))
|
||||
@ -424,7 +433,7 @@ class ClickhouseTestFunnelTypes(ClickhouseTestMixin, APIBaseTest):
|
||||
_create_event(team=self.team, event="step two", distinct_id="2")
|
||||
|
||||
response = self.client.post(
|
||||
"/api/insight/funnel/",
|
||||
f"/api/projects/{self.team.id}/insights/funnel/",
|
||||
{
|
||||
"events": [
|
||||
{"id": "step one", "type": "events", "order": 0},
|
||||
@ -461,7 +470,7 @@ class ClickhouseTestFunnelTypes(ClickhouseTestMixin, APIBaseTest):
|
||||
("step three", 0, 1, False),
|
||||
]:
|
||||
response = self.client.post(
|
||||
"/api/insight/funnel/",
|
||||
f"/api/projects/{self.team.id}/insights/funnel/",
|
||||
{
|
||||
"events": [
|
||||
{"id": "step one", "type": "events", "order": 0},
|
||||
|
@ -59,7 +59,7 @@ class TestClickhousePaths(ClickhouseTestMixin, APIBaseTest):
|
||||
properties={"$current_url": "/about"}, distinct_id="person_1", event="$pageview", team=self.team,
|
||||
)
|
||||
|
||||
response = self.client.get("/api/insight/path",).json()
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/insights/path",).json()
|
||||
self.assertEqual(len(response["result"]), 1)
|
||||
|
||||
def test_insight_paths_basic_exclusions(self):
|
||||
@ -74,7 +74,9 @@ class TestClickhousePaths(ClickhouseTestMixin, APIBaseTest):
|
||||
distinct_id="person_1", event="third event", team=self.team,
|
||||
)
|
||||
|
||||
response = self.client.get("/api/insight/path", data={"exclude_events": '["second event"]'}).json()
|
||||
response = self.client.get(
|
||||
f"/api/projects/{self.team.id}/insights/path", data={"exclude_events": '["second event"]'}
|
||||
).json()
|
||||
self.assertEqual(len(response["result"]), 1)
|
||||
|
||||
def test_backwards_compatible_path_types(self):
|
||||
@ -98,12 +100,18 @@ class TestClickhousePaths(ClickhouseTestMixin, APIBaseTest):
|
||||
_create_event(
|
||||
distinct_id="person_1", event="custom2", team=self.team,
|
||||
)
|
||||
response = self.client.get("/api/insight/path", data={"path_type": "$pageview", "insight": "PATHS",}).json()
|
||||
response = self.client.get(
|
||||
f"/api/projects/{self.team.id}/insights/path", data={"path_type": "$pageview", "insight": "PATHS",}
|
||||
).json()
|
||||
self.assertEqual(len(response["result"]), 2)
|
||||
|
||||
response = self.client.get("/api/insight/path", data={"path_type": "custom_event", "insight": "PATHS"}).json()
|
||||
response = self.client.get(
|
||||
f"/api/projects/{self.team.id}/insights/path", data={"path_type": "custom_event", "insight": "PATHS"}
|
||||
).json()
|
||||
self.assertEqual(len(response["result"]), 1)
|
||||
response = self.client.get("/api/insight/path", data={"path_type": "$screen", "insight": "PATHS"}).json()
|
||||
response = self.client.get(
|
||||
f"/api/projects/{self.team.id}/insights/path", data={"path_type": "$screen", "insight": "PATHS"}
|
||||
).json()
|
||||
self.assertEqual(len(response["result"]), 0)
|
||||
|
||||
def test_backwards_compatible_start_point(self):
|
||||
@ -131,16 +139,19 @@ class TestClickhousePaths(ClickhouseTestMixin, APIBaseTest):
|
||||
distinct_id="person_1", event="custom2", team=self.team,
|
||||
)
|
||||
response = self.client.get(
|
||||
"/api/insight/path", data={"path_type": "$pageview", "insight": "PATHS", "start_point": "/about",}
|
||||
f"/api/projects/{self.team.id}/insights/path",
|
||||
data={"path_type": "$pageview", "insight": "PATHS", "start_point": "/about",},
|
||||
).json()
|
||||
self.assertEqual(len(response["result"]), 1)
|
||||
|
||||
response = self.client.get(
|
||||
"/api/insight/path", data={"path_type": "custom_event", "insight": "PATHS", "start_point": "custom2",}
|
||||
f"/api/projects/{self.team.id}/insights/path",
|
||||
data={"path_type": "custom_event", "insight": "PATHS", "start_point": "custom2",},
|
||||
).json()
|
||||
self.assertEqual(len(response["result"]), 0)
|
||||
response = self.client.get(
|
||||
"/api/insight/path", data={"path_type": "$screen", "insight": "PATHS", "start_point": "/screen1",}
|
||||
f"/api/projects/{self.team.id}/insights/path",
|
||||
data={"path_type": "$screen", "insight": "PATHS", "start_point": "/screen1",},
|
||||
).json()
|
||||
self.assertEqual(len(response["result"]), 1)
|
||||
|
||||
@ -180,12 +191,14 @@ class TestClickhousePaths(ClickhouseTestMixin, APIBaseTest):
|
||||
)
|
||||
|
||||
response = self.client.get(
|
||||
"/api/insight/path", data={"insight": "PATHS", "path_groupings": json.dumps(["/about*"])}
|
||||
f"/api/projects/{self.team.id}/insights/path",
|
||||
data={"insight": "PATHS", "path_groupings": json.dumps(["/about*"])},
|
||||
).json()
|
||||
self.assertEqual(len(response["result"]), 2)
|
||||
|
||||
response = self.client.get(
|
||||
"/api/insight/path", data={"insight": "PATHS", "path_groupings": json.dumps(["/about_*"])}
|
||||
f"/api/projects/{self.team.id}/insights/path",
|
||||
data={"insight": "PATHS", "path_groupings": json.dumps(["/about_*"])},
|
||||
).json()
|
||||
self.assertEqual(len(response["result"]), 3)
|
||||
|
||||
@ -214,7 +227,9 @@ class TestClickhousePaths(ClickhouseTestMixin, APIBaseTest):
|
||||
],
|
||||
}
|
||||
|
||||
post_response = self.client.post("/api/insight/path/", data={**request_data, "funnel_filter": funnel_filter})
|
||||
post_response = self.client.post(
|
||||
f"/api/projects/{self.team.id}/insights/path/", data={**request_data, "funnel_filter": funnel_filter}
|
||||
)
|
||||
self.assertEqual(post_response.status_code, status.HTTP_200_OK)
|
||||
post_j = post_response.json()
|
||||
self.assertEqual(
|
||||
|
@ -1,6 +1,6 @@
|
||||
import apiNoMock from 'lib/api'
|
||||
import { combineUrl } from 'kea-router'
|
||||
import { AvailableFeature, TeamType } from '~/types'
|
||||
import { AvailableFeature, OrganizationType, TeamType } from '~/types'
|
||||
|
||||
type APIMockReturnType = {
|
||||
[K in keyof typeof apiNoMock]: jest.Mock<ReturnType<typeof apiNoMock[K]>, Parameters<typeof apiNoMock[K]>>
|
||||
@ -22,6 +22,7 @@ interface APIMockOptions {
|
||||
}
|
||||
|
||||
export const MOCK_TEAM_ID: TeamType['id'] = 997
|
||||
export const MOCK_ORGANIZATION_ID: OrganizationType['id'] = 'ABCD'
|
||||
|
||||
export const api = apiNoMock as any as APIMockReturnType
|
||||
|
||||
@ -55,14 +56,14 @@ export function defaultAPIMocks(
|
||||
}
|
||||
} else if (pathname === 'api/organizations/@current') {
|
||||
return {
|
||||
id: 'ABCD', // Should be a UUID but that doesn't matter here
|
||||
id: MOCK_ORGANIZATION_ID,
|
||||
}
|
||||
} else if (
|
||||
[
|
||||
`api/projects/${MOCK_TEAM_ID}/actions/`,
|
||||
`api/projects/${MOCK_TEAM_ID}/event_definitions/`,
|
||||
'api/dashboard',
|
||||
'api/organizations/@current',
|
||||
'api/projects/@current/event_definitions/',
|
||||
].includes(pathname)
|
||||
) {
|
||||
return { results: [] }
|
||||
|
@ -11,7 +11,7 @@ const defaultValue = 'https://'
|
||||
|
||||
export const appUrlsLogic = kea<appUrlsLogicType>({
|
||||
connect: {
|
||||
values: [teamLogic, ['currentTeam']],
|
||||
values: [teamLogic, ['currentTeam', 'currentTeamId']],
|
||||
},
|
||||
actions: () => ({
|
||||
setAppUrls: (appUrls: string[]) => ({ appUrls }),
|
||||
@ -30,7 +30,9 @@ export const appUrlsLogic = kea<appUrlsLogicType>({
|
||||
breakdown: '$current_url',
|
||||
date_from: dayjs().subtract(3, 'days').toISOString(),
|
||||
}
|
||||
const result = (await api.get('api/insight/trend/?' + toParams(params))).result as TrendResult[]
|
||||
const result = (
|
||||
await api.get(`api/projects/${values.currentTeamId}/insights/trend/?${toParams(params)}`)
|
||||
).result as TrendResult[]
|
||||
if (result && result[0]?.count === 0) {
|
||||
return []
|
||||
}
|
||||
|
@ -50,7 +50,12 @@ export function SaveToDashboardModal({
|
||||
async function save(event: MouseEvent | FormEvent): Promise<void> {
|
||||
event.preventDefault()
|
||||
if (newItem) {
|
||||
const response = await api.create('api/insight', { filters, name, saved: true, dashboard: dashboardId })
|
||||
const response = await api.create(`api/projects/${currentTeamId}/insights`, {
|
||||
filters,
|
||||
name,
|
||||
saved: true,
|
||||
dashboard: dashboardId,
|
||||
})
|
||||
if (annotations) {
|
||||
for (const { content, date_marker, created_at, scope } of annotations) {
|
||||
await api.create(`api/projects/${currentTeamId}/annotations`, {
|
||||
@ -63,7 +68,7 @@ export function SaveToDashboardModal({
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await api.update(`api/insight/${fromItem}`, { filters })
|
||||
await api.update(`api/projects/${currentTeamId}/insights/${fromItem}`, { filters })
|
||||
}
|
||||
reportSavedInsightToDashboard()
|
||||
toast(
|
||||
|
@ -130,7 +130,7 @@ export const taxonomicFilterLogic = kea<taxonomicFilterLogicType>({
|
||||
{
|
||||
name: 'Pageview URLs',
|
||||
type: TaxonomicFilterGroupType.PageviewUrls,
|
||||
endpoint: 'api/event/values/?key=$current_url',
|
||||
endpoint: `api/projects/${teamId}/events/values/?key=$current_url`,
|
||||
searchAlias: 'value',
|
||||
getName: (option: SimpleOption): string => option.name,
|
||||
getValue: (option: SimpleOption): TaxonomicFilterValue => option.name,
|
||||
@ -138,7 +138,7 @@ export const taxonomicFilterLogic = kea<taxonomicFilterLogicType>({
|
||||
{
|
||||
name: 'Screens',
|
||||
type: TaxonomicFilterGroupType.Screens,
|
||||
endpoint: 'api/event/values/?key=$screen_name',
|
||||
endpoint: `api/projects/${teamId}/events/values/?key=$screen_name`,
|
||||
searchAlias: 'value',
|
||||
getName: (option: SimpleOption): string => option.name,
|
||||
getValue: (option: SimpleOption): TaxonomicFilterValue => option.name,
|
||||
|
@ -8,6 +8,7 @@ import { dashboardsModel } from './dashboardsModel'
|
||||
import { Link } from 'lib/components/Link'
|
||||
import { dashboardItemsModelType } from './dashboardItemsModelType'
|
||||
import { urls } from 'scenes/urls'
|
||||
import { teamLogic } from '../scenes/teamLogic'
|
||||
|
||||
export const dashboardItemsModel = kea<dashboardItemsModelType>({
|
||||
actions: () => ({
|
||||
@ -28,7 +29,9 @@ export const dashboardItemsModel = kea<dashboardItemsModelType>({
|
||||
value: item.name,
|
||||
error: 'You must enter name',
|
||||
success: async (name: string) => {
|
||||
item = await api.update(`api/insight/${item.id}`, { name })
|
||||
item = await api.update(`api/projects/${teamLogic.values.currentTeamId}/insights/${item.id}`, {
|
||||
name,
|
||||
})
|
||||
toast('Successfully renamed item')
|
||||
actions.renameDashboardItemSuccess(item)
|
||||
},
|
||||
@ -46,14 +49,17 @@ export const dashboardItemsModel = kea<dashboardItemsModelType>({
|
||||
|
||||
const { id: _discard, ...rest } = item // eslint-disable-line
|
||||
const newItem = dashboardId ? { ...rest, dashboard: dashboardId, layouts } : { ...rest, layouts }
|
||||
const addedItem = await api.create('api/insight', newItem)
|
||||
const addedItem = await api.create(`api/projects/${teamLogic.values.currentTeamId}/insights`, newItem)
|
||||
|
||||
const dashboard = dashboardId ? dashboardsModel.values.rawDashboards[dashboardId] : null
|
||||
|
||||
if (move && dashboard) {
|
||||
const deletedItem = await api.update(`api/insight/${item.id}`, {
|
||||
deleted: true,
|
||||
})
|
||||
const deletedItem = await api.update(
|
||||
`api/projects/${teamLogic.values.currentTeamId}/insights/${item.id}`,
|
||||
{
|
||||
deleted: true,
|
||||
}
|
||||
)
|
||||
dashboardsModel.actions.updateDashboardItem(deletedItem)
|
||||
|
||||
const toastId = toast(
|
||||
@ -68,10 +74,15 @@ export const dashboardItemsModel = kea<dashboardItemsModelType>({
|
||||
onClick={async () => {
|
||||
toast.dismiss(toastId)
|
||||
const [restoredItem, removedItem] = await Promise.all([
|
||||
api.update(`api/insight/${item.id}`, { deleted: false }),
|
||||
api.update(`api/insight/${addedItem.id}`, {
|
||||
deleted: true,
|
||||
api.update(`api/projects/${teamLogic.values.currentTeamId}/insights/${item.id}`, {
|
||||
deleted: false,
|
||||
}),
|
||||
api.update(
|
||||
`api/projects/${teamLogic.values.currentTeamId}/insights/${addedItem.id}`,
|
||||
{
|
||||
deleted: true,
|
||||
}
|
||||
),
|
||||
])
|
||||
toast(<div>Panel move reverted!</div>)
|
||||
dashboardsModel.actions.updateDashboardItem(restoredItem)
|
||||
|
@ -4,6 +4,7 @@ import { toParams } from 'lib/utils'
|
||||
import { SavedFunnel, ViewType } from '~/types'
|
||||
import { insightHistoryLogic } from 'scenes/insights/InsightHistoryPanel/insightHistoryLogic'
|
||||
import { funnelsModelType } from './funnelsModelType'
|
||||
import { teamLogic } from '../scenes/teamLogic'
|
||||
|
||||
const parseSavedFunnel = (result: Record<string, any>): SavedFunnel => {
|
||||
return {
|
||||
@ -23,20 +24,19 @@ export const funnelsModel = kea<funnelsModelType>({
|
||||
__default: [] as SavedFunnel[],
|
||||
loadFunnels: async () => {
|
||||
const response = await api.get(
|
||||
'api/insight/?' +
|
||||
toParams({
|
||||
order: '-created_at',
|
||||
saved: true,
|
||||
limit: 5,
|
||||
insight: ViewType.FUNNELS,
|
||||
})
|
||||
`api/projects/${teamLogic.values.currentTeamId}/insights/?${toParams({
|
||||
order: '-created_at',
|
||||
saved: true,
|
||||
limit: 5,
|
||||
insight: ViewType.FUNNELS,
|
||||
})}`
|
||||
)
|
||||
const results = response.results.map((result: Record<string, any>) => parseSavedFunnel(result))
|
||||
actions.setNext(response.next)
|
||||
return results
|
||||
},
|
||||
deleteFunnel: async (funnelId: number) => {
|
||||
await api.delete(`api/insight/${funnelId}`)
|
||||
await api.delete(`api/projects/${teamLogic.values.currentTeamId}/insights/${funnelId}`)
|
||||
return values.funnels.filter((funnel) => funnel.id !== funnelId)
|
||||
},
|
||||
},
|
||||
|
@ -45,6 +45,7 @@ import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
|
||||
import { FEATURE_FLAGS } from 'lib/constants'
|
||||
import { LinkButton } from 'lib/components/LinkButton'
|
||||
import { DiveIcon } from 'lib/components/icons'
|
||||
import { teamLogic } from '../teamLogic'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
@ -201,6 +202,7 @@ export function DashboardItem({
|
||||
}: Props): JSX.Element {
|
||||
const [initialLoaded, setInitialLoaded] = useState(false)
|
||||
const [showSaveModal, setShowSaveModal] = useState(false)
|
||||
const { currentTeamId } = useValues(teamLogic)
|
||||
const { nameSortedDashboards } = useValues(dashboardsModel)
|
||||
const { renameDashboardItem } = useActions(dashboardItemsModel)
|
||||
const { featureFlags } = useValues(featureFlagLogic)
|
||||
@ -588,7 +590,7 @@ export function DashboardItem({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
},
|
||||
endpoint: 'insight',
|
||||
endpoint: `projects/${currentTeamId}/insights`,
|
||||
callback: loadDashboardItems,
|
||||
})
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import { dashboardLogicType } from './dashboardLogicType'
|
||||
import React from 'react'
|
||||
import { Layout, Layouts } from 'react-grid-layout'
|
||||
import { insightLogic } from 'scenes/insights/insightLogic'
|
||||
import { teamLogic } from '../teamLogic'
|
||||
|
||||
export interface DashboardLogicProps {
|
||||
id?: number
|
||||
@ -23,7 +24,10 @@ export interface DashboardLogicProps {
|
||||
export const AUTO_REFRESH_INITIAL_INTERVAL_SECONDS = 300
|
||||
|
||||
export const dashboardLogic = kea<dashboardLogicType<DashboardLogicProps>>({
|
||||
connect: [dashboardsModel, dashboardItemsModel, eventUsageLogic],
|
||||
connect: {
|
||||
values: [teamLogic, ['currentTeamId']],
|
||||
logic: [dashboardsModel, dashboardItemsModel, eventUsageLogic],
|
||||
},
|
||||
|
||||
props: {} as DashboardLogicProps,
|
||||
|
||||
@ -490,10 +494,10 @@ export const dashboardLogic = kea<dashboardLogicType<DashboardLogicProps>>({
|
||||
})
|
||||
},
|
||||
updateItemColor: ({ id, color }) => {
|
||||
api.update(`api/insight/${id}`, { color })
|
||||
api.update(`api/projects/${values.currentTeamId}/insights/${id}`, { color })
|
||||
},
|
||||
setDiveDashboard: ({ id, dive_dashboard }) => {
|
||||
api.update(`api/insight/${id}`, { dive_dashboard })
|
||||
api.update(`api/projects/${values.currentTeamId}/insights/${id}`, { dive_dashboard })
|
||||
},
|
||||
refreshAllDashboardItemsManual: () => {
|
||||
// reset auto refresh interval
|
||||
|
@ -12152,7 +12152,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"hasNext": "http://localhost:8000/api/event/?properties=%7B%7D&orderBy=%5B%22-timestamp%22%5D&after=2021-08-24T10:14:50.240000Z",
|
||||
"hasNext": "http://localhost:8000/api/projects/1/events/?properties=%7B%7D&orderBy=%5B%22-timestamp%22%5D&after=2021-08-24T10:14:50.240000Z",
|
||||
"orderBy": "-timestamp",
|
||||
"selectedEvent": null,
|
||||
"newEvents": [
|
||||
|
@ -7,6 +7,7 @@ import { errorToast, toParams, uniqueBy } from 'lib/utils'
|
||||
import { eventDefinitionsModel } from '~/models/eventDefinitionsModel'
|
||||
import { keyMapping } from 'lib/components/PropertyKeyInfo'
|
||||
import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel'
|
||||
import { teamLogic } from '../../teamLogic'
|
||||
|
||||
export const definitionDrawerLogic = kea<definitionDrawerLogicType>({
|
||||
actions: () => ({
|
||||
@ -71,7 +72,9 @@ export const definitionDrawerLogic = kea<definitionDrawerLogicType>({
|
||||
orderBy: ['-timestamp'],
|
||||
limit: 5,
|
||||
})
|
||||
const events = await api.get(`api/event/?${eventsParams}`)
|
||||
const events = await api.get(
|
||||
`api/projects/${teamLogic.values.currentTeamId}/events/?${eventsParams}`
|
||||
)
|
||||
if (values.type === 'property') {
|
||||
actions.loadEventsSnippetSuccess(events.results)
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { BuiltLogic } from 'kea'
|
||||
import { eventsTableLogicType } from 'scenes/events/eventsTableLogicType'
|
||||
import { ApiError, eventsTableLogic, EventsTableLogicProps, OnFetchEventsSuccess } from 'scenes/events/eventsTableLogic'
|
||||
import { mockAPI } from 'lib/api.mock'
|
||||
import { mockAPI, MOCK_TEAM_ID } from 'lib/api.mock'
|
||||
import { expectLogic } from 'kea-test-utils'
|
||||
import { initKeaTestLogic } from '~/test/init'
|
||||
import { router } from 'kea-router'
|
||||
@ -52,7 +52,7 @@ describe('eventsTableLogic', () => {
|
||||
})
|
||||
|
||||
it('sets a key', () => {
|
||||
expect(logic.key).toEqual('all-events-test-key')
|
||||
expect(logic.key).toEqual('all-test-key')
|
||||
})
|
||||
|
||||
it('starts with known defaults', async () => {
|
||||
@ -362,7 +362,7 @@ describe('eventsTableLogic', () => {
|
||||
|
||||
it('can build the export URL when there are no properties or filters', async () => {
|
||||
await expectLogic(logic, () => {}).toMatchValues({
|
||||
exportUrl: '/api/event.csv?properties=%5B%5D&orderBy=%5B%22-timestamp%22%5D',
|
||||
exportUrl: `/api/projects/${MOCK_TEAM_ID}/events.csv?properties=%5B%5D&orderBy=%5B%22-timestamp%22%5D`,
|
||||
})
|
||||
})
|
||||
|
||||
@ -370,8 +370,7 @@ describe('eventsTableLogic', () => {
|
||||
await expectLogic(logic, () => {
|
||||
logic.actions.setProperties([makePropertyFilter('fixed value')])
|
||||
}).toMatchValues({
|
||||
exportUrl:
|
||||
'/api/event.csv?properties=%5B%7B%22key%22%3A%22fixed%20value%22%2C%22operator%22%3Anull%2C%22type%22%3A%22t%22%2C%22value%22%3A%22v%22%7D%5D&orderBy=%5B%22-timestamp%22%5D',
|
||||
exportUrl: `/api/projects/${MOCK_TEAM_ID}/events.csv?properties=%5B%7B%22key%22%3A%22fixed%20value%22%2C%22operator%22%3Anull%2C%22type%22%3A%22t%22%2C%22value%22%3A%22v%22%7D%5D&orderBy=%5B%22-timestamp%22%5D`,
|
||||
})
|
||||
})
|
||||
|
||||
@ -379,7 +378,7 @@ describe('eventsTableLogic', () => {
|
||||
await expectLogic(logic, () => {
|
||||
logic.actions.flipSort()
|
||||
}).toMatchValues({
|
||||
exportUrl: '/api/event.csv?properties=%5B%5D&orderBy=%5B%22timestamp%22%5D',
|
||||
exportUrl: `/api/projects/${MOCK_TEAM_ID}/events.csv?properties=%5B%5D&orderBy=%5B%22timestamp%22%5D`,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -3,11 +3,12 @@ import { errorToast, toParams } from 'lib/utils'
|
||||
import { router } from 'kea-router'
|
||||
import api from 'lib/api'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
import { eventsTableLogicType } from './eventsTableLogicType'
|
||||
import { FixedFilters } from 'scenes/events/EventsTable'
|
||||
import { AnyPropertyFilter, EventsTableRowItem, EventType, PropertyFilter } from '~/types'
|
||||
import { isValidPropertyFilter } from 'lib/components/PropertyFilters/utils'
|
||||
import { teamLogic } from '../teamLogic'
|
||||
|
||||
const POLL_TIMEOUT = 5000
|
||||
|
||||
const formatEvents = (events: EventType[], newEvents: EventType[]): EventsTableRowItem[] => {
|
||||
@ -35,7 +36,6 @@ const formatEvents = (events: EventType[], newEvents: EventType[]): EventsTableR
|
||||
|
||||
export interface EventsTableLogicProps {
|
||||
fixedFilters?: FixedFilters
|
||||
apiUrl?: string // = 'api/event/'
|
||||
key?: string
|
||||
}
|
||||
|
||||
@ -57,10 +57,12 @@ export const eventsTableLogic = kea<eventsTableLogicType<ApiError, EventsTableLo
|
||||
// Set a unique key based on the fixed filters.
|
||||
// This way if we move back/forward between /events and /person/ID, the logic is reloaded.
|
||||
key: (props) =>
|
||||
[props.fixedFilters ? JSON.stringify(props.fixedFilters) : 'all', props.apiUrl || 'events', props.key]
|
||||
[props.fixedFilters ? JSON.stringify(props.fixedFilters) : 'all', props.key]
|
||||
.filter((keyPart) => !!keyPart)
|
||||
.join('-'),
|
||||
|
||||
connect: {
|
||||
values: [teamLogic, ['currentTeamId']],
|
||||
},
|
||||
actions: {
|
||||
setProperties: (properties: AnyPropertyFilter[] | AnyPropertyFilter): { properties: AnyPropertyFilter[] } => {
|
||||
// there seem to be multiple representations of "empty" properties
|
||||
@ -187,9 +189,9 @@ export const eventsTableLogic = kea<eventsTableLogicType<ApiError, EventsTableLo
|
||||
(events, newEvents) => formatEvents(events, newEvents),
|
||||
],
|
||||
exportUrl: [
|
||||
() => [selectors.eventFilter, selectors.orderBy, selectors.properties],
|
||||
(eventFilter, orderBy, properties) =>
|
||||
`/api/event.csv?${toParams({
|
||||
() => [selectors.currentTeamId, selectors.eventFilter, selectors.orderBy, selectors.properties],
|
||||
(teamId, eventFilter, orderBy, properties) =>
|
||||
`/api/projects/${teamId}/events.csv?${toParams({
|
||||
properties,
|
||||
...(props.fixedFilters || {}),
|
||||
...(eventFilter ? { event: eventFilter } : {}),
|
||||
@ -306,7 +308,7 @@ export const eventsTableLogic = kea<eventsTableLogicType<ApiError, EventsTableLo
|
||||
let apiResponse = null
|
||||
|
||||
try {
|
||||
apiResponse = await api.get(`${props.apiUrl || 'api/event/'}?${urlParams}`)
|
||||
apiResponse = await api.get(`api/projects/${values.currentTeamId}/events/?${urlParams}`)
|
||||
} catch (error) {
|
||||
actions.fetchOrPollFailure(error)
|
||||
return
|
||||
@ -347,7 +349,7 @@ export const eventsTableLogic = kea<eventsTableLogicType<ApiError, EventsTableLo
|
||||
|
||||
let apiResponse = null
|
||||
try {
|
||||
apiResponse = await api.get(`${props.apiUrl || 'api/event/'}?${urlParams}`)
|
||||
apiResponse = await api.get(`api/projects/${values.currentTeamId}/events/?${urlParams}`)
|
||||
} catch (e) {
|
||||
// We don't call fetchOrPollFailure because we don't to generate an error alert for this
|
||||
return
|
||||
|
@ -16,8 +16,10 @@ import { normalizeColumnTitle, useIsTableScrolling } from 'lib/components/Table/
|
||||
import { urls } from 'scenes/urls'
|
||||
import { Tooltip } from 'lib/components/Tooltip'
|
||||
import stringWithWBR from 'lib/utils/stringWithWBR'
|
||||
import { teamLogic } from '../teamLogic'
|
||||
|
||||
export function FeatureFlags(): JSX.Element {
|
||||
const { currentTeamId } = useValues(teamLogic)
|
||||
const { featureFlags, featureFlagsLoading } = useValues(featureFlagsLogic)
|
||||
const { updateFeatureFlag, loadFeatureFlags } = useActions(featureFlagsLogic)
|
||||
const { push } = useActions(router)
|
||||
@ -145,7 +147,7 @@ export function FeatureFlags(): JSX.Element {
|
||||
</Link>
|
||||
{featureFlag.id && (
|
||||
<DeleteWithUndo
|
||||
endpoint="feature_flag"
|
||||
endpoint={`projects/${currentTeamId}/feature_flags`}
|
||||
object={{ name: featureFlag.name, id: featureFlag.id }}
|
||||
className="text-danger"
|
||||
style={{ marginLeft: 8 }}
|
||||
|
@ -7,6 +7,7 @@ import { toast } from 'react-toastify'
|
||||
import { router } from 'kea-router'
|
||||
import { deleteWithUndo } from 'lib/utils'
|
||||
import { urls } from 'scenes/urls'
|
||||
import { teamLogic } from '../teamLogic'
|
||||
const NEW_FLAG = {
|
||||
id: null,
|
||||
key: '',
|
||||
@ -34,6 +35,9 @@ const EMPTY_MULTIVARIATE_OPTIONS: MultivariateFlagOptions = {
|
||||
}
|
||||
|
||||
export const featureFlagLogic = kea<featureFlagLogicType>({
|
||||
connect: {
|
||||
values: [teamLogic, ['currentTeamId']],
|
||||
},
|
||||
actions: {
|
||||
setFeatureFlagId: (id: number | 'new') => ({ id }),
|
||||
addMatchGroup: true,
|
||||
@ -197,18 +201,18 @@ export const featureFlagLogic = kea<featureFlagLogicType>({
|
||||
featureFlag: {
|
||||
loadFeatureFlag: async () => {
|
||||
if (values.featureFlagId && values.featureFlagId !== 'new') {
|
||||
return await api.get(`api/feature_flag/${values.featureFlagId}`)
|
||||
return await api.get(`api/projects/${values.currentTeamId}/feature_flags/${values.featureFlagId}`)
|
||||
}
|
||||
return NEW_FLAG
|
||||
},
|
||||
saveFeatureFlag: async (updatedFlag: Partial<FeatureFlagType>) => {
|
||||
if (!updatedFlag.id) {
|
||||
return await api.create('api/feature_flag', {
|
||||
return await api.create(`api/projects/${values.currentTeamId}/feature_flags`, {
|
||||
...updatedFlag,
|
||||
id: undefined,
|
||||
})
|
||||
} else {
|
||||
return await api.update(`api/feature_flag/${updatedFlag.id}`, {
|
||||
return await api.update(`api/projects/${values.currentTeamId}/feature_flags/${updatedFlag.id}`, {
|
||||
...updatedFlag,
|
||||
id: undefined,
|
||||
})
|
||||
@ -216,7 +220,7 @@ export const featureFlagLogic = kea<featureFlagLogicType>({
|
||||
},
|
||||
},
|
||||
}),
|
||||
listeners: ({ actions }) => ({
|
||||
listeners: ({ actions, values }) => ({
|
||||
saveFeatureFlagSuccess: () => {
|
||||
toast.success(
|
||||
<div>
|
||||
@ -233,7 +237,7 @@ export const featureFlagLogic = kea<featureFlagLogicType>({
|
||||
},
|
||||
deleteFeatureFlag: async ({ featureFlag }) => {
|
||||
deleteWithUndo({
|
||||
endpoint: 'feature_flag',
|
||||
endpoint: `projects/${values.currentTeamId}/feature_flags`,
|
||||
object: { name: featureFlag.name, id: featureFlag.id },
|
||||
callback: () => {
|
||||
router.actions.push(urls.featureFlags())
|
||||
|
@ -2,17 +2,21 @@ import { kea } from 'kea'
|
||||
import api from 'lib/api'
|
||||
import { featureFlagsLogicType } from './featureFlagsLogicType'
|
||||
import { FeatureFlagType } from '~/types'
|
||||
import { teamLogic } from '../teamLogic'
|
||||
|
||||
export const featureFlagsLogic = kea<featureFlagsLogicType>({
|
||||
connect: {
|
||||
values: [teamLogic, ['currentTeamId']],
|
||||
},
|
||||
loaders: ({ values }) => ({
|
||||
featureFlags: {
|
||||
__default: [] as FeatureFlagType[],
|
||||
loadFeatureFlags: async () => {
|
||||
const response = await api.get('api/feature_flag/')
|
||||
const response = await api.get(`api/projects/${values.currentTeamId}/feature_flags/`)
|
||||
return response.results as FeatureFlagType[]
|
||||
},
|
||||
updateFeatureFlag: async ({ id, payload }: { id: number; payload: Partial<FeatureFlagType> }) => {
|
||||
const response = await api.update(`api/feature_flag/${id}`, payload)
|
||||
const response = await api.update(`api/projects/${values.currentTeamId}/feature_flags/${id}`, payload)
|
||||
return [...values.featureFlags].map((flag) => (flag.id === response.id ? response : flag))
|
||||
},
|
||||
},
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { funnelLogic } from './funnelLogic'
|
||||
import { api, defaultAPIMocks, mockAPI } from 'lib/api.mock'
|
||||
import { api, defaultAPIMocks, mockAPI, MOCK_TEAM_ID } from 'lib/api.mock'
|
||||
import { expectLogic } from 'kea-test-utils'
|
||||
import { initKeaTestLogic } from '~/test/init'
|
||||
import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
|
||||
@ -15,14 +15,14 @@ describe('funnelLogic', () => {
|
||||
let logic: ReturnType<typeof funnelLogic.build>
|
||||
|
||||
mockAPI(async (url) => {
|
||||
if (url.pathname === 'api/insight/funnel/') {
|
||||
if (url.pathname === `api/projects/${MOCK_TEAM_ID}/insights/funnel/`) {
|
||||
return {
|
||||
is_cached: true,
|
||||
last_refresh: '2021-09-16T13:41:41.297295Z',
|
||||
result: ['result from api'],
|
||||
type: 'Funnel',
|
||||
}
|
||||
} else if (url.pathname.startsWith('api/insight')) {
|
||||
} else if (url.pathname.startsWith(`api/projects/${MOCK_TEAM_ID}/insights`)) {
|
||||
return { results: [], next: null }
|
||||
}
|
||||
return defaultAPIMocks(url)
|
||||
@ -166,7 +166,7 @@ describe('funnelLogic', () => {
|
||||
})
|
||||
|
||||
expect(api.create).toBeCalledWith(
|
||||
'api/insight/funnel/?',
|
||||
`api/projects/${MOCK_TEAM_ID}/insights/funnel/`,
|
||||
expect.objectContaining({
|
||||
actions: [],
|
||||
events: [
|
||||
|
@ -49,6 +49,7 @@ import { dashboardsModel } from '~/models/dashboardsModel'
|
||||
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
|
||||
import { cleanFilters } from 'scenes/insights/utils/cleanFilters'
|
||||
import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils'
|
||||
import { teamLogic } from '../teamLogic'
|
||||
|
||||
const DEVIATION_SIGNIFICANCE_MULTIPLIER = 1.5
|
||||
// Chosen via heuristics by eyeballing some values
|
||||
@ -60,7 +61,7 @@ export const funnelLogic = kea<funnelLogicType>({
|
||||
key: keyForInsightLogicProps('insight_funnel'),
|
||||
|
||||
connect: (props: InsightLogicProps) => ({
|
||||
values: [insightLogic(props), ['filters', 'insight', 'insightLoading']],
|
||||
values: [insightLogic(props), ['filters', 'insight', 'insightLoading'], teamLogic, ['currentTeamId']],
|
||||
actions: [insightLogic(props), ['loadResults', 'loadResultsSuccess'], funnelsModel, ['loadFunnels']],
|
||||
logic: [eventUsageLogic, dashboardsModel],
|
||||
}),
|
||||
@ -129,7 +130,7 @@ export const funnelLogic = kea<funnelLogicType>({
|
||||
{
|
||||
loadCorrelations: async () => {
|
||||
return (
|
||||
await api.create('api/insight/funnel/correlation', {
|
||||
await api.create(`api/projects/${values.currentTeamId}/insights/funnel/correlation`, {
|
||||
...values.apiParams,
|
||||
funnel_correlation_type: 'events',
|
||||
})
|
||||
@ -144,7 +145,7 @@ export const funnelLogic = kea<funnelLogicType>({
|
||||
{
|
||||
loadPropertyCorrelations: async (propertyNames: string[]) => {
|
||||
return (
|
||||
await api.create('api/insight/funnel/correlation', {
|
||||
await api.create(`api/projects/${values.currentTeamId}/insights/funnel/correlation`, {
|
||||
...values.apiParams,
|
||||
funnel_correlation_type: 'properties',
|
||||
// Name is comma separated list of property names
|
||||
@ -161,7 +162,7 @@ export const funnelLogic = kea<funnelLogicType>({
|
||||
{
|
||||
loadEventWithPropertyCorrelations: async (eventName: string) => {
|
||||
const results = (
|
||||
await api.create('api/insight/funnel/correlation', {
|
||||
await api.create(`api/projects/${values.currentTeamId}/insights/funnel/correlation`, {
|
||||
...values.apiParams,
|
||||
funnel_correlation_type: 'event_with_properties',
|
||||
funnel_correlation_event_names: [eventName],
|
||||
@ -854,7 +855,7 @@ export const funnelLogic = kea<funnelLogicType>({
|
||||
actions.setFilters({ new_entity: values.filters.new_entity }, false, true)
|
||||
},
|
||||
saveFunnelInsight: async ({ name }) => {
|
||||
await api.create('api/insight', {
|
||||
await api.create(`api/projects/${values.currentTeamId}/insights`, {
|
||||
filters: values.filters,
|
||||
name,
|
||||
saved: true,
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
FunnelsTimeConversionBins,
|
||||
FunnelAPIResponse,
|
||||
FunnelStepReference,
|
||||
TeamType,
|
||||
} from '~/types'
|
||||
|
||||
const PERCENTAGE_DISPLAY_PRECISION = 1 // Number of decimals to show in percentages
|
||||
@ -226,15 +227,19 @@ export function getVisibilityIndex(step: FunnelStep, key?: number | string): str
|
||||
export const SECONDS_TO_POLL = 3 * 60
|
||||
|
||||
export async function pollFunnel<T = FunnelStep[] | FunnelsTimeConversionBins>(
|
||||
teamId: TeamType['id'],
|
||||
apiParams: FunnelRequestParams
|
||||
): Promise<FunnelResult<T>> {
|
||||
// Tricky: This API endpoint has wildly different return types depending on parameters.
|
||||
const { refresh, ...bodyParams } = apiParams
|
||||
let result = await api.create('api/insight/funnel/?' + (refresh ? 'refresh=true' : ''), bodyParams)
|
||||
let result = await api.create(
|
||||
`api/projects/${teamId}/insights/funnel/${refresh ? '?refresh=true' : ''}`,
|
||||
bodyParams
|
||||
)
|
||||
const start = window.performance.now()
|
||||
while (result.result?.loading && (window.performance.now() - start) / 1000 < SECONDS_TO_POLL) {
|
||||
await wait()
|
||||
result = await api.create('api/insight/funnel', bodyParams)
|
||||
result = await api.create(`api/projects/${teamId}/insights/funnel`, bodyParams)
|
||||
}
|
||||
// if endpoint is still loading after 3 minutes just return default
|
||||
if (result.loading) {
|
||||
|
@ -6,6 +6,7 @@ import { toast } from 'react-toastify'
|
||||
import { DashboardItemType } from '~/types'
|
||||
import { insightHistoryLogicType } from './insightHistoryLogicType'
|
||||
import { dashboardItemsModel } from '~/models/dashboardItemsModel'
|
||||
import { teamLogic } from '../../teamLogic'
|
||||
|
||||
const updateInsightState = (
|
||||
state: DashboardItemType[],
|
||||
@ -39,17 +40,19 @@ const updateInsightState = (
|
||||
|
||||
/* insightHistoryLogic - Handles all logic for saved insights and recent history */
|
||||
export const insightHistoryLogic = kea<insightHistoryLogicType>({
|
||||
connect: {
|
||||
values: [teamLogic, ['currentTeamId']],
|
||||
},
|
||||
loaders: ({ actions }) => ({
|
||||
insights: {
|
||||
__default: [] as DashboardItemType[],
|
||||
loadInsights: async () => {
|
||||
const response = await api.get(
|
||||
'api/insight/?' +
|
||||
toParams({
|
||||
order: '-created_at',
|
||||
limit: 25,
|
||||
user: true,
|
||||
})
|
||||
`api/projects/${teamLogic.values.currentTeamId}/insights/?${toParams({
|
||||
order: '-created_at',
|
||||
limit: 25,
|
||||
user: true,
|
||||
})}`
|
||||
)
|
||||
actions.setInsightsNext(response.next)
|
||||
return response.results
|
||||
@ -59,13 +62,12 @@ export const insightHistoryLogic = kea<insightHistoryLogicType>({
|
||||
__default: [] as DashboardItemType[],
|
||||
loadSavedInsights: async () => {
|
||||
const response = await api.get(
|
||||
'api/insight/?' +
|
||||
toParams({
|
||||
order: '-created_at',
|
||||
saved: true,
|
||||
limit: 25,
|
||||
user: true,
|
||||
})
|
||||
`api/projects/${teamLogic.values.currentTeamId}/insights/?${toParams({
|
||||
order: '-created_at',
|
||||
saved: true,
|
||||
limit: 25,
|
||||
user: true,
|
||||
})}`
|
||||
)
|
||||
actions.setSavedInsightsNext(response.next)
|
||||
return response.results
|
||||
@ -75,12 +77,11 @@ export const insightHistoryLogic = kea<insightHistoryLogicType>({
|
||||
__default: [] as DashboardItemType[],
|
||||
loadTeamInsights: async () => {
|
||||
const response = await api.get(
|
||||
'api/insight/?' +
|
||||
toParams({
|
||||
order: '-created_at',
|
||||
saved: true,
|
||||
limit: 25,
|
||||
})
|
||||
`api/projects/${teamLogic.values.currentTeamId}/insights/?${toParams({
|
||||
order: '-created_at',
|
||||
saved: true,
|
||||
limit: 25,
|
||||
})}`
|
||||
)
|
||||
actions.setTeamInsightsNext(response.next)
|
||||
return response.results
|
||||
@ -159,13 +160,13 @@ export const insightHistoryLogic = kea<insightHistoryLogicType>({
|
||||
},
|
||||
listeners: ({ actions, values }) => ({
|
||||
updateInsight: async ({ insight }) => {
|
||||
await api.update(`api/insight/${insight.id}`, insight)
|
||||
await api.update(`api/projects/${teamLogic.values.currentTeamId}/insights/${insight.id}`, insight)
|
||||
toast('Saved Insight')
|
||||
actions.updateInsightSuccess(insight)
|
||||
},
|
||||
deleteInsight: ({ insight }) => {
|
||||
deleteWithUndo({
|
||||
endpoint: 'insight',
|
||||
endpoint: `api/projects/${values.currentTeamId}/insights`,
|
||||
object: { name: insight.name, id: insight.id },
|
||||
callback: () => actions.loadSavedInsights(),
|
||||
})
|
||||
|
@ -4,7 +4,7 @@ import { insightMetadataLogic, InsightMetadataLogicProps } from 'scenes/insights
|
||||
import { expectLogic, truth } from 'kea-test-utils'
|
||||
import { initKeaTestLogic } from '~/test/init'
|
||||
import { insightLogic } from 'scenes/insights/insightLogic'
|
||||
import { defaultAPIMocks, mockAPI } from 'lib/api.mock'
|
||||
import { defaultAPIMocks, mockAPI, MOCK_TEAM_ID } from 'lib/api.mock'
|
||||
import { userLogic } from 'scenes/userLogic'
|
||||
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
|
||||
import { AvailableFeature } from '~/types'
|
||||
@ -23,7 +23,7 @@ describe('insightMetadataLogic', () => {
|
||||
|
||||
mockAPI(async (url) => {
|
||||
const { pathname } = url
|
||||
if (pathname.startsWith('api/insight')) {
|
||||
if (pathname.startsWith(`api/projects/${MOCK_TEAM_ID}/insight`)) {
|
||||
return { results: [], next: null }
|
||||
}
|
||||
return defaultAPIMocks(url, { availableFeatures: [AvailableFeature.DASHBOARD_COLLABORATION] })
|
||||
|
@ -6,6 +6,7 @@ import { NotFound } from 'lib/components/NotFound'
|
||||
import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
|
||||
import React from 'react'
|
||||
import { DashboardItemType } from '~/types'
|
||||
import { teamLogic } from '../teamLogic'
|
||||
import { insightRouterLogicType } from './InsightRouterType'
|
||||
|
||||
const insightRouterLogic = kea<insightRouterLogicType>({
|
||||
@ -23,7 +24,7 @@ const insightRouterLogic = kea<insightRouterLogicType>({
|
||||
},
|
||||
listeners: ({ actions }) => ({
|
||||
loadInsight: async ({ id }) => {
|
||||
const response = await api.get(`api/insight/?short_id=${id}`)
|
||||
const response = await api.get(`api/projects/${teamLogic.values.currentTeamId}/insights/?short_id=${id}`)
|
||||
if (response.results.length) {
|
||||
const item = response.results[0] as DashboardItemType
|
||||
eventUsageLogic.actions.reportInsightShortUrlVisited(true, item.filters.insight || null)
|
||||
|
@ -26,7 +26,7 @@ xdescribe('<Insights /> trends', () => {
|
||||
cy.intercept('/api/action/', { fixture: 'api/action/actions' })
|
||||
cy.intercept('/api/cohort/', { fixture: 'api/cohort/cohorts' })
|
||||
cy.intercept('/api/person/properties/', { fixture: 'api/person/properties' })
|
||||
cy.interceptLazy('/api/insight/', () => ({ fixture: 'api/insight/trends' })).as('api_insight')
|
||||
cy.interceptLazy('/api/projects/2/insights/', () => ({ fixture: 'api/insight/trends' })).as('api_insight')
|
||||
|
||||
helpers.mockPosthog()
|
||||
})
|
||||
@ -79,7 +79,7 @@ xdescribe('<Insights /> trends', () => {
|
||||
it('can render bar graphs', () => {
|
||||
mountAndCheckAPI()
|
||||
|
||||
cy.overrideInterceptLazy('/api/insight/', () => ({ fixture: 'api/insight/trends/breakdown' }))
|
||||
cy.overrideInterceptLazy('/api/projects/2/insights/', () => ({ fixture: 'api/insight/trends/breakdown' }))
|
||||
|
||||
cy.get('[data-attr=add-breakdown-button]').click()
|
||||
cy.get('[data-attr=prop-breakdown-select]').click().type('Browser').type('{enter}')
|
||||
|
@ -3344,7 +3344,7 @@
|
||||
}
|
||||
],
|
||||
"funnelsLoading": false,
|
||||
"next": "http://localhost:8000/api/insight/?insight=FUNNELS&limit=5&offset=5&order=-created_at&saved=true",
|
||||
"next": "http://localhost:8000/api/projects/${currentTeamId}/insights/?insight=FUNNELS&limit=5&offset=5&order=-created_at&saved=true",
|
||||
"loadingMore": false
|
||||
}
|
||||
},
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { defaultAPIMocks, mockAPI } from 'lib/api.mock'
|
||||
import { defaultAPIMocks, mockAPI, MOCK_TEAM_ID } from 'lib/api.mock'
|
||||
import { expectLogic } from 'kea-test-utils'
|
||||
import { initKeaTestLogic } from '~/test/init'
|
||||
import { insightLogic } from './insightLogic'
|
||||
@ -16,20 +16,27 @@ describe('insightLogic', () => {
|
||||
const throwAPIError = (): void => {
|
||||
throw { status: 0, statusText: 'error from the API' }
|
||||
}
|
||||
if (['api/insight/42', 'api/insight/43'].includes(pathname)) {
|
||||
if (
|
||||
[`api/projects/${MOCK_TEAM_ID}/insights/42`, `api/projects/${MOCK_TEAM_ID}/insights/43`].includes(pathname)
|
||||
) {
|
||||
return {
|
||||
result: pathname === 'api/insight/42' ? ['result from api'] : null,
|
||||
id: pathname === 'api/insight/42' ? 42 : 43,
|
||||
result: pathname.endsWith('42') ? ['result from api'] : null,
|
||||
id: pathname.endsWith('42') ? 42 : 43,
|
||||
filters: {
|
||||
insight: ViewType.TRENDS,
|
||||
events: [{ id: 3 }],
|
||||
properties: [{ value: 'a', operator: PropertyOperator.Exact, key: 'a', type: 'a' }],
|
||||
},
|
||||
}
|
||||
} else if (['api/insight/44'].includes(pathname)) {
|
||||
} else if ([`api/projects/${MOCK_TEAM_ID}/insights/44`].includes(pathname)) {
|
||||
throwAPIError()
|
||||
} else if (
|
||||
['api/insight', 'api/insight/session/', 'api/insight/trend/', 'api/insight/funnel/'].includes(pathname)
|
||||
[
|
||||
`api/projects/${MOCK_TEAM_ID}/insights`,
|
||||
`api/projects/${MOCK_TEAM_ID}/insights/session/`,
|
||||
`api/projects/${MOCK_TEAM_ID}/insights/trend/`,
|
||||
`api/projects/${MOCK_TEAM_ID}/insights/funnel/`,
|
||||
].includes(pathname)
|
||||
) {
|
||||
if (searchParams?.events?.[0]?.throw) {
|
||||
throwAPIError()
|
||||
|
@ -20,6 +20,7 @@ import { pollFunnel } from 'scenes/funnels/funnelUtils'
|
||||
import { preflightLogic } from 'scenes/PreflightCheck/logic'
|
||||
import { extractObjectDiffKeys } from './utils'
|
||||
import * as Sentry from '@sentry/browser'
|
||||
import { teamLogic } from '../teamLogic'
|
||||
|
||||
const IS_TEST_MODE = process.env.NODE_ENV === 'test'
|
||||
|
||||
@ -38,6 +39,7 @@ export const insightLogic = kea<insightLogicType>({
|
||||
key: keyForInsightLogicProps('new'),
|
||||
|
||||
connect: {
|
||||
values: [teamLogic, ['currentTeamId']],
|
||||
logic: [eventUsageLogic, dashboardsModel],
|
||||
},
|
||||
|
||||
@ -94,13 +96,16 @@ export const insightLogic = kea<insightLogicType>({
|
||||
} as Partial<DashboardItemType>,
|
||||
{
|
||||
loadInsight: async ({ id }) => {
|
||||
return await api.get(`api/insight/${id}`)
|
||||
return await api.get(`api/projects/${teamLogic.values.currentTeamId}/insights/${id}`)
|
||||
},
|
||||
updateInsight: async (payload: Partial<DashboardItemType>, breakpoint) => {
|
||||
if (!Object.entries(payload).length) {
|
||||
return
|
||||
}
|
||||
const response = await api.update(`api/insight/${values.insight.id}`, payload)
|
||||
const response = await api.update(
|
||||
`api/projects/${teamLogic.values.currentTeamId}/insights/${values.insight.id}`,
|
||||
payload
|
||||
)
|
||||
breakpoint()
|
||||
return { ...response, result: response.result || values.insight.result }
|
||||
},
|
||||
@ -127,6 +132,10 @@ export const insightLogic = kea<insightLogicType>({
|
||||
}
|
||||
|
||||
let response
|
||||
const { currentTeamId } = values
|
||||
if (!currentTeamId) {
|
||||
throw new Error("Can't load insight before current project is determined.")
|
||||
}
|
||||
try {
|
||||
if (
|
||||
insight === ViewType.TRENDS ||
|
||||
@ -134,23 +143,27 @@ export const insightLogic = kea<insightLogicType>({
|
||||
insight === ViewType.LIFECYCLE
|
||||
) {
|
||||
response = await api.get(
|
||||
`api/insight/trend/?${toParams(filterTrendsClientSideParams(params))}`,
|
||||
`api/projects/${currentTeamId}/insights/trend/?${toParams(
|
||||
filterTrendsClientSideParams(params)
|
||||
)}`,
|
||||
cache.abortController.signal
|
||||
)
|
||||
} else if (insight === ViewType.SESSIONS || filters?.session) {
|
||||
response = await api.get(
|
||||
`api/insight/session/?${toParams(filterTrendsClientSideParams(params))}`,
|
||||
`api/projects/${currentTeamId}/insights/session/?${toParams(
|
||||
filterTrendsClientSideParams(params)
|
||||
)}`,
|
||||
cache.abortController.signal
|
||||
)
|
||||
} else if (insight === ViewType.RETENTION) {
|
||||
response = await api.get(
|
||||
`api/insight/retention/?${toParams(params)}`,
|
||||
`api/projects/${currentTeamId}/insights/retention/?${toParams(params)}`,
|
||||
cache.abortController.signal
|
||||
)
|
||||
} else if (insight === ViewType.FUNNELS) {
|
||||
response = await pollFunnel(params)
|
||||
response = await pollFunnel(currentTeamId, params)
|
||||
} else if (insight === ViewType.PATHS) {
|
||||
response = await api.create(`api/insight/path`, params)
|
||||
response = await api.create(`api/projects/${currentTeamId}/insights/path`, params)
|
||||
} else {
|
||||
throw new Error(`Can not load insight of type ${insight}`)
|
||||
}
|
||||
@ -467,10 +480,13 @@ export const insightLogic = kea<insightLogicType>({
|
||||
actions.setInsight({ ...values.insight, tags: values.insight.tags?.filter((_tag) => _tag !== tag) })
|
||||
},
|
||||
saveInsight: async () => {
|
||||
const savedInsight = await api.update(`api/insight/${values.insight.id}`, {
|
||||
...values.insight,
|
||||
saved: true,
|
||||
})
|
||||
const savedInsight = await api.update(
|
||||
`api/projects/${teamLogic.values.currentTeamId}/insights/${values.insight.id}`,
|
||||
{
|
||||
...values.insight,
|
||||
saved: true,
|
||||
}
|
||||
)
|
||||
actions.setInsight({ ...savedInsight, result: savedInsight.result || values.insight.result })
|
||||
actions.setInsightMode(ItemMode.View, InsightEventSource.InsightHeader)
|
||||
toast(
|
||||
@ -481,7 +497,7 @@ export const insightLogic = kea<insightLogicType>({
|
||||
)
|
||||
},
|
||||
loadInsightSuccess: async ({ payload, insight }) => {
|
||||
// loaded `/api/insight`, but it didn't have `results`, so make another query
|
||||
// loaded `/api/projects/:id/insights`, but it didn't have `results`, so make another query
|
||||
if (!insight.result && values.filters && !payload?.doNotLoadResults) {
|
||||
actions.loadResults()
|
||||
}
|
||||
@ -492,7 +508,7 @@ export const insightLogic = kea<insightLogicType>({
|
||||
return
|
||||
}
|
||||
if (!insight.id) {
|
||||
const createdInsight = await api.create('api/insight', {
|
||||
const createdInsight = await api.create(`api/projects/${values.currentTeamId}/insights`, {
|
||||
filters: insight.filters,
|
||||
})
|
||||
breakpoint()
|
||||
|
@ -8,7 +8,7 @@ import { organizationLogicType } from './organizationLogicType'
|
||||
|
||||
jest.mock('lib/api')
|
||||
|
||||
describe('entityFilterLogic', () => {
|
||||
describe('organizationLogic', () => {
|
||||
let logic: BuiltLogic<organizationLogicType<OrganizationUpdatePayload>>
|
||||
|
||||
mockAPI(async (url) => {
|
||||
@ -19,9 +19,7 @@ describe('entityFilterLogic', () => {
|
||||
beforeEach(() => {
|
||||
window.POSTHOG_APP_CONTEXT = { current_user: { organization: { id: 'WXYZ' } } } as unknown as AppContext
|
||||
})
|
||||
afterEach(() => {
|
||||
delete window.POSTHOG_APP_CONTEXT
|
||||
})
|
||||
|
||||
initKeaTestLogic({
|
||||
logic: organizationLogic,
|
||||
onLogic: (l) => {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { defaultAPIMocks, mockAPI } from 'lib/api.mock'
|
||||
import { defaultAPIMocks, mockAPI, MOCK_TEAM_ID } from 'lib/api.mock'
|
||||
import { expectLogic } from 'kea-test-utils'
|
||||
import { initKeaTestLogic } from '~/test/init'
|
||||
import { pathsLogic } from 'scenes/paths/pathsLogic'
|
||||
@ -11,7 +11,7 @@ describe('pathsLogic', () => {
|
||||
|
||||
mockAPI(async (url) => {
|
||||
const { pathname } = url
|
||||
if (['api/insight/paths/'].includes(pathname)) {
|
||||
if (`api/projects/${MOCK_TEAM_ID}/insights/paths/` === pathname) {
|
||||
return { result: ['result from api'] }
|
||||
}
|
||||
return defaultAPIMocks(url)
|
||||
@ -28,6 +28,7 @@ describe('pathsLogic', () => {
|
||||
it('setFilter calls insightLogic.setFilters', async () => {
|
||||
await expectLogic(logic, () => {
|
||||
logic.actions.setFilter({
|
||||
insight: 'PATHS',
|
||||
step_limit: 999,
|
||||
})
|
||||
})
|
||||
@ -51,6 +52,7 @@ describe('pathsLogic', () => {
|
||||
it('insightLogic.setFilters updates filter', async () => {
|
||||
await expectLogic(logic, () => {
|
||||
insightLogic(props).actions.setFilters({
|
||||
insight: 'PATHS',
|
||||
step_limit: 999,
|
||||
})
|
||||
})
|
||||
|
@ -7,9 +7,10 @@ describe('<Person /> ', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
cy.intercept('/_preflight/', { fixture: '_preflight' })
|
||||
cy.intercept('/api/projects/@current/', { fixture: 'api/projects/@current' })
|
||||
cy.intercept('/api/users/@me/', { fixture: 'api/users/@me' })
|
||||
cy.intercept('/api/person/', { fixture: 'api/person' }).as('api_person')
|
||||
cy.intercept('/api/event/?', { fixture: 'api/event/single_person_events' }).as('api_event')
|
||||
cy.intercept('/api/projects/2/events/?', { fixture: 'api/event/single_person_events' }).as('api_event')
|
||||
|
||||
helpers.mockPosthog()
|
||||
helpers.setLocation('/person/01779064-53be-000c-683f-23b1a8c8eb4c')
|
||||
@ -38,10 +39,10 @@ describe('<Person /> ', () => {
|
||||
cy.intercept('/api/dashboard/', { fixture: 'api/dashboard' })
|
||||
cy.intercept('/api/personal_api_keys/', { fixture: 'api/personal_api_keys' })
|
||||
cy.intercept('/api/projects/@current/', { fixture: 'api/projects/@current' })
|
||||
cy.intercept('/api/event/sessions/', { fixture: 'api/event/sessions/session_with_recording' }).as(
|
||||
'api_sessions'
|
||||
)
|
||||
cy.intercept('/api/event/session_recording', { fixture: 'api/event/session_recording' }).as(
|
||||
cy.intercept('/api/projects/2/events/sessions/', {
|
||||
fixture: 'api/event/sessions/session_with_recording',
|
||||
}).as('api_sessions')
|
||||
cy.intercept('/api/projects/2/events/session_recording', { fixture: 'api/event/session_recording' }).as(
|
||||
'api_session_recording'
|
||||
)
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { defaultAPIMocks, mockAPI } from 'lib/api.mock'
|
||||
import { defaultAPIMocks, mockAPI, MOCK_TEAM_ID } from 'lib/api.mock'
|
||||
import { expectLogic } from 'kea-test-utils'
|
||||
import { initKeaTestLogic } from '~/test/init'
|
||||
import { retentionTableLogic } from 'scenes/retention/retentionTableLogic'
|
||||
@ -11,9 +11,9 @@ describe('retentionTableLogic', () => {
|
||||
|
||||
mockAPI(async (url) => {
|
||||
const { pathname } = url
|
||||
if (['api/insight', 'api/projects/85/actions/'].includes(pathname)) {
|
||||
if ([`api/projects/${MOCK_TEAM_ID}/insights/`, `api/projects/${MOCK_TEAM_ID}/actions/`].includes(pathname)) {
|
||||
return { results: [] }
|
||||
} else if (pathname === 'api/insight/retention/') {
|
||||
} else if (pathname === `api/projects/${MOCK_TEAM_ID}/insights/retention/`) {
|
||||
return { result: ['result from api'] }
|
||||
}
|
||||
return defaultAPIMocks(url)
|
||||
@ -29,37 +29,37 @@ describe('retentionTableLogic', () => {
|
||||
|
||||
it('setFilters calls insightLogic.setFilters', async () => {
|
||||
await expectLogic(logic, () => {
|
||||
logic.actions.setFilters({ events: [{ id: 42 }] })
|
||||
logic.actions.setFilters({ insight: 'RETENTION', period: 'Week' })
|
||||
})
|
||||
.toDispatchActions([
|
||||
(action) =>
|
||||
action.type === insightLogic(props).actionTypes.setFilters &&
|
||||
action.payload.filters?.events?.[0]?.id === 42,
|
||||
action.payload.filters?.period === 'Week',
|
||||
])
|
||||
.toMatchValues(logic, {
|
||||
filters: expect.objectContaining({
|
||||
events: [{ id: 42 }],
|
||||
period: 'Week',
|
||||
}),
|
||||
})
|
||||
.toMatchValues(insightLogic(props), {
|
||||
filters: expect.objectContaining({
|
||||
events: [{ id: 42 }],
|
||||
period: 'Week',
|
||||
}),
|
||||
})
|
||||
})
|
||||
|
||||
it('insightLogic.setFilters updates filters', async () => {
|
||||
await expectLogic(logic, () => {
|
||||
insightLogic(props).actions.setFilters({ events: [{ id: 42 }] })
|
||||
insightLogic(props).actions.setFilters({ insight: 'RETENTION', period: 'Week' })
|
||||
})
|
||||
.toMatchValues(logic, {
|
||||
filters: expect.objectContaining({
|
||||
events: [{ id: 42 }],
|
||||
period: 'Week',
|
||||
}),
|
||||
})
|
||||
.toMatchValues(insightLogic(props), {
|
||||
filters: expect.objectContaining({
|
||||
events: [{ id: 42 }],
|
||||
period: 'Week',
|
||||
}),
|
||||
})
|
||||
})
|
||||
|
@ -35,6 +35,7 @@ import dayjs from 'dayjs'
|
||||
|
||||
import { PageHeader } from 'lib/components/PageHeader'
|
||||
import { SavedInsightsEmptyState } from 'scenes/insights/EmptyStates'
|
||||
import { teamLogic } from '../teamLogic'
|
||||
|
||||
const { TabPane } = Tabs
|
||||
|
||||
@ -64,6 +65,7 @@ export function SavedInsights(): JSX.Element {
|
||||
|
||||
const { nameSortedDashboards } = useValues(dashboardsModel)
|
||||
const { hasDashboardCollaboration } = useValues(organizationLogic)
|
||||
const { currentTeamId } = useValues(teamLogic)
|
||||
const { members } = useValues(membersLogic)
|
||||
const { tab, order, createdBy, layoutView, search, insightType, dateFrom, dateTo } = filters
|
||||
const insightTypes: InsightType[] = [
|
||||
@ -210,7 +212,7 @@ export function SavedInsights(): JSX.Element {
|
||||
onClick={() =>
|
||||
deleteWithUndo({
|
||||
object: item,
|
||||
endpoint: 'insight',
|
||||
endpoint: `api/projects/${currentTeamId}/insights`,
|
||||
callback: loadInsights,
|
||||
})
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import { prompt } from 'lib/logic/prompt'
|
||||
import { toast } from 'react-toastify'
|
||||
import { Dayjs } from 'dayjs'
|
||||
import { dashboardItemsModel } from '~/models/dashboardItemsModel'
|
||||
import { teamLogic } from '../teamLogic'
|
||||
import { urls } from 'scenes/urls'
|
||||
|
||||
export interface InsightsResult {
|
||||
@ -42,6 +43,9 @@ function cleanFilters(values: Partial<SavedInsightFilters>): SavedInsightFilters
|
||||
}
|
||||
|
||||
export const savedInsightsLogic = kea<savedInsightsLogicType<InsightsResult, SavedInsightFilters>>({
|
||||
connect: {
|
||||
values: [teamLogic, ['currentTeamId']],
|
||||
},
|
||||
actions: {
|
||||
setSavedInsightsFilters: (filters: Partial<SavedInsightFilters>, merge = true) => ({ filters, merge }),
|
||||
addGraph: (type: string) => ({ type }),
|
||||
@ -58,30 +62,31 @@ export const savedInsightsLogic = kea<savedInsightsLogicType<InsightsResult, Sav
|
||||
await breakpoint(1)
|
||||
const { filters } = values
|
||||
const response = await api.get(
|
||||
'api/insight/?' +
|
||||
toParams({
|
||||
order: filters.order,
|
||||
limit: 15,
|
||||
saved: true,
|
||||
...(filters.tab === SavedInsightsTabs.Yours && { user: true }),
|
||||
...(filters.tab === SavedInsightsTabs.Favorites && { favorited: true }),
|
||||
...(filters.search && { search: filters.search }),
|
||||
...(filters.insightType?.toLowerCase() !== 'all types' && {
|
||||
insight: filters.insightType?.toUpperCase(),
|
||||
`api/projects/${teamLogic.values.currentTeamId}/insights/?${toParams({
|
||||
order: filters.order,
|
||||
limit: 15,
|
||||
saved: true,
|
||||
...(filters.tab === SavedInsightsTabs.Yours && { user: true }),
|
||||
...(filters.tab === SavedInsightsTabs.Favorites && { favorited: true }),
|
||||
...(filters.search && { search: filters.search }),
|
||||
...(filters.insightType?.toLowerCase() !== 'all types' && {
|
||||
insight: filters.insightType?.toUpperCase(),
|
||||
}),
|
||||
...(filters.createdBy !== 'All users' && { created_by: filters.createdBy }),
|
||||
...(filters.dateFrom &&
|
||||
filters.dateFrom !== 'all' && {
|
||||
date_from: filters.dateFrom,
|
||||
date_to: filters.dateTo,
|
||||
}),
|
||||
...(filters.createdBy !== 'All users' && { created_by: filters.createdBy }),
|
||||
...(filters.dateFrom &&
|
||||
filters.dateFrom !== 'all' && {
|
||||
date_from: filters.dateFrom,
|
||||
date_to: filters.dateTo,
|
||||
}),
|
||||
})
|
||||
})}`
|
||||
)
|
||||
return response
|
||||
},
|
||||
loadPaginatedInsights: async (url: string) => await api.get(url),
|
||||
updateFavoritedInsight: async ({ id, favorited }) => {
|
||||
const response = await api.update(`api/insight/${id}`, { favorited })
|
||||
const response = await api.update(`api/projects/${teamLogic.values.currentTeamId}/insights/${id}`, {
|
||||
favorited,
|
||||
})
|
||||
const updatedInsights = values.insights.results.map((insight) =>
|
||||
insight.id === id ? response : insight
|
||||
)
|
||||
@ -149,14 +154,16 @@ export const savedInsightsLogic = kea<savedInsightsLogicType<InsightsResult, Sav
|
||||
value: name,
|
||||
error: 'You must enter name',
|
||||
success: async (name: string) => {
|
||||
const insight = await api.update(`api/insight/${id}`, { name })
|
||||
const insight = await api.update(`api/projects/${teamLogic.values.currentTeamId}/insights/${id}`, {
|
||||
name,
|
||||
})
|
||||
toast('Successfully renamed item')
|
||||
actions.setInsight(insight)
|
||||
},
|
||||
})
|
||||
},
|
||||
duplicateInsight: async ({ insight }) => {
|
||||
await api.create('api/insight', insight)
|
||||
await api.create(`api/projects/${values.currentTeamId}/insights`, insight)
|
||||
actions.loadInsights()
|
||||
},
|
||||
setDates: () => {
|
||||
|
@ -157,6 +157,9 @@ export const sceneConfigurations: Partial<Record<Scene, SceneConfig>> = {
|
||||
[Scene.Sessions]: {
|
||||
projectBased: true,
|
||||
},
|
||||
[Scene.SessionRecordings]: {
|
||||
projectBased: true,
|
||||
},
|
||||
[Scene.Person]: {
|
||||
projectBased: true,
|
||||
},
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
} from './sessionRecordingsTableLogic'
|
||||
import { sessionRecordingsTableLogicType } from './sessionRecordingsTableLogicType'
|
||||
import { BuiltLogic } from 'kea'
|
||||
import { mockAPI, defaultAPIMocks } from 'lib/api.mock'
|
||||
import { mockAPI, defaultAPIMocks, MOCK_TEAM_ID } from 'lib/api.mock'
|
||||
import { expectLogic } from 'kea-test-utils'
|
||||
import { initKeaTestLogic } from '~/test/init'
|
||||
import { router } from 'kea-router'
|
||||
@ -21,7 +21,7 @@ describe('sessionRecordingsTableLogic', () => {
|
||||
|
||||
mockAPI(async (url) => {
|
||||
const { pathname, searchParams } = url
|
||||
if (pathname === 'api/projects/@current/session_recordings') {
|
||||
if (pathname === `api/projects/${MOCK_TEAM_ID}/session_recordings`) {
|
||||
if (searchParams['events'].length > 0 && searchParams['events'][0]['id'] === '$autocapture') {
|
||||
return {
|
||||
results: ['List of recordings filtered by events'],
|
||||
|
@ -14,6 +14,7 @@ import { router } from 'kea-router'
|
||||
import dayjs from 'dayjs'
|
||||
import { RecordingWatchedSource } from 'lib/utils/eventUsageLogic'
|
||||
import equal from 'fast-deep-equal'
|
||||
import { teamLogic } from '../teamLogic'
|
||||
|
||||
export type SessionRecordingId = string
|
||||
export type PersonUUID = string
|
||||
@ -50,6 +51,9 @@ export const sessionRecordingsTableLogic = kea<sessionRecordingsTableLogicType<P
|
||||
props: {} as {
|
||||
personUUID?: PersonUUID
|
||||
},
|
||||
connect: {
|
||||
values: [teamLogic, ['currentTeamId']],
|
||||
},
|
||||
actions: {
|
||||
getSessionRecordings: true,
|
||||
openSessionPlayer: (sessionRecordingId: SessionRecordingId | null, source: RecordingWatchedSource) => ({
|
||||
@ -83,7 +87,7 @@ export const sessionRecordingsTableLogic = kea<sessionRecordingsTableLogicType<P
|
||||
}
|
||||
const params = toParams(paramsDict)
|
||||
await breakpoint(100) // Debounce for lots of quick filter changes
|
||||
const response = await api.get(`api/projects/@current/session_recordings?${params}`)
|
||||
const response = await api.get(`api/projects/${values.currentTeamId}/session_recordings?${params}`)
|
||||
breakpoint()
|
||||
return response
|
||||
},
|
||||
|
@ -12,7 +12,7 @@ xdescribe('<Sessions />', () => {
|
||||
cy.intercept('/api/personal_api_keys/', { fixture: 'api/personal_api_keys' })
|
||||
cy.intercept('/api/projects/@current/', { fixture: 'api/projects/@current' })
|
||||
cy.intercept('/api/person/properties/', { fixture: 'api/person/properties' })
|
||||
cy.interceptLazy('/api/event/sessions/', given.sessions).as('api_sessions')
|
||||
cy.interceptLazy('/api/projects/2/events/sessions/', given.sessions).as('api_sessions')
|
||||
|
||||
helpers.mockPosthog()
|
||||
helpers.setLocation('/sessions')
|
||||
@ -108,7 +108,7 @@ xdescribe('<Sessions />', () => {
|
||||
given('sessions', () => iterateResponses([{ fixture: 'api/event/sessions/session_with_recording' }]))
|
||||
|
||||
beforeEach(() => {
|
||||
cy.intercept('/api/event/session_recording', { fixture: 'api/event/session_recording' }).as(
|
||||
cy.intercept('/api/projects/2/events/session_recording', { fixture: 'api/event/session_recording' }).as(
|
||||
'api_session_recording'
|
||||
)
|
||||
})
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { sessionsPlayLogic } from 'scenes/sessions/sessionsPlayLogic'
|
||||
import { api, defaultAPIMocks, mockAPI } from 'lib/api.mock'
|
||||
import { api, defaultAPIMocks, mockAPI, MOCK_TEAM_ID } from 'lib/api.mock'
|
||||
import { expectLogic } from 'kea-test-utils'
|
||||
import { initKeaTestLogic } from '~/test/init'
|
||||
import { sessionsTableLogic } from 'scenes/sessions/sessionsTableLogic'
|
||||
@ -10,13 +10,15 @@ import { combineUrl } from 'kea-router'
|
||||
|
||||
jest.mock('lib/api')
|
||||
|
||||
const EVENTS_SESSION_RECORDING_ENDPOINT = `api/projects/${MOCK_TEAM_ID}/events/session_recording`
|
||||
|
||||
describe('sessionsPlayLogic', () => {
|
||||
let logic: ReturnType<typeof sessionsPlayLogic.build>
|
||||
|
||||
mockAPI(async (url) => {
|
||||
if (
|
||||
url.pathname === 'api/event/session_recording' || // Old api
|
||||
url.pathname === 'api/projects/@current/session_recordings' // New api
|
||||
url.pathname === EVENTS_SESSION_RECORDING_ENDPOINT || // Old api
|
||||
url.pathname === `api/projects/${MOCK_TEAM_ID}/session_recordings` // New api
|
||||
) {
|
||||
return { result: recordingJson }
|
||||
} else if (url.pathname === 'api/sessions_filter') {
|
||||
@ -63,29 +65,29 @@ describe('sessionsPlayLogic', () => {
|
||||
await expectLogic(logic).toMount([eventUsageLogic])
|
||||
api.get.mockClear()
|
||||
|
||||
const firstNext = `api/event/session_recording?session_recording_id=1&offset=200&limit=200`
|
||||
const secondNext = `api/event/session_recording?session_recording_id=1&offset=400&limit=200`
|
||||
const thirdNext = `api/event/session_recording?session_recording_id=1&offset=600&limit=200`
|
||||
const firstNext = `${EVENTS_SESSION_RECORDING_ENDPOINT}?session_recording_id=1&offset=200&limit=200`
|
||||
const secondNext = `${EVENTS_SESSION_RECORDING_ENDPOINT}?session_recording_id=1&offset=400&limit=200`
|
||||
const thirdNext = `${EVENTS_SESSION_RECORDING_ENDPOINT}?session_recording_id=1&offset=600&limit=200`
|
||||
const snaps = recordingJson.snapshots
|
||||
|
||||
api.get
|
||||
.mockImplementationOnce(async (url: string) => {
|
||||
if (combineUrl(url).pathname === 'api/event/session_recording') {
|
||||
if (combineUrl(url).pathname === EVENTS_SESSION_RECORDING_ENDPOINT) {
|
||||
return { result: { ...recordingJson, next: firstNext } }
|
||||
}
|
||||
})
|
||||
.mockImplementationOnce(async (url: string) => {
|
||||
if (combineUrl(url).pathname === 'api/event/session_recording') {
|
||||
if (combineUrl(url).pathname === EVENTS_SESSION_RECORDING_ENDPOINT) {
|
||||
return { result: { ...recordingJson, next: secondNext } }
|
||||
}
|
||||
})
|
||||
.mockImplementationOnce(async (url: string) => {
|
||||
if (combineUrl(url).pathname === 'api/event/session_recording') {
|
||||
if (combineUrl(url).pathname === EVENTS_SESSION_RECORDING_ENDPOINT) {
|
||||
return { result: { ...recordingJson, next: thirdNext } }
|
||||
}
|
||||
})
|
||||
.mockImplementationOnce(async (url: string) => {
|
||||
if (combineUrl(url).pathname === 'api/event/session_recording') {
|
||||
if (combineUrl(url).pathname === EVENTS_SESSION_RECORDING_ENDPOINT) {
|
||||
return { result: recordingJson }
|
||||
}
|
||||
})
|
||||
@ -129,18 +131,18 @@ describe('sessionsPlayLogic', () => {
|
||||
await expectLogic(logic).toMount([eventUsageLogic])
|
||||
api.get.mockClear()
|
||||
|
||||
const firstNext = `api/event/session_recording?session_recording_id=1&offset=200&limit=200`
|
||||
const secondNext = `api/event/session_recording?session_recording_id=1&offset=400&limit=200`
|
||||
const firstNext = `${EVENTS_SESSION_RECORDING_ENDPOINT}?session_recording_id=1&offset=200&limit=200`
|
||||
const secondNext = `${EVENTS_SESSION_RECORDING_ENDPOINT}?session_recording_id=1&offset=400&limit=200`
|
||||
const snaps = recordingJson.snapshots
|
||||
|
||||
api.get
|
||||
.mockImplementationOnce(async (url: string) => {
|
||||
if (combineUrl(url).pathname === 'api/event/session_recording') {
|
||||
if (combineUrl(url).pathname === EVENTS_SESSION_RECORDING_ENDPOINT) {
|
||||
return { result: { ...recordingJson, next: firstNext } }
|
||||
}
|
||||
})
|
||||
.mockImplementationOnce(async (url: string) => {
|
||||
if (combineUrl(url).pathname === 'api/event/session_recording') {
|
||||
if (combineUrl(url).pathname === EVENTS_SESSION_RECORDING_ENDPOINT) {
|
||||
return { result: { ...recordingJson, next: secondNext } }
|
||||
}
|
||||
})
|
||||
@ -193,22 +195,22 @@ describe('sessionsPlayLogic', () => {
|
||||
await expectLogic(preflightLogic).toDispatchActions(['loadPreflightSuccess'])
|
||||
await expectLogic(logic).toMount([eventUsageLogic])
|
||||
|
||||
const firstNext = `api/event/session_recording?session_recording_id=1&offset=200&limit=200`
|
||||
const secondNext = `api/event/session_recording?session_recording_id=1&offset=400&limit=200`
|
||||
const firstNext = `${EVENTS_SESSION_RECORDING_ENDPOINT}?session_recording_id=1&offset=200&limit=200`
|
||||
const secondNext = `${EVENTS_SESSION_RECORDING_ENDPOINT}?session_recording_id=1&offset=400&limit=200`
|
||||
|
||||
api.get
|
||||
.mockImplementationOnce(async (url: string) => {
|
||||
if (combineUrl(url).pathname === 'api/event/session_recording') {
|
||||
if (combineUrl(url).pathname === EVENTS_SESSION_RECORDING_ENDPOINT) {
|
||||
return { result: { ...recordingJson, next: firstNext } }
|
||||
}
|
||||
})
|
||||
.mockImplementationOnce(async (url: string) => {
|
||||
if (combineUrl(url).pathname === 'api/event/session_recording') {
|
||||
if (combineUrl(url).pathname === EVENTS_SESSION_RECORDING_ENDPOINT) {
|
||||
return { result: { ...recordingJson, next: secondNext } }
|
||||
}
|
||||
})
|
||||
.mockImplementationOnce(async (url: string) => {
|
||||
if (combineUrl(url).pathname === 'api/event/session_recording') {
|
||||
if (combineUrl(url).pathname === EVENTS_SESSION_RECORDING_ENDPOINT) {
|
||||
return { result: recordingJson }
|
||||
}
|
||||
})
|
||||
@ -269,7 +271,7 @@ describe('sessionsPlayLogic', () => {
|
||||
},
|
||||
]
|
||||
api.get.mockImplementationOnce(async (url: string) => {
|
||||
if (combineUrl(url).pathname === 'api/event/session_recording') {
|
||||
if (combineUrl(url).pathname === EVENTS_SESSION_RECORDING_ENDPOINT) {
|
||||
return {
|
||||
result: {
|
||||
...recordingJson,
|
||||
@ -299,7 +301,7 @@ describe('sessionsPlayLogic', () => {
|
||||
},
|
||||
]
|
||||
api.get.mockImplementationOnce(async (url: string) => {
|
||||
if (combineUrl(url).pathname === 'api/event/session_recording') {
|
||||
if (combineUrl(url).pathname === EVENTS_SESSION_RECORDING_ENDPOINT) {
|
||||
return {
|
||||
result: {
|
||||
...recordingJson,
|
||||
|
@ -8,13 +8,20 @@ import { EventIndex } from '@posthog/react-rrweb-player'
|
||||
import { sessionsTableLogic } from 'scenes/sessions/sessionsTableLogic'
|
||||
import { toast } from 'react-toastify'
|
||||
import { eventUsageLogic, RecordingWatchedSource } from 'lib/utils/eventUsageLogic'
|
||||
import { teamLogic } from '../teamLogic'
|
||||
import { eventWithTime } from 'rrweb/typings/types'
|
||||
|
||||
const IS_TEST_MODE = process.env.NODE_ENV === 'test'
|
||||
|
||||
export const sessionsPlayLogic = kea<sessionsPlayLogicType>({
|
||||
connect: {
|
||||
logic: [eventUsageLogic],
|
||||
values: [sessionsTableLogic, ['sessions', 'pagination', 'orderedSessionRecordingIds', 'loadedSessionEvents']],
|
||||
values: [
|
||||
sessionsTableLogic,
|
||||
['sessions', 'pagination', 'orderedSessionRecordingIds', 'loadedSessionEvents'],
|
||||
teamLogic,
|
||||
['currentTeamId'],
|
||||
],
|
||||
actions: [
|
||||
sessionsTableLogic,
|
||||
['fetchNextSessions', 'appendNewSessions', 'closeSessionPlayer', 'loadSessionEvents'],
|
||||
@ -153,7 +160,7 @@ export const sessionsPlayLogic = kea<sessionsPlayLogicType>({
|
||||
} else {
|
||||
// Very first call
|
||||
const params = toParams({ session_recording_id: sessionRecordingId, save_view: true })
|
||||
response = await api.get(`api/event/session_recording?${params}`)
|
||||
response = await api.get(`api/projects/${values.currentTeamId}/events/session_recording?${params}`)
|
||||
actions.reportUsage(response.result, performance.now() - startTime)
|
||||
}
|
||||
const currData = values.sessionPlayerData
|
||||
|
@ -10,6 +10,7 @@ import { sessionsFiltersLogic } from 'scenes/sessions/filters/sessionsFiltersLog
|
||||
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
|
||||
import { FEATURE_FLAGS } from 'lib/constants'
|
||||
import { RecordingWatchedSource } from 'lib/utils/eventUsageLogic'
|
||||
import { teamLogic } from '../teamLogic'
|
||||
|
||||
type SessionRecordingId = string
|
||||
|
||||
@ -49,7 +50,9 @@ export const sessionsTableLogic = kea<sessionsTableLogicType<SessionRecordingId>
|
||||
properties: values.properties,
|
||||
})
|
||||
await breakpoint(10)
|
||||
const response = await api.get(`api/event/sessions/?${params}`)
|
||||
const response = await api.get(
|
||||
`api/projects/${teamLogic.values.currentTeamId}/events/sessions/?${params}`
|
||||
)
|
||||
breakpoint()
|
||||
actions.setPagination(response.pagination)
|
||||
return response.result
|
||||
@ -207,7 +210,7 @@ export const sessionsTableLogic = kea<sessionsTableLogicType<SessionRecordingId>
|
||||
filters: values.filters,
|
||||
properties: values.properties,
|
||||
})
|
||||
const response = await api.get(`api/event/sessions/?${params}`)
|
||||
const response = await api.get(`api/projects/${teamLogic.values.currentTeamId}/events/sessions/?${params}`)
|
||||
breakpoint()
|
||||
actions.setPagination(response.pagination)
|
||||
actions.appendNewSessions(response.result)
|
||||
@ -237,7 +240,9 @@ export const sessionsTableLogic = kea<sessionsTableLogicType<SessionRecordingId>
|
||||
|
||||
await breakpoint(200)
|
||||
|
||||
const response = await api.get(`api/event/session_events?${toParams(params)}`)
|
||||
const response = await api.get(
|
||||
`api/projects/${teamLogic.values.currentTeamId}/events/session_events?${toParams(params)}`
|
||||
)
|
||||
actions.addSessionEvents(session, response.result)
|
||||
}
|
||||
},
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { BuiltLogic } from 'kea'
|
||||
import { defaultAPIMocks, mockAPI } from 'lib/api.mock'
|
||||
import { defaultAPIMocks, mockAPI, MOCK_TEAM_ID } from 'lib/api.mock'
|
||||
import { expectLogic } from 'kea-test-utils'
|
||||
import { initKeaTestLogic } from '~/test/init'
|
||||
import { trendsLogic } from 'scenes/trends/trendsLogic'
|
||||
@ -14,9 +14,15 @@ describe('trendsLogic', () => {
|
||||
|
||||
mockAPI(async (url) => {
|
||||
const { pathname } = url
|
||||
if (['api/insight'].includes(pathname)) {
|
||||
if (pathname === `api/projects/${MOCK_TEAM_ID}/insights`) {
|
||||
return { results: [] }
|
||||
} else if (['api/insight/123', 'api/insight/session/', 'api/insight/trend/'].includes(pathname)) {
|
||||
} else if (
|
||||
[
|
||||
`api/projects/${MOCK_TEAM_ID}/insights/123`,
|
||||
`api/projects/${MOCK_TEAM_ID}/insights/session/`,
|
||||
`api/projects/${MOCK_TEAM_ID}/insights/trend/`,
|
||||
].includes(pathname)
|
||||
) {
|
||||
return { result: ['result from api'] }
|
||||
}
|
||||
return defaultAPIMocks(url)
|
||||
|
@ -3,6 +3,8 @@ import { initKea } from '~/initKea'
|
||||
import { testUtilsPlugin, expectLogic } from 'kea-test-utils'
|
||||
import { createMemoryHistory } from 'history'
|
||||
import posthog from 'posthog-js'
|
||||
import { AppContext } from '../types'
|
||||
import { MOCK_TEAM_ID } from '../lib/api.mock'
|
||||
|
||||
export function initKeaTestLogic<L extends Logic = Logic>({
|
||||
logic,
|
||||
@ -17,6 +19,10 @@ export function initKeaTestLogic<L extends Logic = Logic>({
|
||||
let unmount: () => void
|
||||
|
||||
beforeEach(async () => {
|
||||
window.POSTHOG_APP_CONTEXT = {
|
||||
current_team: { id: MOCK_TEAM_ID },
|
||||
...window.POSTHOG_APP_CONTEXT,
|
||||
} as unknown as AppContext
|
||||
posthog.init('no token', {
|
||||
api_host: 'borked',
|
||||
test: true,
|
||||
@ -45,5 +51,6 @@ export function initKeaTestLogic<L extends Logic = Logic>({
|
||||
unmount()
|
||||
await expectLogic(logic).toFinishAllListeners()
|
||||
}
|
||||
delete window.POSTHOG_APP_CONTEXT
|
||||
})
|
||||
}
|
||||
|
@ -189,7 +189,11 @@ export const actionsTabLogic = kea<actionsTabLogicType<ActionFormInstance>>({
|
||||
Insights
|
||||
</a>{' '}
|
||||
-{' '}
|
||||
<a href={`${apiURL}/action/${response.id}`} target="_blank" rel="noreferrer noopener">
|
||||
<a
|
||||
href={`${apiURL}/projects/@current/actions/${response.id}`}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
Actions
|
||||
</a>
|
||||
</>
|
||||
|
@ -1,4 +1,4 @@
|
||||
// /api/event/?event=$autocapture&properties[pathname]=/docs/introduction/what-is-kea
|
||||
// /api/projects/@current/events/?event=$autocapture&properties[pathname]=/docs/introduction/what-is-kea
|
||||
import { kea } from 'kea'
|
||||
import { encodeParams } from 'kea-router'
|
||||
import { currentPageLogic } from '~/toolbar/stats/currentPageLogic'
|
||||
|
@ -20,7 +20,7 @@ export const featureFlagsLogic = kea<featureFlagsLogicType>({
|
||||
[] as CombinedFeatureFlagAndOverrideType[],
|
||||
{
|
||||
getUserFlags: async (_, breakpoint) => {
|
||||
const response = await toolbarFetch('/api/feature_flag/my_flags')
|
||||
const response = await toolbarFetch('/api/projects/@current/feature_flags/my_flags')
|
||||
breakpoint()
|
||||
if (!response.ok) {
|
||||
return []
|
||||
|
@ -42,7 +42,7 @@ router = DefaultRouterPlusPlus()
|
||||
|
||||
# Legacy endpoints shared (to be removed eventually)
|
||||
router.register(r"annotation", annotation.LegacyAnnotationsViewSet) # Should be completely unused now
|
||||
router.register(r"feature_flag", feature_flag.LegacyFeatureFlagViewSet)
|
||||
router.register(r"feature_flag", feature_flag.LegacyFeatureFlagViewSet) # Should be completely unused now
|
||||
router.register(r"dashboard", dashboard.LegacyDashboardsViewSet)
|
||||
router.register(r"dashboard_item", dashboard.LegacyDashboardItemsViewSet)
|
||||
router.register(r"plugin_config", plugin.LegacyPluginConfigViewSet)
|
||||
@ -112,8 +112,8 @@ if is_clickhouse_enabled():
|
||||
|
||||
# Legacy endpoints CH (to be removed eventually)
|
||||
router.register(r"action", LegacyClickhouseActionsViewSet, basename="action") # Should be completely unused now
|
||||
router.register(r"event", LegacyClickhouseEventsViewSet, basename="event")
|
||||
router.register(r"insight", LegacyClickhouseInsightsViewSet, basename="insight")
|
||||
router.register(r"event", LegacyClickhouseEventsViewSet, basename="event") # Should be completely unused now
|
||||
router.register(r"insight", LegacyClickhouseInsightsViewSet, basename="insight") # Should be completely unused now
|
||||
router.register(r"person", LegacyClickhousePersonViewSet, basename="person")
|
||||
router.register(r"paths", LegacyClickhousePathsViewSet, basename="paths")
|
||||
router.register(r"element", LegacyClickhouseElementViewSet, basename="element")
|
||||
@ -131,10 +131,10 @@ if is_clickhouse_enabled():
|
||||
)
|
||||
else:
|
||||
# Legacy endpoints PG (to be removed eventually)
|
||||
router.register(r"insight", insight.LegacyInsightViewSet)
|
||||
router.register(r"insight", insight.LegacyInsightViewSet) # Should be completely unused now
|
||||
router.register(r"action", action.LegacyActionViewSet) # Should be completely unused now
|
||||
router.register(r"person", person.LegacyPersonViewSet)
|
||||
router.register(r"event", event.LegacyEventViewSet)
|
||||
router.register(r"event", event.LegacyEventViewSet) # Should be completely unused now
|
||||
router.register(r"paths", paths.LegacyPathsViewSet, basename="paths")
|
||||
router.register(r"element", element.LegacyElementViewSet)
|
||||
router.register(r"cohort", cohort.LegacyCohortViewSet)
|
||||
|
@ -280,7 +280,7 @@ class EventViewSet(StructuredViewSetMixin, mixins.RetrieveModelMixin, mixins.Lis
|
||||
return [{"name": convert_property_value(value)} for value in flattened]
|
||||
|
||||
# ******************************************
|
||||
# /event/sessions
|
||||
# /events/sessions
|
||||
#
|
||||
# params:
|
||||
# - pagination: (dict) Object containing information about pagination (offset, last page info)
|
||||
@ -306,7 +306,7 @@ class EventViewSet(StructuredViewSetMixin, mixins.RetrieveModelMixin, mixins.Lis
|
||||
return Response({"result": SessionsListEvents().run(filter=filter, team=self.team)})
|
||||
|
||||
# ******************************************
|
||||
# /event/session_recording
|
||||
# /events/session_recording
|
||||
# params:
|
||||
# - session_recording_id: (string) id of the session recording
|
||||
# - save_view: (boolean) save view of the recording
|
||||
|
@ -188,17 +188,17 @@ class InsightViewSet(StructuredViewSetMixin, viewsets.ModelViewSet):
|
||||
|
||||
# ******************************************
|
||||
# Calculated Insight Endpoints
|
||||
# /insight/trend
|
||||
# /insight/session
|
||||
# /insight/funnel
|
||||
# /insight/retention
|
||||
# /insight/path
|
||||
# /projects/:id/insights/trend
|
||||
# /projects/:id/insights/session
|
||||
# /projects/:id/insights/funnel
|
||||
# /projects/:id/insights/retention
|
||||
# /projects/:id/insights/path
|
||||
#
|
||||
# Request parameteres and caching are handled here and passed onto respective .queries classes
|
||||
# ******************************************
|
||||
|
||||
# ******************************************
|
||||
# /insight/trend
|
||||
# /projects/:id/insights/trend
|
||||
#
|
||||
# params:
|
||||
# - from_dashboard: (string) determines trend is being retrieved from dashboard item to update dashboard_item metadata
|
||||
@ -230,7 +230,7 @@ class InsightViewSet(StructuredViewSetMixin, viewsets.ModelViewSet):
|
||||
return {"result": result}
|
||||
|
||||
# ******************************************
|
||||
# /insight/session
|
||||
# /projects/:id/insights/session
|
||||
#
|
||||
# params:
|
||||
# - session: (string: avg, dist) specifies session type
|
||||
@ -246,7 +246,7 @@ class InsightViewSet(StructuredViewSetMixin, viewsets.ModelViewSet):
|
||||
return {"result": result}
|
||||
|
||||
# ******************************************
|
||||
# /insight/funnel
|
||||
# /projects/:id/insights/funnel
|
||||
# The funnel endpoint is asynchronously processed. When a request is received, the endpoint will
|
||||
# call an async task with an id that can be continually polled for 3 minutes.
|
||||
#
|
||||
@ -291,7 +291,7 @@ class InsightViewSet(StructuredViewSetMixin, viewsets.ModelViewSet):
|
||||
return {"result": result}
|
||||
|
||||
# ******************************************
|
||||
# /insight/retention
|
||||
# /projects/:id/insights/retention
|
||||
# params:
|
||||
# - start_entity: (dict) specifies id and type of the entity to focus retention on
|
||||
# - **shared filter types
|
||||
@ -312,7 +312,7 @@ class InsightViewSet(StructuredViewSetMixin, viewsets.ModelViewSet):
|
||||
return {"result": result}
|
||||
|
||||
# ******************************************
|
||||
# /insight/path
|
||||
# /projects/:id/insights/path
|
||||
# params:
|
||||
# - start: (string) specifies the name of the starting property or element
|
||||
# - request_type: (string: $pageview, $autocapture, $screen, custom_event) specifies the path type
|
||||
|
@ -125,7 +125,7 @@ class TestDashboard(APIBaseTest):
|
||||
|
||||
# cache results
|
||||
response = self.client.get(
|
||||
"/api/insight/trend/?events=%s&properties=%s"
|
||||
f"/api/projects/{self.team.id}/insights/trend/?events=%s&properties=%s"
|
||||
% (json.dumps(filter_dict["events"]), json.dumps(filter_dict["properties"]))
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
@ -404,7 +404,7 @@ class TestDashboard(APIBaseTest):
|
||||
dashboard=dashboard, filters=filter.to_dict(), team=self.team,
|
||||
)
|
||||
self.client.get(
|
||||
"/api/insight/trend/?events=%s&properties=%s&date_from=-7d"
|
||||
f"/api/projects/{self.team.id}/insights/trend/?events=%s&properties=%s&date_from=-7d"
|
||||
% (json.dumps(filter_dict["events"]), json.dumps(filter_dict["properties"]))
|
||||
)
|
||||
patch_response = self.client.patch(
|
||||
@ -414,7 +414,7 @@ class TestDashboard(APIBaseTest):
|
||||
|
||||
# cache results
|
||||
response = self.client.get(
|
||||
"/api/insight/trend/?events=%s&properties=%s&date_from=-24h"
|
||||
f"/api/projects/{self.team.id}/insights/trend/?events=%s&properties=%s&date_from=-24h"
|
||||
% (json.dumps(filter_dict["events"]), json.dumps(filter_dict["properties"]))
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
@ -426,7 +426,7 @@ class TestDashboard(APIBaseTest):
|
||||
def test_invalid_properties(self):
|
||||
properties = "invalid_json"
|
||||
|
||||
response = self.client.get(f"/api/insight/trend/?properties={properties}")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/insights/trend/?properties={properties}")
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertDictEqual(
|
||||
|
@ -59,7 +59,7 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
expected_queries += 7
|
||||
|
||||
with self.assertNumQueries(expected_queries):
|
||||
response = self.client.get("/api/event/?distinct_id=2").json()
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events/?distinct_id=2").json()
|
||||
self.assertEqual(
|
||||
response["results"][0]["person"],
|
||||
{"distinct_ids": ["2"], "is_identified": True, "properties": {"email": "tim@posthog.com"}},
|
||||
@ -84,7 +84,7 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
expected_queries += 4 # PostHog event, PostHog event, PostHog person, PostHog person distinct ID
|
||||
|
||||
with self.assertNumQueries(expected_queries):
|
||||
response = self.client.get("/api/event/?event=event_name").json()
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events/?event=event_name").json()
|
||||
self.assertEqual(response["results"][0]["event"], "event_name")
|
||||
|
||||
def test_filter_events_by_properties(self):
|
||||
@ -104,13 +104,14 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
|
||||
with self.assertNumQueries(expected_queries):
|
||||
response = self.client.get(
|
||||
"/api/event/?properties=%s" % (json.dumps([{"key": "$browser", "value": "Safari"}]))
|
||||
f"/api/projects/{self.team.id}/events/?properties=%s"
|
||||
% (json.dumps([{"key": "$browser", "value": "Safari"}]))
|
||||
).json()
|
||||
self.assertEqual(response["results"][0]["id"], event2.pk)
|
||||
|
||||
properties = "invalid_json"
|
||||
|
||||
response = self.client.get(f"/api/event/?properties={properties}")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events/?properties={properties}")
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertDictEqual(
|
||||
@ -145,7 +146,7 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
with self.settings(USE_PRECALCULATED_CH_COHORT_PEOPLE=True): # Normally this is False in tests
|
||||
with freeze_time("2020-01-04T13:01:01Z"):
|
||||
response = self.client.get(
|
||||
"/api/event/?properties=%s"
|
||||
f"/api/projects/{self.team.id}/events/?properties=%s"
|
||||
% (json.dumps([{"key": "id", "value": cohort1.id, "type": "cohort"}]))
|
||||
).json()
|
||||
|
||||
@ -164,12 +165,12 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
event="random event", team=self.team, distinct_id="some-other-one", properties={"$ip": "8.8.8.8"}
|
||||
)
|
||||
|
||||
response = self.client.get(f"/api/event/?person_id={person.pk}").json()
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events/?person_id={person.pk}").json()
|
||||
self.assertEqual(len(response["results"]), 2)
|
||||
self.assertEqual(response["results"][0]["elements"], [])
|
||||
|
||||
def test_filter_by_nonexisting_person(self):
|
||||
response = self.client.get(f"/api/event/?person_id=5555555555")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events/?person_id=5555555555")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.json()["results"]), 0)
|
||||
|
||||
@ -222,7 +223,7 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
team=self.team,
|
||||
properties={"random_prop": "don't include", "some other prop": "with some text"},
|
||||
)
|
||||
response = self.client.get("/api/event/values/?key=custom_event").json()
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events/values/?key=custom_event").json()
|
||||
self.assertListEqual(sorted(events), sorted([event["name"] for event in response]))
|
||||
|
||||
def test_event_property_values(self):
|
||||
@ -274,7 +275,7 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
|
||||
team2 = Organization.objects.bootstrap(None)[2]
|
||||
event_factory(distinct_id="bla", event="random event", team=team2, properties={"random_prop": "abcd"})
|
||||
response = self.client.get("/api/event/values/?key=random_prop").json()
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events/values/?key=random_prop").json()
|
||||
|
||||
keys = [resp["name"].replace(" ", "") for resp in response]
|
||||
self.assertCountEqual(
|
||||
@ -293,10 +294,14 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
)
|
||||
self.assertEqual(len(response), 9)
|
||||
|
||||
response = self.client.get("/api/event/values/?key=random_prop&value=qw").json()
|
||||
response = self.client.get(
|
||||
f"/api/projects/{self.team.id}/events/values/?key=random_prop&value=qw"
|
||||
).json()
|
||||
self.assertEqual(response[0]["name"], "qwerty")
|
||||
|
||||
response = self.client.get("/api/event/values/?key=random_prop&value=6").json()
|
||||
response = self.client.get(
|
||||
f"/api/projects/{self.team.id}/events/values/?key=random_prop&value=6"
|
||||
).json()
|
||||
self.assertEqual(response[0]["name"], "565")
|
||||
|
||||
def test_before_and_after(self):
|
||||
@ -317,20 +322,24 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
ActionStep.objects.create(action=action, event="sign up")
|
||||
action.calculate_events()
|
||||
|
||||
response = self.client.get("/api/event/?after=2020-01-09T00:00:00.000Z&action_id=%s" % action.pk).json()
|
||||
response = self.client.get(
|
||||
f"/api/projects/{self.team.id}/events/?after=2020-01-09T00:00:00.000Z&action_id=%s" % action.pk
|
||||
).json()
|
||||
self.assertEqual(len(response["results"]), 1)
|
||||
self.assertEqual(response["results"][0]["id"], event1.pk)
|
||||
|
||||
response = self.client.get("/api/event/?before=2020-01-09T00:00:00.000Z&action_id=%s" % action.pk).json()
|
||||
response = self.client.get(
|
||||
f"/api/projects/{self.team.id}/events/?before=2020-01-09T00:00:00.000Z&action_id=%s" % action.pk
|
||||
).json()
|
||||
self.assertEqual(len(response["results"]), 1)
|
||||
self.assertEqual(response["results"][0]["id"], event2.pk)
|
||||
|
||||
# without action
|
||||
response = self.client.get("/api/event/?after=2020-01-09T00:00:00.000Z").json()
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events/?after=2020-01-09T00:00:00.000Z").json()
|
||||
self.assertEqual(len(response["results"]), 1)
|
||||
self.assertEqual(response["results"][0]["id"], event1.pk)
|
||||
|
||||
response = self.client.get("/api/event/?before=2020-01-09T00:00:00.000Z").json()
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events/?before=2020-01-09T00:00:00.000Z").json()
|
||||
self.assertEqual(len(response["results"]), 2)
|
||||
self.assertEqual(response["results"][0]["id"], event2.pk)
|
||||
self.assertEqual(response["results"][1]["id"], event3.pk)
|
||||
@ -344,9 +353,11 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
distinct_id="1",
|
||||
timestamp=timezone.now() - relativedelta(months=11) + relativedelta(days=idx, seconds=idx),
|
||||
)
|
||||
response = self.client.get("/api/event/?distinct_id=1").json()
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events/?distinct_id=1").json()
|
||||
self.assertEqual(len(response["results"]), 100)
|
||||
self.assertIn("http://testserver/api/event/?distinct_id=1&before=", response["next"])
|
||||
self.assertIn(
|
||||
f"http://testserver/api/projects/{self.team.id}/events/?distinct_id=1&before=", response["next"]
|
||||
)
|
||||
|
||||
page2 = self.client.get(response["next"]).json()
|
||||
from posthog.utils import is_clickhouse_enabled
|
||||
@ -367,13 +378,13 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
action = Action.objects.create(team=self.team)
|
||||
action.calculate_events()
|
||||
|
||||
response = self.client.get("/api/event/?action_id=%s" % action.pk)
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events/?action_id=%s" % action.pk)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.json()["results"]), 0)
|
||||
|
||||
def test_get_single_action(self):
|
||||
event1 = event_factory(team=self.team, event="sign up", distinct_id="2", properties={"key": "test_val"})
|
||||
response = self.client.get("/api/event/%s/" % event1.id)
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events/%s/" % event1.id)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.json()["event"], "sign up")
|
||||
self.assertEqual(response.json()["properties"], {"key": "test_val"})
|
||||
@ -394,11 +405,13 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
event_factory(team=self.team, event="4th action", distinct_id="2", properties={"$os": "Windows 95"})
|
||||
|
||||
with freeze_time("2012-01-15T04:01:34.000Z"):
|
||||
response = self.client.get("/api/event/sessions/",).json()
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events/sessions/",).json()
|
||||
|
||||
self.assertEqual(len(response["result"]), 2)
|
||||
|
||||
response = self.client.get("/api/event/sessions/?date_from=2012-01-14&date_to=2012-01-15",).json()
|
||||
response = self.client.get(
|
||||
f"/api/projects/{self.team.id}/events/sessions/?date_from=2012-01-14&date_to=2012-01-15",
|
||||
).json()
|
||||
self.assertEqual(len(response["result"]), 4)
|
||||
|
||||
# 4 sessions were already created above
|
||||
@ -406,7 +419,9 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
with freeze_time(relative_date_parse("2012-01-15T04:01:34.000Z") + relativedelta(hours=i)):
|
||||
event_factory(team=self.team, event="action {}".format(i), distinct_id=str(i + 3))
|
||||
|
||||
response = self.client.get("/api/event/sessions/?date_from=2012-01-14&date_to=2012-01-17",).json()
|
||||
response = self.client.get(
|
||||
f"/api/projects/{self.team.id}/events/sessions/?date_from=2012-01-14&date_to=2012-01-17",
|
||||
).json()
|
||||
self.assertEqual(len(response["result"]), SESSIONS_LIST_DEFAULT_LIMIT)
|
||||
self.assertIsNone(response.get("pagination"))
|
||||
|
||||
@ -414,16 +429,18 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
with freeze_time(relative_date_parse("2012-01-15T04:01:34.000Z") + relativedelta(hours=i + 46)):
|
||||
event_factory(team=self.team, event="action {}".format(i), distinct_id=str(i + 49))
|
||||
|
||||
response = self.client.get("/api/event/sessions/?date_from=2012-01-14&date_to=2012-01-17",).json()
|
||||
response = self.client.get(
|
||||
f"/api/projects/{self.team.id}/events/sessions/?date_from=2012-01-14&date_to=2012-01-17",
|
||||
).json()
|
||||
self.assertEqual(len(response["result"]), SESSIONS_LIST_DEFAULT_LIMIT)
|
||||
self.assertIsNotNone(response["pagination"])
|
||||
|
||||
def test_events_nonexistent_cohort_handling(self):
|
||||
response_nonexistent_property = self.client.get(
|
||||
f"/api/event/sessions/?filters={json.dumps([{'type':'property','key':'abc','value':'xyz'}])}"
|
||||
f"/api/projects/{self.team.id}/events/sessions/?filters={json.dumps([{'type':'property','key':'abc','value':'xyz'}])}"
|
||||
).json()
|
||||
response_nonexistent_cohort = self.client.get(
|
||||
f"/api/event/sessions/?filters={json.dumps([{'type':'cohort','key':'id','value':2137}])}"
|
||||
f"/api/projects/{self.team.id}/events/sessions/?filters={json.dumps([{'type':'cohort','key':'id','value':2137}])}"
|
||||
).json()
|
||||
|
||||
self.assertEqual(response_nonexistent_property, response_nonexistent_cohort) # Both cases just empty
|
||||
@ -447,7 +464,9 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
event_factory(team=self.team, event="4th action", distinct_id="2", properties={"$os": "Windows 95"})
|
||||
|
||||
with freeze_time("2012-01-15T04:01:34.000Z"):
|
||||
response_person_1 = self.client.get("/api/event/sessions/?distinct_id=1",).json()
|
||||
response_person_1 = self.client.get(
|
||||
f"/api/projects/{self.team.id}/events/sessions/?distinct_id=1",
|
||||
).json()
|
||||
|
||||
self.assertEqual(len(response_person_1["result"]), 1)
|
||||
|
||||
@ -458,7 +477,7 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
with freeze_time("2012-01-15T04:01:44.000Z"):
|
||||
event_factory(team=self.team, event="5th action", distinct_id="2", properties={"$os": "Windows 95"})
|
||||
with freeze_time("2012-01-15T04:01:34.000Z"):
|
||||
response = self.client.get("/api/event/").json()
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events/").json()
|
||||
self.assertEqual(len(response["results"]), 1)
|
||||
|
||||
def test_session_events(self):
|
||||
@ -482,7 +501,7 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
event_factory(team=self.team, event="4th action", distinct_id="1", properties={"$os": "Mac OS X"})
|
||||
|
||||
response = self.client.get(
|
||||
f"/api/event/session_events?distinct_id=1&date_from=2012-01-14T03:25:34&date_to=2012-01-15T04:00:00"
|
||||
f"/api/projects/{self.team.id}/events/session_events?distinct_id=1&date_from=2012-01-14T03:25:34&date_to=2012-01-15T04:00:00"
|
||||
).json()
|
||||
self.assertEqual(len(response["result"]), 2)
|
||||
self.assertEqual(response["result"][0]["event"], "2nd action")
|
||||
@ -493,7 +512,7 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
with freeze_time("2012-01-15T04:01:34.000Z"):
|
||||
for _ in range(12):
|
||||
event_factory(team=self.team, event="5th action", distinct_id="2", properties={"$os": "Windows 95"})
|
||||
response = self.client.get("/api/event.csv?limit=5")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events.csv?limit=5")
|
||||
self.assertEqual(
|
||||
len(response.content.splitlines()), 6, "CSV export should return up to limit=5 events (+ headers row)",
|
||||
)
|
||||
@ -503,7 +522,7 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
with freeze_time("2012-01-15T04:01:34.000Z"):
|
||||
for _ in range(12):
|
||||
event_factory(team=self.team, event="5th action", distinct_id="2", properties={"$os": "Windows 95"})
|
||||
response = self.client.get("/api/event.csv")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events.csv")
|
||||
self.assertEqual(
|
||||
len(response.content.splitlines()),
|
||||
11,
|
||||
@ -515,7 +534,7 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
with freeze_time("2012-01-15T04:01:34.000Z"):
|
||||
for _ in range(12):
|
||||
event_factory(team=self.team, event="5th action", distinct_id="2", properties={"$os": "Windows 95"})
|
||||
response = self.client.get("/api/event.csv")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events.csv")
|
||||
self.assertEqual(
|
||||
len(response.content.splitlines()),
|
||||
11,
|
||||
@ -527,7 +546,7 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
with freeze_time("2012-01-15T04:01:34.000Z"):
|
||||
for _ in range(12):
|
||||
event_factory(team=self.team, event="5th action", distinct_id="2", properties={"$os": "Windows 95"})
|
||||
response = self.client.get("/api/event.csv?limit=100")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events.csv?limit=100")
|
||||
self.assertEqual(
|
||||
len(response.content.splitlines()),
|
||||
11,
|
||||
@ -553,15 +572,15 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
else:
|
||||
event_factory(team=self.team, event="event", distinct_id="1", timestamp=timezone.now(), id=event_id)
|
||||
|
||||
response = self.client.get(f"/api/event/{event_id}",)
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events/{event_id}",)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.json()["event"], "event")
|
||||
|
||||
response = self.client.get(f"/api/event/123456",)
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events/123456",)
|
||||
# EE will inform the user the ID passed is not a valid UUID
|
||||
self.assertIn(response.status_code, [status.HTTP_404_NOT_FOUND, status.HTTP_400_BAD_REQUEST])
|
||||
|
||||
response = self.client.get(f"/api/event/im_a_string_not_an_integer",)
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events/im_a_string_not_an_integer",)
|
||||
self.assertIn(response.status_code, [status.HTTP_404_NOT_FOUND, status.HTTP_400_BAD_REQUEST])
|
||||
|
||||
def test_limit(self):
|
||||
@ -586,10 +605,10 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
event="$pageview", team=self.team, distinct_id="some-other-one", properties={"$ip": "8.8.8.8"}
|
||||
)
|
||||
|
||||
response = self.client.get("/api/event/?limit=1").json()
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events/?limit=1").json()
|
||||
self.assertEqual(1, len(response["results"]))
|
||||
|
||||
response = self.client.get("/api/event/?limit=2").json()
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/events/?limit=2").json()
|
||||
self.assertEqual(2, len(response["results"]))
|
||||
|
||||
def test_get_events_with_specified_token(self):
|
||||
@ -602,16 +621,22 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
event1 = event_factory(team=self.team, event="sign up", distinct_id="2", properties={"key": "test_val"})
|
||||
event2 = event_factory(team=user2.team, event="sign up", distinct_id="2", properties={"key": "test_val"})
|
||||
|
||||
response_team1 = self.client.get(f"/api/event/{event1.id}/")
|
||||
response_team1_token = self.client.get(f"/api/event/{event1.id}/", data={"token": self.team.api_token})
|
||||
response_team1 = self.client.get(f"/api/projects/{self.team.id}/events/{event1.id}/")
|
||||
response_team1_token = self.client.get(
|
||||
f"/api/projects/{self.team.id}/events/{event1.id}/", data={"token": self.team.api_token}
|
||||
)
|
||||
|
||||
response_team2_event1 = self.client.get(f"/api/event/{event1.id}/", data={"token": user2.team.api_token})
|
||||
response_team2_event1 = self.client.get(
|
||||
f"/api/projects/{self.team.id}/events/{event1.id}/", data={"token": user2.team.api_token}
|
||||
)
|
||||
|
||||
# The feature being tested here is usually used with personal API token auth,
|
||||
# but logging in works the same way and is more to the point in the test
|
||||
self.client.force_login(user2)
|
||||
|
||||
response_team2_event2 = self.client.get(f"/api/event/{event2.id}/", data={"token": user2.team.api_token})
|
||||
response_team2_event2 = self.client.get(
|
||||
f"/api/projects/{self.team.id}/events/{event2.id}/", data={"token": user2.team.api_token}
|
||||
)
|
||||
|
||||
self.assertEqual(response_team1.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response_team1_token.status_code, status.HTTP_200_OK)
|
||||
@ -620,7 +645,7 @@ def factory_test_event_api(event_factory, person_factory, _):
|
||||
self.assertEqual(response_team2_event1.status_code, status.HTTP_403_FORBIDDEN)
|
||||
self.assertEqual(response_team2_event2.status_code, status.HTTP_200_OK)
|
||||
|
||||
response_invalid_token = self.client.get(f"/api/event?token=invalid")
|
||||
response_invalid_token = self.client.get(f"/api/projects/{self.team.id}/events?token=invalid")
|
||||
self.assertEqual(response_invalid_token.status_code, 401)
|
||||
|
||||
return TestEvents
|
||||
|
@ -19,7 +19,9 @@ class TestFeatureFlag(APIBaseTest):
|
||||
def test_cant_create_flag_with_duplicate_key(self):
|
||||
count = FeatureFlag.objects.count()
|
||||
# Make sure the endpoint works with and without the trailing slash
|
||||
response = self.client.post("/api/feature_flag", {"name": "Beta feature", "key": "red_button"})
|
||||
response = self.client.post(
|
||||
f"/api/projects/{self.team.id}/feature_flags", {"name": "Beta feature", "key": "red_button"}
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertEqual(
|
||||
response.json(),
|
||||
@ -37,7 +39,8 @@ class TestFeatureFlag(APIBaseTest):
|
||||
team=self.team, rollout_percentage=50, name="some feature", key="some-feature", created_by=self.user,
|
||||
)
|
||||
response = self.client.patch(
|
||||
f"/api/feature_flag/{another_feature_flag.pk}", {"name": "Beta feature", "key": "red_button"},
|
||||
f"/api/projects/{self.team.id}/feature_flags/{another_feature_flag.pk}",
|
||||
{"name": "Beta feature", "key": "red_button"},
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertEqual(
|
||||
@ -54,7 +57,8 @@ class TestFeatureFlag(APIBaseTest):
|
||||
|
||||
# Try updating the existing one
|
||||
response = self.client.patch(
|
||||
f"/api/feature_flag/{self.feature_flag.id}/", {"name": "Beta feature 3", "key": "red_button"},
|
||||
f"/api/projects/{self.team.id}/feature_flags/{self.feature_flag.id}/",
|
||||
{"name": "Beta feature 3", "key": "red_button"},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.feature_flag.refresh_from_db()
|
||||
@ -62,7 +66,7 @@ class TestFeatureFlag(APIBaseTest):
|
||||
|
||||
def test_is_simple_flag(self):
|
||||
feature_flag = self.client.post(
|
||||
"/api/feature_flag/",
|
||||
f"/api/projects/{self.team.id}/feature_flags/",
|
||||
data={
|
||||
"name": "Beta feature",
|
||||
"key": "beta-feature",
|
||||
@ -86,7 +90,7 @@ class TestFeatureFlag(APIBaseTest):
|
||||
def test_create_feature_flag(self, mock_capture):
|
||||
|
||||
response = self.client.post(
|
||||
"/api/feature_flag/",
|
||||
f"/api/projects/{self.team.id}/feature_flags/",
|
||||
{"name": "Alpha feature", "key": "alpha-feature", "filters": {"groups": [{"rollout_percentage": 50}]}},
|
||||
format="json",
|
||||
)
|
||||
@ -112,7 +116,9 @@ class TestFeatureFlag(APIBaseTest):
|
||||
@patch("posthoganalytics.capture")
|
||||
def test_create_minimal_feature_flag(self, mock_capture):
|
||||
|
||||
response = self.client.post("/api/feature_flag/", {"key": "omega-feature"}, format="json")
|
||||
response = self.client.post(
|
||||
f"/api/projects/{self.team.id}/feature_flags/", {"key": "omega-feature"}, format="json"
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
self.assertEqual(response.json()["key"], "omega-feature")
|
||||
self.assertEqual(response.json()["name"], "")
|
||||
@ -139,7 +145,7 @@ class TestFeatureFlag(APIBaseTest):
|
||||
def test_create_multivariate_feature_flag(self, mock_capture):
|
||||
|
||||
response = self.client.post(
|
||||
"/api/feature_flag/",
|
||||
f"/api/projects/{self.team.id}/feature_flags/",
|
||||
{
|
||||
"name": "Multivariate feature",
|
||||
"key": "multivariate-feature",
|
||||
@ -177,7 +183,7 @@ class TestFeatureFlag(APIBaseTest):
|
||||
|
||||
def test_cant_create_multivariate_feature_flag_with_variant_rollout_lt_100(self):
|
||||
response = self.client.post(
|
||||
"/api/feature_flag/",
|
||||
f"/api/projects/{self.team.id}/feature_flags/",
|
||||
{
|
||||
"name": "Multivariate feature",
|
||||
"key": "multivariate-feature",
|
||||
@ -202,7 +208,7 @@ class TestFeatureFlag(APIBaseTest):
|
||||
|
||||
def test_cant_create_multivariate_feature_flag_with_variant_rollout_gt_100(self):
|
||||
response = self.client.post(
|
||||
"/api/feature_flag/",
|
||||
f"/api/projects/{self.team.id}/feature_flags/",
|
||||
{
|
||||
"name": "Multivariate feature",
|
||||
"key": "multivariate-feature",
|
||||
@ -227,7 +233,7 @@ class TestFeatureFlag(APIBaseTest):
|
||||
|
||||
def test_cant_create_feature_flag_without_key(self):
|
||||
count = FeatureFlag.objects.count()
|
||||
response = self.client.post("/api/feature_flag/", format="json")
|
||||
response = self.client.post(f"/api/projects/{self.team.id}/feature_flags/", format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertEqual(
|
||||
response.json(),
|
||||
@ -240,7 +246,7 @@ class TestFeatureFlag(APIBaseTest):
|
||||
instance = self.feature_flag
|
||||
|
||||
response = self.client.patch(
|
||||
f"/api/feature_flag/{instance.pk}",
|
||||
f"/api/projects/{self.team.id}/feature_flags/{instance.pk}",
|
||||
{
|
||||
"name": "Updated name",
|
||||
"filters": {
|
||||
@ -283,7 +289,7 @@ class TestFeatureFlag(APIBaseTest):
|
||||
self.client.force_login(new_user)
|
||||
|
||||
with patch("posthoganalytics.capture") as mock_capture:
|
||||
response = self.client.delete(f"/api/feature_flag/{instance.pk}/")
|
||||
response = self.client.delete(f"/api/projects/{self.team.id}/feature_flags/{instance.pk}/")
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
||||
self.assertFalse(FeatureFlag.objects.filter(pk=instance.pk).exists())
|
||||
@ -305,10 +311,10 @@ class TestFeatureFlag(APIBaseTest):
|
||||
|
||||
@patch("posthoganalytics.capture")
|
||||
def test_cannot_delete_feature_flag_on_another_team(self, mock_capture):
|
||||
_, _, user = User.objects.bootstrap("Test", "team2@posthog.com", None)
|
||||
self.client.force_login(user)
|
||||
_, other_team, other_user = User.objects.bootstrap("Test", "team2@posthog.com", None)
|
||||
self.client.force_login(other_user)
|
||||
|
||||
response = self.client.delete(f"/api/feature_flag/{self.feature_flag.pk}/")
|
||||
response = self.client.delete(f"/api/projects/{other_team.id}/feature_flags/{self.feature_flag.pk}/")
|
||||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
||||
self.assertTrue(FeatureFlag.objects.filter(pk=self.feature_flag.pk).exists())
|
||||
|
||||
@ -321,20 +327,22 @@ class TestFeatureFlag(APIBaseTest):
|
||||
assert self.team is not None
|
||||
self.assertNotEqual(user.team.id, self.team.id)
|
||||
|
||||
response_team_1 = self.client.get(f"/api/feature_flag")
|
||||
response_team_1_token = self.client.get(f"/api/feature_flag?token={user.team.api_token}")
|
||||
response_team_2 = self.client.get(f"/api/feature_flag?token={self.team.api_token}")
|
||||
response_team_1 = self.client.get(f"/api/projects/@current/feature_flags")
|
||||
response_team_1_token = self.client.get(f"/api/projects/@current/feature_flags?token={user.team.api_token}")
|
||||
response_team_2 = self.client.get(f"/api/projects/@current/feature_flags?token={self.team.api_token}")
|
||||
|
||||
self.assertEqual(response_team_1.json(), response_team_1_token.json())
|
||||
self.assertNotEqual(response_team_1.json(), response_team_2.json())
|
||||
|
||||
response_invalid_token = self.client.get(f"/api/feature_flag?token=invalid")
|
||||
response_invalid_token = self.client.get(f"/api/projects/@current/feature_flags?token=invalid")
|
||||
self.assertEqual(response_invalid_token.status_code, 401)
|
||||
|
||||
def test_creating_a_feature_flag_with_same_team_and_key_after_deleting(self):
|
||||
FeatureFlag.objects.create(team=self.team, created_by=self.user, key="alpha-feature", deleted=True)
|
||||
|
||||
response = self.client.post("/api/feature_flag/", {"name": "Alpha feature", "key": "alpha-feature"})
|
||||
response = self.client.post(
|
||||
f"/api/projects/{self.team.id}/feature_flags/", {"name": "Alpha feature", "key": "alpha-feature"}
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
instance = FeatureFlag.objects.get(id=response.json()["id"])
|
||||
self.assertEqual(instance.key, "alpha-feature")
|
||||
@ -344,7 +352,9 @@ class TestFeatureFlag(APIBaseTest):
|
||||
|
||||
instance = FeatureFlag.objects.create(team=self.team, created_by=self.user, key="beta-feature")
|
||||
|
||||
response = self.client.patch(f"/api/feature_flag/{instance.pk}", {"key": "alpha-feature",}, format="json",)
|
||||
response = self.client.patch(
|
||||
f"/api/projects/{self.team.id}/feature_flags/{instance.pk}", {"key": "alpha-feature",}, format="json",
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
instance.refresh_from_db()
|
||||
self.assertEqual(instance.key, "alpha-feature")
|
||||
@ -352,7 +362,7 @@ class TestFeatureFlag(APIBaseTest):
|
||||
@patch("posthoganalytics.capture")
|
||||
def test_my_flags(self, mock_capture):
|
||||
self.client.post(
|
||||
"/api/feature_flag/",
|
||||
f"/api/projects/{self.team.id}/feature_flags/",
|
||||
{
|
||||
"name": "Alpha feature",
|
||||
"key": "alpha-feature",
|
||||
@ -375,7 +385,7 @@ class TestFeatureFlag(APIBaseTest):
|
||||
distinct_id_user.distinct_id = "distinct_id"
|
||||
distinct_id_user.save()
|
||||
self.client.force_login(distinct_id_user)
|
||||
response = self.client.get("/api/feature_flag/my_flags")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/feature_flags/my_flags")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
response_data = response.json()
|
||||
self.assertEqual(len(response_data), 2)
|
||||
@ -395,7 +405,7 @@ class TestFeatureFlag(APIBaseTest):
|
||||
distinct_id_0_user.distinct_id = "distinct_id_0"
|
||||
distinct_id_0_user.save()
|
||||
self.client.force_login(distinct_id_0_user)
|
||||
response = self.client.get("/api/feature_flag/my_flags")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/feature_flags/my_flags")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
response_data = response.json()
|
||||
self.assertEqual(len(response_data), 2)
|
||||
@ -432,7 +442,7 @@ class TestFeatureFlag(APIBaseTest):
|
||||
)
|
||||
)
|
||||
|
||||
response = self.client.get("/api/feature_flag/my_flags")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/feature_flags/my_flags")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
response_data = response.json()
|
||||
|
||||
@ -456,7 +466,7 @@ class TestFeatureFlag(APIBaseTest):
|
||||
{"feature_flag": feature_flag_instance.id, "override_value": "hey-hey"},
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
response = self.client.get("/api/feature_flag/my_flags")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/feature_flags/my_flags")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
response_data = response.json()
|
||||
first_flag = response_data[0]
|
||||
@ -469,7 +479,7 @@ class TestFeatureFlag(APIBaseTest):
|
||||
{"feature_flag": feature_flag_instance.id, "override_value": "new-override"},
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
response = self.client.get("/api/feature_flag/my_flags")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/feature_flags/my_flags")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
response_data = response.json()
|
||||
first_flag = response_data[0]
|
||||
@ -489,7 +499,7 @@ class TestFeatureFlag(APIBaseTest):
|
||||
{"feature_flag": feature_flag_instance.id, "override_value": "hey-hey"},
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
response = self.client.get("/api/feature_flag/my_flags")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/feature_flags/my_flags")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
response_data = response.json()
|
||||
first_flag = response_data[0]
|
||||
@ -501,7 +511,7 @@ class TestFeatureFlag(APIBaseTest):
|
||||
response = self.client.delete(f"/api/projects/@current/feature_flag_overrides/{existing_override_id}",)
|
||||
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
||||
|
||||
response = self.client.get("/api/feature_flag/my_flags")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/feature_flags/my_flags")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
response_data = response.json()
|
||||
first_flag = response_data[0]
|
||||
|
@ -39,7 +39,7 @@ def insight_test_factory(event_factory, person_factory):
|
||||
# create without user
|
||||
DashboardItem.objects.create(filters=Filter(data=filter_dict).to_dict(), team=self.team)
|
||||
|
||||
response = self.client.get("/api/insight/", data={"user": "true"}).json()
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/insights/", data={"user": "true"}).json()
|
||||
|
||||
self.assertEqual(len(response["results"]), 1)
|
||||
|
||||
@ -61,7 +61,9 @@ def insight_test_factory(event_factory, person_factory):
|
||||
# create without user
|
||||
DashboardItem.objects.create(filters=Filter(data=filter_dict).to_dict(), team=self.team)
|
||||
|
||||
response = self.client.get("/api/insight/", data={"saved": "true", "user": "true"})
|
||||
response = self.client.get(
|
||||
f"/api/projects/{self.team.id}/insights/", data={"saved": "true", "user": "true"}
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
self.assertEqual(len(response.json()["results"]), 1)
|
||||
@ -85,7 +87,7 @@ def insight_test_factory(event_factory, person_factory):
|
||||
# create without user
|
||||
DashboardItem.objects.create(filters=Filter(data=filter_dict).to_dict(), team=self.team)
|
||||
|
||||
response = self.client.get("/api/insight/?favorited=true&user=true")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/insights/?favorited=true&user=true")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
self.assertEqual(len(response.json()["results"]), 1)
|
||||
@ -106,7 +108,7 @@ def insight_test_factory(event_factory, person_factory):
|
||||
filters=Filter(data=filter_dict).to_dict(), team=new_team, short_id="12345678",
|
||||
)
|
||||
|
||||
response = self.client.get("/api/insight/?short_id=12345678")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/insights/?short_id=12345678")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
self.assertEqual(len(response.json()["results"]), 1)
|
||||
@ -129,7 +131,7 @@ def insight_test_factory(event_factory, person_factory):
|
||||
filters=Filter(data=filter_dict).to_dict(), team=self.team, saved=True,
|
||||
)
|
||||
|
||||
response = self.client.get("/api/insight/?basic=true")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/insights/?basic=true")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
self.assertEqual(len(response.json()["results"]), 2)
|
||||
@ -153,7 +155,7 @@ def insight_test_factory(event_factory, person_factory):
|
||||
def test_create_insight_items(self):
|
||||
# Make sure the endpoint works with and without the trailing slash
|
||||
response = self.client.post(
|
||||
"/api/insight",
|
||||
f"/api/projects/{self.team.id}/insights",
|
||||
data={
|
||||
"filters": {
|
||||
"events": [{"id": "$pageview"}],
|
||||
@ -175,7 +177,7 @@ def insight_test_factory(event_factory, person_factory):
|
||||
def test_update_insight(self):
|
||||
insight = DashboardItem.objects.create(team=self.team, name="special insight", created_by=self.user,)
|
||||
response = self.client.patch(
|
||||
f"/api/insight/{insight.id}",
|
||||
f"/api/projects/{self.team.id}/insights/{insight.id}",
|
||||
{
|
||||
"name": "insight new name",
|
||||
"tags": ["official", "engineering"],
|
||||
@ -207,7 +209,7 @@ def insight_test_factory(event_factory, person_factory):
|
||||
["Custom filter", 100, "", " ", None], ["Custom filter", "100", None, None, None]
|
||||
):
|
||||
response = self.client.patch(
|
||||
f"/api/insight/{insight.id}",
|
||||
f"/api/projects/{self.team.id}/insights/{insight.id}",
|
||||
{"filters": {"events": [{"id": "$pageview", "custom_name": custom_name}]}},
|
||||
)
|
||||
|
||||
@ -223,7 +225,7 @@ def insight_test_factory(event_factory, person_factory):
|
||||
dashboard = Dashboard.objects.create(name="My Dashboard", team=self.team)
|
||||
|
||||
response = self.client.post(
|
||||
"/api/insight",
|
||||
f"/api/projects/{self.team.id}/insights",
|
||||
data={
|
||||
"filters": {
|
||||
"insight": "FUNNELS",
|
||||
@ -278,7 +280,7 @@ def insight_test_factory(event_factory, person_factory):
|
||||
|
||||
with freeze_time("2012-01-15T04:01:34.000Z"):
|
||||
response = self.client.get(
|
||||
"/api/insight/trend/?events={}".format(json.dumps([{"id": "$pageview"}]))
|
||||
f"/api/projects/{self.team.id}/insights/trend/?events={json.dumps([{'id': '$pageview'}])}"
|
||||
).json()
|
||||
|
||||
self.assertEqual(response["result"][0]["count"], 2)
|
||||
@ -286,10 +288,10 @@ def insight_test_factory(event_factory, person_factory):
|
||||
|
||||
def test_nonexistent_cohort_is_handled(self):
|
||||
response_nonexistent_property = self.client.get(
|
||||
f"/api/insight/trend/?events={json.dumps([{'id': '$pageview'}])}&properties={json.dumps([{'type':'event','key':'foo','value':'barabarab'}])}"
|
||||
f"/api/projects/{self.team.id}/insights/trend/?events={json.dumps([{'id': '$pageview'}])}&properties={json.dumps([{'type':'event','key':'foo','value':'barabarab'}])}"
|
||||
)
|
||||
response_nonexistent_cohort = self.client.get(
|
||||
f"/api/insight/trend/?events={json.dumps([{'id': '$pageview'}])}&properties={json.dumps([{'type':'cohort','key':'id','value':2137}])}"
|
||||
f"/api/projects/{self.team.id}/insights/trend/?events={json.dumps([{'id': '$pageview'}])}&properties={json.dumps([{'type':'cohort','key':'id','value':2137}])}"
|
||||
) # This should not throw an error, just act like there's no event matches
|
||||
|
||||
response_nonexistent_property_data = response_nonexistent_property.json()
|
||||
@ -304,10 +306,10 @@ def insight_test_factory(event_factory, person_factory):
|
||||
whatever_cohort_without_match_groups = Cohort.objects.create(team=self.team)
|
||||
|
||||
response_nonexistent_property = self.client.get(
|
||||
f"/api/insight/trend/?events={json.dumps([{'id': '$pageview'}])}&properties={json.dumps([{'type':'event','key':'foo','value':'barabarab'}])}"
|
||||
f"/api/projects/{self.team.id}/insights/trend/?events={json.dumps([{'id': '$pageview'}])}&properties={json.dumps([{'type':'event','key':'foo','value':'barabarab'}])}"
|
||||
)
|
||||
response_cohort_without_match_groups = self.client.get(
|
||||
f"/api/insight/trend/?events={json.dumps([{'id':'$pageview'}])}&properties={json.dumps([{'type':'cohort','key':'id','value':whatever_cohort_without_match_groups.pk}])}"
|
||||
f"/api/projects/{self.team.id}/insights/trend/?events={json.dumps([{'id':'$pageview'}])}&properties={json.dumps([{'type':'cohort','key':'id','value':whatever_cohort_without_match_groups.pk}])}"
|
||||
) # This should not throw an error, just act like there's no event matches
|
||||
|
||||
self.assertEqual(response_nonexistent_property.status_code, 200)
|
||||
@ -333,10 +335,10 @@ def insight_test_factory(event_factory, person_factory):
|
||||
|
||||
with self.settings(USE_PRECALCULATED_CH_COHORT_PEOPLE=True): # Normally this is False in tests
|
||||
response_user_property = self.client.get(
|
||||
f"/api/insight/trend/?events={json.dumps([{'id': '$pageview'}])}&properties={json.dumps([{'type':'person','key':'foo','value':'bar'}])}"
|
||||
f"/api/projects/{self.team.id}/insights/trend/?events={json.dumps([{'id': '$pageview'}])}&properties={json.dumps([{'type':'person','key':'foo','value':'bar'}])}"
|
||||
)
|
||||
response_precalculated_cohort = self.client.get(
|
||||
f"/api/insight/trend/?events={json.dumps([{'id':'$pageview'}])}&properties={json.dumps([{'type':'cohort','key':'id','value':113}])}"
|
||||
f"/api/projects/{self.team.id}/insights/trend/?events={json.dumps([{'id':'$pageview'}])}&properties={json.dumps([{'type':'cohort','key':'id','value':113}])}"
|
||||
)
|
||||
|
||||
self.assertEqual(response_precalculated_cohort.status_code, 200)
|
||||
@ -356,7 +358,7 @@ def insight_test_factory(event_factory, person_factory):
|
||||
|
||||
with freeze_time("2012-01-15T04:01:34.000Z"):
|
||||
response = self.client.get(
|
||||
"/api/insight/trend/",
|
||||
f"/api/projects/{self.team.id}/insights/trend/",
|
||||
data={
|
||||
"events": json.dumps([{"id": "$pageview"}]),
|
||||
"breakdown": "$some_property",
|
||||
@ -396,10 +398,11 @@ def insight_test_factory(event_factory, person_factory):
|
||||
)
|
||||
|
||||
get_response = self.client.get(
|
||||
"/api/insight/path", data={"properties": json.dumps([{"key": "test", "value": "val"}]),}
|
||||
f"/api/projects/{self.team.id}/insights/path",
|
||||
data={"properties": json.dumps([{"key": "test", "value": "val"}]),},
|
||||
).json()
|
||||
post_response = self.client.post(
|
||||
"/api/insight/path", {"properties": [{"key": "test", "value": "val"}],}
|
||||
f"/api/projects/{self.team.id}/insights/path", {"properties": [{"key": "test", "value": "val"}],}
|
||||
).json()
|
||||
self.assertEqual(len(get_response["result"]), 1)
|
||||
self.assertEqual(len(post_response["result"]), 1)
|
||||
@ -409,7 +412,7 @@ def insight_test_factory(event_factory, person_factory):
|
||||
event_factory(team=self.team, event="user signed up", distinct_id="1")
|
||||
event_factory(team=self.team, event="user did things", distinct_id="1")
|
||||
response = self.client.post(
|
||||
"/api/insight/funnel/",
|
||||
f"/api/projects/{self.team.id}/insights/funnel/",
|
||||
{
|
||||
"events": [
|
||||
{"id": "user signed up", "type": "events", "order": 0},
|
||||
@ -434,14 +437,7 @@ def insight_test_factory(event_factory, person_factory):
|
||||
event_factory(team=self.team, event="user signed up", distinct_id="1")
|
||||
event_factory(team=self.team, event="user did things", distinct_id="1")
|
||||
response = self.client.get(
|
||||
"/api/insight/funnel/?funnel_window_days=14&events={}".format(
|
||||
json.dumps(
|
||||
[
|
||||
{"id": "user signed up", "type": "events", "order": 0},
|
||||
{"id": "user did things", "type": "events", "order": 1},
|
||||
]
|
||||
)
|
||||
)
|
||||
f"/api/projects/{self.team.id}/insights/funnel/?funnel_window_days=14&events={json.dumps([{'id': 'user signed up', 'type': 'events', 'order': 0},{'id': 'user did things', 'type': 'events', 'order': 1},])}"
|
||||
).json()
|
||||
|
||||
# clickhouse funnels don't have a loading system
|
||||
@ -461,7 +457,7 @@ def insight_test_factory(event_factory, person_factory):
|
||||
event_factory(
|
||||
team=self.team, event="$pageview", distinct_id="person1", timestamp=timezone.now() - timedelta(days=10),
|
||||
)
|
||||
response = self.client.get("/api/insight/retention/",).json()
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/insights/retention/",).json()
|
||||
|
||||
self.assertEqual(len(response["result"]), 11)
|
||||
|
||||
@ -486,14 +482,15 @@ def insight_test_factory(event_factory, person_factory):
|
||||
|
||||
events_filter = json.dumps([{"id": "$pageview"}])
|
||||
|
||||
response_team1 = self.client.get(f"/api/insight/trend/?events={events_filter}")
|
||||
response_team1 = self.client.get(f"/api/projects/{self.team.id}/insights/trend/?events={events_filter}")
|
||||
response_team1_token = self.client.get(
|
||||
f"/api/insight/trend/?events={events_filter}&token={self.user.team.api_token}"
|
||||
f"/api/projects/{self.team.id}/insights/trend/?events={events_filter}&token={self.user.team.api_token}"
|
||||
)
|
||||
|
||||
self.client.force_login(user2)
|
||||
response_team2 = self.client.get(
|
||||
f"/api/insight/trend/?events={events_filter}", data={"token": user2.team.api_token}
|
||||
f"/api/projects/{self.team.id}/insights/trend/?events={events_filter}",
|
||||
data={"token": user2.team.api_token},
|
||||
)
|
||||
|
||||
self.assertEqual(response_team1.status_code, 200)
|
||||
@ -501,7 +498,7 @@ def insight_test_factory(event_factory, person_factory):
|
||||
self.assertEqual(response_team1.json()["result"], response_team1_token.json()["result"])
|
||||
self.assertNotEqual(len(response_team1.json()["result"]), len(response_team2.json()["result"]))
|
||||
|
||||
response_invalid_token = self.client.get(f"/api/insight/trend?token=invalid")
|
||||
response_invalid_token = self.client.get(f"/api/projects/{self.team.id}/insights/trend?token=invalid")
|
||||
self.assertEqual(response_invalid_token.status_code, 401)
|
||||
|
||||
return TestInsight
|
||||
|
@ -57,7 +57,7 @@ def factory_test_session_recordings_api(session_recording_event_factory):
|
||||
self.create_snapshot("user2", "2", base_time + relativedelta(seconds=20))
|
||||
self.create_snapshot("user", "1", base_time + relativedelta(seconds=30))
|
||||
|
||||
response = self.client.get("/api/projects/@current/session_recordings")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/session_recordings")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
response_data = response.json()
|
||||
self.assertEqual(len(response_data["results"]), 2)
|
||||
@ -86,7 +86,7 @@ def factory_test_session_recordings_api(session_recording_event_factory):
|
||||
self.create_snapshot("user", "1", now() - relativedelta(days=1), team_id=another_team.pk)
|
||||
self.create_snapshot("user", "2", now() - relativedelta(days=1))
|
||||
|
||||
response = self.client.get("/api/projects/@current/session_recordings")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/session_recordings")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
response_data = response.json()
|
||||
self.assertEqual(len(response_data["results"]), 1)
|
||||
@ -101,7 +101,7 @@ def factory_test_session_recordings_api(session_recording_event_factory):
|
||||
)
|
||||
self.create_snapshot("d1", "1", base_time)
|
||||
self.create_snapshot("d2", "2", base_time + relativedelta(seconds=30))
|
||||
response = self.client.get("/api/projects/@current/session_recordings")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/session_recordings")
|
||||
response_data = response.json()
|
||||
self.assertEqual(len(response_data["results"]), 2)
|
||||
self.assertEqual(response_data["results"][0]["person"]["id"], p.pk)
|
||||
@ -112,7 +112,7 @@ def factory_test_session_recordings_api(session_recording_event_factory):
|
||||
SessionRecordingViewed.objects.create(team=self.team, user=self.user, session_id="1")
|
||||
self.create_snapshot("u1", "1", base_time)
|
||||
self.create_snapshot("u1", "2", base_time + relativedelta(seconds=30))
|
||||
response = self.client.get("/api/projects/@current/session_recordings")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/session_recordings")
|
||||
response_data = response.json()
|
||||
self.assertEqual(len(response_data["results"]), 2)
|
||||
self.assertEqual(response_data["results"][0]["id"], "2")
|
||||
@ -128,7 +128,7 @@ def factory_test_session_recordings_api(session_recording_event_factory):
|
||||
base_time = now() - relativedelta(days=1)
|
||||
self.create_snapshot("d1", session_recording_id, base_time)
|
||||
self.create_snapshot("d1", session_recording_id, base_time + relativedelta(seconds=30))
|
||||
response = self.client.get(f"/api/projects/@current/session_recordings/{session_recording_id}")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/session_recordings/{session_recording_id}")
|
||||
response_data = response.json()
|
||||
self.assertEqual(
|
||||
response_data["result"]["snapshots"][0], {"timestamp": base_time.timestamp() * 1000, "type": 2}
|
||||
@ -147,7 +147,7 @@ def factory_test_session_recordings_api(session_recording_event_factory):
|
||||
for s in range(num_snapshots):
|
||||
self.create_snapshot("user", "1", base_time)
|
||||
|
||||
response = self.client.get("/api/projects/@current/session_recordings/1")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/session_recordings/1")
|
||||
response_data = response.json()
|
||||
self.assertEqual(len(response_data["result"]["snapshots"]), num_snapshots)
|
||||
|
||||
@ -168,7 +168,7 @@ def factory_test_session_recordings_api(session_recording_event_factory):
|
||||
"user", chunked_session_id, start_time + relativedelta(seconds=s), s, chunk_size
|
||||
)
|
||||
|
||||
next_url = f"/api/projects/@current/session_recordings/{chunked_session_id}"
|
||||
next_url = f"/api/projects/{self.team.id}/session_recordings/{chunked_session_id}"
|
||||
|
||||
for i in range(expected_num_requests):
|
||||
response = self.client.get(next_url)
|
||||
@ -199,41 +199,41 @@ def factory_test_session_recordings_api(session_recording_event_factory):
|
||||
"user", chunked_session_id, base_time + relativedelta(seconds=s), s, chunk_size
|
||||
)
|
||||
|
||||
response = self.client.get(f"/api/projects/@current/session_recordings/{chunked_session_id}")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/session_recordings/{chunked_session_id}")
|
||||
response_data = response.json()
|
||||
self.assertEqual(len(response_data["result"]["snapshots"]), num_snapshots)
|
||||
|
||||
def test_single_session_recording_doesnt_leak_teams(self):
|
||||
another_team = Team.objects.create(organization=self.organization)
|
||||
self.create_snapshot("user", "id_no_team_leaking", now() - relativedelta(days=1), team_id=another_team.pk)
|
||||
response = self.client.get("/api/projects/@current/session_recordings/id_no_team_leaking")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/session_recordings/id_no_team_leaking")
|
||||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
||||
|
||||
def test_session_recording_with_no_person(self):
|
||||
self.create_snapshot("d1", "id_no_person", now() - relativedelta(days=1))
|
||||
response = self.client.get("/api/projects/@current/session_recordings/id_no_person")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/session_recordings/id_no_person")
|
||||
response_data = response.json()
|
||||
self.assertEqual(response_data["result"]["person"], None)
|
||||
|
||||
def test_session_recording_doesnt_exist(self):
|
||||
response = self.client.get("/api/projects/@current/session_recordings/non_existent_id")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/session_recordings/non_existent_id")
|
||||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
||||
|
||||
def test_setting_viewed_state_of_session_recording(self):
|
||||
self.create_snapshot("u1", "1", now() - relativedelta(days=1))
|
||||
response = self.client.get("/api/projects/@current/session_recordings")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/session_recordings")
|
||||
response_data = response.json()
|
||||
# Make sure it starts not viewed
|
||||
self.assertEqual(response_data["results"][0]["viewed"], False)
|
||||
|
||||
response = self.client.get("/api/projects/@current/session_recordings/1")
|
||||
response = self.client.get("/api/projects/@current/session_recordings")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/session_recordings/1")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/session_recordings")
|
||||
response_data = response.json()
|
||||
# Make sure it remains not viewed
|
||||
self.assertEqual(response_data["results"][0]["viewed"], False)
|
||||
|
||||
response = self.client.get("/api/projects/@current/session_recordings/1?save_view=True")
|
||||
response = self.client.get("/api/projects/@current/session_recordings")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/session_recordings/1?save_view=True")
|
||||
response = self.client.get(f"/api/projects/{self.team.id}/session_recordings")
|
||||
response_data = response.json()
|
||||
# Make sure the query param sets it to viewed
|
||||
self.assertEqual(response_data["results"][0]["viewed"], True)
|
||||
|
@ -16,6 +16,7 @@ from posthog.api.test.test_event_definition import (
|
||||
create_user,
|
||||
)
|
||||
from posthog.api.test.test_retention import identify
|
||||
from posthog.models.team import Team
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@ -83,6 +84,7 @@ def test_includes_only_intervals_within_range(client: Client):
|
||||
}
|
||||
],
|
||||
),
|
||||
team=team,
|
||||
)
|
||||
assert trends == {
|
||||
"is_cached": False,
|
||||
@ -124,9 +126,9 @@ class TrendsRequest:
|
||||
events: List[Dict[str, Any]]
|
||||
|
||||
|
||||
def get_trends(client, request: TrendsRequest):
|
||||
def get_trends(client, request: TrendsRequest, team: Team):
|
||||
return client.get(
|
||||
"/api/insight/trend/",
|
||||
f"/api/projects/{team.id}/insights/trend/",
|
||||
data={
|
||||
"date_from": request.date_from,
|
||||
"date_to": request.date_to,
|
||||
@ -140,7 +142,7 @@ def get_trends(client, request: TrendsRequest):
|
||||
)
|
||||
|
||||
|
||||
def get_trends_ok(client: Client, request: TrendsRequest):
|
||||
response = get_trends(client=client, request=request)
|
||||
def get_trends_ok(client: Client, request: TrendsRequest, team: Team):
|
||||
response = get_trends(client=client, request=request, team=team)
|
||||
assert response.status_code == 200, response.content
|
||||
return response.json()
|
||||
|
@ -17,7 +17,9 @@ from posthog.test.base import BaseTest
|
||||
|
||||
|
||||
def session_recording_test_factory(session_recording, filter_sessions, event_factory):
|
||||
def create_recording_request_and_filter(session_recording_id, limit=None, offset=None) -> Tuple[Request, Filter]:
|
||||
def create_recording_request_and_filter(
|
||||
team_id, session_recording_id, limit=None, offset=None
|
||||
) -> Tuple[Request, Filter]:
|
||||
params = {}
|
||||
if limit:
|
||||
params["limit"] = limit
|
||||
@ -27,7 +29,8 @@ def session_recording_test_factory(session_recording, filter_sessions, event_fac
|
||||
build_req.META = {"HTTP_HOST": "www.testserver"}
|
||||
|
||||
req = Request(
|
||||
build_req, f"/api/event/session_recording?session_recording_id={session_recording_id}{urlencode(params)}"
|
||||
build_req,
|
||||
f"/api/projects/{team_id}/session_recordings?session_recording_id={session_recording_id}{urlencode(params)}",
|
||||
)
|
||||
return (req, Filter(request=req, data=params))
|
||||
|
||||
@ -43,7 +46,7 @@ def session_recording_test_factory(session_recording, filter_sessions, event_fac
|
||||
self.create_snapshot("user2", "2", now() + relativedelta(seconds=20))
|
||||
self.create_snapshot("user", "1", now() + relativedelta(seconds=30))
|
||||
|
||||
req, filt = create_recording_request_and_filter("1")
|
||||
req, filt = create_recording_request_and_filter(self.team.id, "1")
|
||||
session = session_recording(team=self.team, session_recording_id="1", request=req, filter=filt).run()
|
||||
self.assertEqual(
|
||||
session["snapshots"],
|
||||
@ -58,7 +61,7 @@ def session_recording_test_factory(session_recording, filter_sessions, event_fac
|
||||
|
||||
def test_query_run_with_no_such_session(self):
|
||||
|
||||
req, filt = create_recording_request_and_filter("xxx")
|
||||
req, filt = create_recording_request_and_filter(self.team.id, "xxx")
|
||||
session = session_recording(team=self.team, session_recording_id="xxx", request=req, filter=filt).run()
|
||||
self.assertEqual(
|
||||
session, {"snapshots": [], "person": None, "start_time": None, "next": None, "duration": 0}
|
||||
@ -174,7 +177,7 @@ def session_recording_test_factory(session_recording, filter_sessions, event_fac
|
||||
for s in range(num_snapshots + 1):
|
||||
self.create_chunked_snapshot("user", chunked_session_id, now() + relativedelta(seconds=s), s)
|
||||
|
||||
req, filt = create_recording_request_and_filter(chunked_session_id)
|
||||
req, filt = create_recording_request_and_filter(self.team.id, chunked_session_id)
|
||||
session = session_recording(
|
||||
team=self.team, session_recording_id=chunked_session_id, request=req, filter=filt
|
||||
).run()
|
||||
@ -195,7 +198,7 @@ def session_recording_test_factory(session_recording, filter_sessions, event_fac
|
||||
for s in range(200):
|
||||
self.create_chunked_snapshot("user", chunked_session_id, now() + relativedelta(seconds=s), s)
|
||||
|
||||
req, filt = create_recording_request_and_filter(chunked_session_id, limit)
|
||||
req, filt = create_recording_request_and_filter(self.team.id, chunked_session_id, limit)
|
||||
session = session_recording(
|
||||
team=self.team, session_recording_id=chunked_session_id, request=req, filter=filt
|
||||
).run()
|
||||
@ -223,7 +226,7 @@ def session_recording_test_factory(session_recording, filter_sessions, event_fac
|
||||
)
|
||||
|
||||
# A successful single session recording query will make {num_chunks} requests
|
||||
base_req, base_filter = create_recording_request_and_filter(chunked_session_id)
|
||||
base_req, base_filter = create_recording_request_and_filter(self.team.id, chunked_session_id)
|
||||
session = None
|
||||
|
||||
for i in range(expected_num_requests):
|
||||
@ -307,7 +310,7 @@ def session_recording_test_factory(session_recording, filter_sessions, event_fac
|
||||
)
|
||||
|
||||
# Do the thing
|
||||
req, filt = create_recording_request_and_filter(chunked_session_id)
|
||||
req, filt = create_recording_request_and_filter(self.team.id, chunked_session_id)
|
||||
session = session_recording(
|
||||
team=self.team, session_recording_id=chunked_session_id, request=req, filter=filt
|
||||
).run()
|
||||
|
@ -91,25 +91,27 @@ def paths_test_factory(paths, event_factory, person_factory):
|
||||
with freeze_time("2012-01-15T03:21:34.000Z"):
|
||||
date_from = now() - relativedelta(days=7)
|
||||
response = self.client.get(
|
||||
"/api/insight/path/?insight=PATHS&date_from=" + date_from.strftime("%Y-%m-%d")
|
||||
f"/api/projects/{self.team.id}/insights/path/?insight=PATHS&date_from="
|
||||
+ date_from.strftime("%Y-%m-%d")
|
||||
).json()
|
||||
self.assertEqual(len(response["result"]), 4)
|
||||
|
||||
date_to = now()
|
||||
response = self.client.get(
|
||||
"/api/insight/path/?insight=PATHS&date_to=" + date_to.strftime("%Y-%m-%d")
|
||||
f"/api/projects/{self.team.id}/insights/path/?insight=PATHS&date_to=" + date_to.strftime("%Y-%m-%d")
|
||||
).json()
|
||||
self.assertEqual(len(response["result"]), 4)
|
||||
|
||||
date_from = now() + relativedelta(days=7)
|
||||
response = self.client.get(
|
||||
"/api/insight/path/?insight=PATHS&date_from=" + date_from.strftime("%Y-%m-%d")
|
||||
f"/api/projects/{self.team.id}/insights/path/?insight=PATHS&date_from="
|
||||
+ date_from.strftime("%Y-%m-%d")
|
||||
).json()
|
||||
self.assertEqual(len(response["result"]), 0)
|
||||
|
||||
date_to = now() - relativedelta(days=7)
|
||||
response = self.client.get(
|
||||
"/api/insight/path/?insight=PATHS&date_to=" + date_to.strftime("%Y-%m-%d")
|
||||
f"/api/projects/{self.team.id}/insights/path/?insight=PATHS&date_to=" + date_to.strftime("%Y-%m-%d")
|
||||
).json()
|
||||
self.assertEqual(len(response["result"]), 0)
|
||||
|
||||
@ -348,7 +350,9 @@ def paths_test_factory(paths, event_factory, person_factory):
|
||||
properties={"$current_url": "/help"}, distinct_id="person_5b", event="$pageview", team=self.team,
|
||||
)
|
||||
|
||||
response = self.client.get("/api/insight/path/?type=%24pageview&start=%2Fpricing").json()
|
||||
response = self.client.get(
|
||||
f"/api/projects/{self.team.id}/insights/path/?type=%24pageview&start=%2Fpricing"
|
||||
).json()
|
||||
|
||||
filter = PathFilter(data={"path_type": "$pageview", "start_point": "/pricing"})
|
||||
response = paths(team=self.team, filter=filter).run(team=self.team, filter=filter,)
|
||||
|
Loading…
Reference in New Issue
Block a user