Skip to content

Commit

Permalink
Merge branch 'master' into distinct-id-tombstones
Browse files Browse the repository at this point in the history
  • Loading branch information
tkaemming authored Feb 14, 2024
2 parents e892876 + 8d467d2 commit 816e885
Show file tree
Hide file tree
Showing 501 changed files with 15,691 additions and 5,143 deletions.
1 change: 0 additions & 1 deletion .github/actions/run-backend-tests/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ runs:
run: echo "PYTEST_ARGS=--snapshot-update" >> $GITHUB_ENV # We can only update snapshots within the PostHog org

# Tests

- name: Run FOSS tests
if: ${{ inputs.segment == 'FOSS' }}
env:
Expand Down
17 changes: 12 additions & 5 deletions .github/workflows/container-images-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,6 @@ jobs:
with:
fetch-depth: 2

- name: Override git.py
run: >
echo "def get_git_commit(): return '${GITHUB_SHA}'" > posthog/git.py
echo "def get_git_branch(): return '${GITHUB_REF_NAME}'" >> posthog/git.py
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

Expand Down Expand Up @@ -97,6 +92,18 @@ jobs:
"context": ${{ toJson(github) }}
}
- name: Trigger PostHog Cloud deployment from Charts
uses: mvasigh/dispatch-action@main
with:
token: ${{ steps.deployer.outputs.token }}
repo: charts
owner: PostHog
event_type: posthog_deploy
message: |
{
"image_tag": "${{ steps.build.outputs.digest }}"
}
- name: Check for changes in plugins directory
id: check_changes_plugins
run: |
Expand Down
7 changes: 4 additions & 3 deletions .run/Celery.run.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
<env name="DEBUG" value="1" />
<env name="CLICKHOUSE_SECURE" value="False" />
<env name="KAFKA_HOSTS" value="localhost" />
<env name="DATABASE_URL" value="postgres://posthog:posthog@localhost:5432/posthog" />
<env name="DEBUG" value="1" />
<env name="KAFKA_HOSTS" value="localhost" />
<env name="PYTHONUNBUFFERED" value="1" />
<env name="SKIP_SERVICE_VERSION_REQUIREMENTS" value="1" />
<env name="REPLAY_EMBEDDINGS_ALLOWED_TEAM" value="1,2,3" />
</envs>
<option name="SDK_HOME" value="$PROJECT_DIR$/env/bin/python" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/env/bin" />
Expand Down
2 changes: 1 addition & 1 deletion bin/celery-queues.env
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
# Important: Add new queues to make Celery consume tasks from them.

# NOTE: Keep in sync with posthog/tasks/utils.py
CELERY_WORKER_QUEUES=celery,stats,email,insight_export,insight_refresh,analytics_queries,exports,subscription_delivery,usage_reports
CELERY_WORKER_QUEUES=celery,stats,email,insight_export,insight_refresh,analytics_queries,exports,subscription_delivery,usage_reports,session_replay_embeddings
12 changes: 3 additions & 9 deletions cypress/e2e/billingv2.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@ describe('Billing', () => {

cy.get('[data-attr=more-button]').first().click()
cy.contains('.LemonButton', 'Unsubscribe').click()
cy.get('.LemonModal__content h3').should(
'contain',
'Why are you unsubscribing from Product analytics + data stack?'
)
cy.get('.LemonModal h3').should('contain', 'Why are you unsubscribing from Product analytics + data stack?')
cy.get('[data-attr=unsubscribe-reason-survey-textarea]').type('Product analytics')
cy.contains('.LemonModal .LemonButton', 'Unsubscribe').click()

Expand All @@ -45,17 +42,14 @@ describe('Billing', () => {
it('Unsubscribe survey text area maintains unique state between product types', () => {
cy.get('[data-attr=more-button]').first().click()
cy.contains('.LemonButton', 'Unsubscribe').click()
cy.get('.LemonModal__content h3').should(
'contain',
'Why are you unsubscribing from Product analytics + data stack?'
)
cy.get('.LemonModal h3').should('contain', 'Why are you unsubscribing from Product analytics + data stack?')

cy.get('[data-attr=unsubscribe-reason-survey-textarea]').type('Product analytics')
cy.contains('.LemonModal .LemonButton', 'Cancel').click()

cy.get('[data-attr=more-button]').eq(1).click()
cy.contains('.LemonButton', 'Unsubscribe').click()
cy.get('.LemonModal__content h3').should('contain', 'Why are you unsubscribing from Session replay?')
cy.get('.LemonModal h3').should('contain', 'Why are you unsubscribing from Session replay?')
cy.get('[data-attr=unsubscribe-reason-survey-textarea]').type('Session replay')
cy.contains('.LemonModal .LemonButton', 'Cancel').click()

Expand Down
8 changes: 4 additions & 4 deletions cypress/productAnalytics/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ export const savedInsights = {
}

export function interceptInsightLoad(insightType: string): string {
cy.intercept('GET', /api\/projects\/\d+\/insights\/trend\/\?.*/).as('loadNewTrendsInsight')
cy.intercept('POST', /api\/projects\/\d+\/insights\/funnel\/?/).as('loadNewFunnelInsight')
cy.intercept('GET', /api\/projects\/\d+\/insights\/retention\/\?.*/).as('loadNewRetentionInsight')
cy.intercept('POST', /api\/projects\/\d+\/insights\/path\/?/).as('loadNewPathsInsight')
cy.intercept('POST', /api\/projects\/\d+\/insights\/trend\//).as('loadNewTrendsInsight')
cy.intercept('POST', /api\/projects\/\d+\/insights\/funnel\//).as('loadNewFunnelInsight')
cy.intercept('POST', /api\/projects\/\d+\/insights\/retention\//).as('loadNewRetentionInsight')
cy.intercept('POST', /api\/projects\/\d+\/insights\/path\//).as('loadNewPathsInsight')
cy.intercept('POST', /api\/projects\/\d+\/query\//).as('loadNewQueryInsight')

let networkInterceptAlias: string = ''
Expand Down
2 changes: 0 additions & 2 deletions cypress/support/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ beforeEach(() => {
Cypress.env('POSTHOG_PROPERTY_CURRENT_TEST_FULL_TITLE', Cypress.currentTest.titlePath.join(' > '))
Cypress.env('POSTHOG_PROPERTY_GITHUB_ACTION_RUN_URL', process.env.GITHUB_ACTION_RUN_URL)

cy.intercept('api/prompts/my_prompts/', { sequences: [], state: {} })

cy.intercept('https://app.posthog.com/decide/*', (req) =>
req.reply(
decideResponse({
Expand Down
12 changes: 3 additions & 9 deletions ee/api/billing.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from django.http import HttpResponse
from django.shortcuts import redirect
from rest_framework import serializers, status, viewsets
from rest_framework.authentication import BasicAuthentication, SessionAuthentication
from rest_framework.decorators import action
from rest_framework.exceptions import NotFound, PermissionDenied, ValidationError
from rest_framework.request import Request
Expand All @@ -16,7 +15,7 @@
from ee.billing.billing_manager import BillingManager, build_billing_token
from ee.models import License
from ee.settings import BILLING_SERVICE_URL
from posthog.auth import PersonalAPIKeyAuthentication
from posthog.api.routing import TeamAndOrgViewSetMixin
from posthog.cloud_utils import get_cached_instance_license
from posthog.models import Organization

Expand All @@ -34,14 +33,9 @@ class LicenseKeySerializer(serializers.Serializer):
license = serializers.CharField()


class BillingViewset(viewsets.GenericViewSet):
class BillingViewset(TeamAndOrgViewSetMixin, viewsets.GenericViewSet):
serializer_class = BillingSerializer

authentication_classes = [
PersonalAPIKeyAuthentication,
SessionAuthentication,
BasicAuthentication,
]
derive_current_team_from_user_only = True

def list(self, request: Request, *args: Any, **kwargs: Any) -> Response:
license = get_cached_instance_license()
Expand Down
13 changes: 4 additions & 9 deletions ee/api/dashboard_collaborator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@

from django.db import IntegrityError
from rest_framework import exceptions, mixins, serializers, viewsets
from rest_framework.permissions import SAFE_METHODS, BasePermission, IsAuthenticated
from rest_framework.permissions import SAFE_METHODS, BasePermission
from rest_framework.request import Request

from ee.models.dashboard_privilege import DashboardPrivilege
from posthog.api.routing import StructuredViewSetMixin
from posthog.api.routing import TeamAndOrgViewSetMixin
from posthog.api.shared import UserBasicSerializer
from posthog.models import Dashboard, User
from posthog.permissions import TeamMemberAccessPermission
from posthog.user_permissions import UserPermissions, UserPermissionsSerializerMixin


Expand Down Expand Up @@ -83,17 +82,13 @@ def create(self, validated_data):


class DashboardCollaboratorViewSet(
StructuredViewSetMixin,
TeamAndOrgViewSetMixin,
mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet,
):
permission_classes = [
IsAuthenticated,
TeamMemberAccessPermission,
CanEditDashboardCollaborator,
]
permission_classes = [CanEditDashboardCollaborator]
pagination_class = None
queryset = DashboardPrivilege.objects.select_related("dashboard").filter(user__is_active=True)
lookup_field = "user__uuid"
Expand Down
9 changes: 5 additions & 4 deletions ee/api/explicit_team_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from rest_framework.permissions import IsAuthenticated

from ee.models.explicit_team_membership import ExplicitTeamMembership
from posthog.api.routing import StructuredViewSetMixin
from posthog.api.routing import TeamAndOrgViewSetMixin
from posthog.api.shared import UserBasicSerializer
from posthog.models.organization import OrganizationMembership
from posthog.models.team import Team
Expand Down Expand Up @@ -101,8 +101,7 @@ def validate(self, attrs):
return attrs


class ExplicitTeamMemberViewSet(StructuredViewSetMixin, viewsets.ModelViewSet):
permission_classes = [IsAuthenticated, TeamMemberStrictManagementPermission]
class ExplicitTeamMemberViewSet(TeamAndOrgViewSetMixin, viewsets.ModelViewSet):
pagination_class = None
queryset = ExplicitTeamMembership.objects.filter(parent_membership__user__is_active=True).select_related(
"team", "parent_membership", "parent_membership__user"
Expand All @@ -112,6 +111,8 @@ class ExplicitTeamMemberViewSet(StructuredViewSetMixin, viewsets.ModelViewSet):
serializer_class = ExplicitTeamMemberSerializer
include_in_docs = True

permission_classes = [IsAuthenticated, TeamMemberStrictManagementPermission]

def get_permissions(self):
if (
self.action == "destroy"
Expand All @@ -120,7 +121,7 @@ def get_permissions(self):
):
# Special case: allow already authenticated users to leave projects
return []
return super().get_permissions()
return [permission() for permission in self.permission_classes]

def get_object(self) -> ExplicitTeamMembership:
queryset = self.filter_queryset(self.get_queryset())
Expand Down
8 changes: 4 additions & 4 deletions ee/api/feature_flag_role_access.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from rest_framework import exceptions, mixins, serializers, viewsets
from rest_framework.permissions import SAFE_METHODS, BasePermission, IsAuthenticated
from rest_framework.permissions import SAFE_METHODS, BasePermission

from ee.api.role import RoleSerializer
from ee.models.feature_flag_role_access import FeatureFlagRoleAccess
from ee.models.organization_resource_access import OrganizationResourceAccess
from ee.models.role import Role
from posthog.api.feature_flag import FeatureFlagSerializer
from posthog.api.routing import StructuredViewSetMixin
from posthog.api.routing import TeamAndOrgViewSetMixin
from posthog.models import FeatureFlag
from posthog.models.organization import OrganizationMembership

Expand Down Expand Up @@ -66,14 +66,14 @@ def create(self, validated_data):


class FeatureFlagRoleAccessViewSet(
StructuredViewSetMixin,
TeamAndOrgViewSetMixin,
mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.DestroyModelMixin,
mixins.RetrieveModelMixin,
viewsets.GenericViewSet,
):
permission_classes = [IsAuthenticated, FeatureFlagRoleAccessPermissions]
permission_classes = [FeatureFlagRoleAccessPermissions]
serializer_class = FeatureFlagRoleAccessSerializer
queryset = FeatureFlagRoleAccess.objects.select_related("feature_flag")
filter_rewrite_rules = {"team_id": "feature_flag__team_id"}
Expand Down
14 changes: 2 additions & 12 deletions ee/api/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,10 @@

from django.conf import settings
from rest_framework import exceptions, serializers, viewsets
from rest_framework.permissions import IsAuthenticated

from ee.models.hook import Hook
from posthog.api.routing import StructuredViewSetMixin
from posthog.api.routing import TeamAndOrgViewSetMixin
from posthog.models.user import User
from posthog.permissions import (
OrganizationMemberPermissions,
TeamMemberAccessPermission,
)


class HookSerializer(serializers.ModelSerializer):
Expand All @@ -31,18 +26,13 @@ def validate_target(self, target):
return target


class HookViewSet(StructuredViewSetMixin, viewsets.ModelViewSet):
class HookViewSet(TeamAndOrgViewSetMixin, viewsets.ModelViewSet):
"""
Retrieve, create, update or destroy REST hooks.
"""

queryset = Hook.objects.all()
ordering = "-created_at"
permission_classes = [
IsAuthenticated,
OrganizationMemberPermissions,
TeamMemberAccessPermission,
]
serializer_class = HookSerializer

def perform_create(self, serializer):
Expand Down
12 changes: 3 additions & 9 deletions ee/api/organization_resource_access.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
from rest_framework import mixins, serializers, viewsets
from rest_framework.permissions import IsAuthenticated

from ee.api.role import RolePermissions
from ee.models.organization_resource_access import OrganizationResourceAccess
from posthog.api.routing import StructuredViewSetMixin
from posthog.permissions import OrganizationMemberPermissions
from posthog.api.routing import TeamAndOrgViewSetMixin


class OrganizationResourceAccessSerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -35,18 +33,14 @@ def create(self, validated_data):


class OrganizationResourceAccessViewSet(
StructuredViewSetMixin,
TeamAndOrgViewSetMixin,
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.CreateModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet,
):
permission_classes = [
IsAuthenticated,
OrganizationMemberPermissions,
RolePermissions,
]
permission_classes = [RolePermissions]
serializer_class = OrganizationResourceAccessSerializer
queryset = OrganizationResourceAccess.objects.all()
20 changes: 6 additions & 14 deletions ee/api/role.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@

from django.db import IntegrityError
from rest_framework import mixins, serializers, viewsets
from rest_framework.permissions import SAFE_METHODS, BasePermission, IsAuthenticated
from rest_framework.permissions import SAFE_METHODS, BasePermission

from ee.models.feature_flag_role_access import FeatureFlagRoleAccess
from ee.models.organization_resource_access import OrganizationResourceAccess
from ee.models.role import Role, RoleMembership
from posthog.api.routing import StructuredViewSetMixin
from posthog.api.routing import TeamAndOrgViewSetMixin
from posthog.api.shared import UserBasicSerializer
from posthog.models import OrganizationMembership
from posthog.models.feature_flag import FeatureFlag
from posthog.models.user import User
from posthog.permissions import OrganizationMemberPermissions


class RolePermissions(BasePermission):
Expand Down Expand Up @@ -86,19 +85,15 @@ def get_associated_flags(self, role: Role):


class RoleViewSet(
StructuredViewSetMixin,
TeamAndOrgViewSetMixin,
mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet,
):
permission_classes = [
IsAuthenticated,
OrganizationMemberPermissions,
RolePermissions,
]
permission_classes = [RolePermissions]
serializer_class = RoleSerializer
queryset = Role.objects.all()

Expand Down Expand Up @@ -132,16 +127,13 @@ def create(self, validated_data):


class RoleMembershipViewSet(
StructuredViewSetMixin,
TeamAndOrgViewSetMixin,
mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet,
):
permission_classes = [
IsAuthenticated,
RolePermissions,
]
permission_classes = [RolePermissions]
serializer_class = RoleMembershipSerializer
queryset = RoleMembership.objects.select_related("role")
filter_rewrite_rules = {"organization_id": "role__organization_id"}
Loading

0 comments on commit 816e885

Please sign in to comment.