Skip to content

Commit

Permalink
Merge branch 'master' into dn-feat/team-assignment
Browse files Browse the repository at this point in the history
  • Loading branch information
daibhin committed Jan 15, 2025
2 parents 9a84a34 + 73e86b0 commit 7ac3492
Show file tree
Hide file tree
Showing 21 changed files with 1,080 additions and 904 deletions.
1,675 changes: 906 additions & 769 deletions .flox/env/manifest.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions .flox/env/manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ libtool = { pkg-path = "libtool", pkg-group = "python" }
nodejs = { pkg-path = "nodejs_18", pkg-group = "nodejs" }
corepack = { pkg-path = "corepack", pkg-group = "nodejs" }
brotli = { pkg-path = "brotli", pkg-group = "nodejs" }
nodemon = { pkg-path = "nodemon", pkg-group = "nodejs", version = "3.1" }
# Rust toolchain (based on https://flox.dev/docs/cookbook/languages/rust/)
cargo.pkg-path = "cargo"
cargo.pkg-group = "rust-toolchain"
Expand Down
12 changes: 0 additions & 12 deletions ee/session_recordings/session_recording_playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,14 @@
from loginas.utils import is_impersonated_session
from rest_framework import request, response, serializers, viewsets
from posthog.api.utils import action
from rest_framework.exceptions import PermissionDenied

from posthog.api.forbid_destroy_model import ForbidDestroyModel
from posthog.api.routing import TeamAndOrgViewSetMixin
from posthog.api.shared import UserBasicSerializer
from posthog.constants import AvailableFeature
from posthog.models import (
SessionRecording,
SessionRecordingPlaylist,
SessionRecordingPlaylistItem,
Team,
User,
)
from posthog.models.activity_logging.activity_log import (
Expand All @@ -26,7 +23,6 @@
changes_between,
log_activity,
)
from posthog.models.team.team import check_is_feature_available_for_team
from posthog.models.utils import UUIDT
from posthog.rate_limit import (
ClickHouseBurstRateThrottle,
Expand Down Expand Up @@ -108,8 +104,6 @@ def create(self, validated_data: dict, *args, **kwargs) -> SessionRecordingPlayl
request = self.context["request"]
team = self.context["get_team"]()

self._check_can_create_playlist(team)

created_by = validated_data.pop("created_by", request.user)
playlist = SessionRecordingPlaylist.objects.create(
team=team,
Expand Down Expand Up @@ -158,12 +152,6 @@ def update(self, instance: SessionRecordingPlaylist, validated_data: dict, **kwa

return updated_playlist

def _check_can_create_playlist(self, team: Team) -> bool:
playlist_count = SessionRecordingPlaylist.objects.filter(deleted=False, team=team).count()
if not check_is_feature_available_for_team(team.pk, AvailableFeature.RECORDINGS_PLAYLISTS, playlist_count):
raise PermissionDenied("You have hit the limit for playlists for this team.")
return True


class SessionRecordingPlaylistViewSet(TeamAndOrgViewSetMixin, ForbidDestroyModel, viewsets.ModelViewSet):
scope_object = "session_recording_playlist"
Expand Down
18 changes: 3 additions & 15 deletions ee/session_recordings/test/test_session_recording_playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from rest_framework import status

from ee.api.test.base import APILicensedTest
from ee.api.test.fixtures.available_product_features import AVAILABLE_PRODUCT_FEATURES
from posthog.models import SessionRecording, SessionRecordingPlaylistItem
from posthog.models.user import User
from posthog.session_recordings.models.session_recording_playlist import (
Expand Down Expand Up @@ -77,24 +76,13 @@ def test_creates_playlist(self):
"last_modified_by": response.json()["last_modified_by"],
}

def test_creates_too_many_playlists(self):
limit = 0
self.organization.available_product_features = AVAILABLE_PRODUCT_FEATURES
self.organization.save()
for feature in AVAILABLE_PRODUCT_FEATURES:
if "key" in feature and feature["key"] == "recordings_playlists":
limit = int(feature["limit"])
for _ in range(limit):
def test_can_create_many_playlists(self):
for i in range(100):
response = self.client.post(
f"/api/projects/{self.team.id}/session_recording_playlists",
data={"name": "test"},
data={"name": f"test-{i}"},
)
assert response.status_code == status.HTTP_201_CREATED
response = self.client.post(
f"/api/projects/{self.team.id}/session_recording_playlists",
data={"name": "test"},
)
assert response.status_code == status.HTTP_403_FORBIDDEN

def test_gets_individual_playlist_by_shortid(self):
create_response = self.client.post(f"/api/projects/{self.team.id}/session_recording_playlists")
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ export const PayGateMiniLimitFeatureOther = (): JSX.Element => {
...meCurrent.organization,
available_product_features: [
{
key: 'recordings_playlists',
name: 'Recordings Playlists',
key: 'advanced_permissions',
name: 'Advanced Permissions',
limit: 3,
},
],
Expand All @@ -93,7 +93,7 @@ export const PayGateMiniLimitFeatureOther = (): JSX.Element => {
],
},
})
return <Template feature={AvailableFeature.RECORDINGS_PLAYLISTS} currentUsage={3} />
return <Template feature={AvailableFeature.ADVANCED_PERMISSIONS} currentUsage={3} />
}

export const PayGateMiniLimitFeatureProjects = (): JSX.Element => {
Expand Down
34 changes: 17 additions & 17 deletions frontend/src/lib/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1452,30 +1452,30 @@ export function resolveWebhookService(webhookUrl: string): string {
return 'your webhook service'
}

export function hexToRGB(hex: string): { r: number; g: number; b: number } {
const originalString = hex.trim()
const hasPoundSign = originalString[0] === '#'
let originalColor = hasPoundSign ? originalString.slice(1) : originalString

// convert 3-digit hex colors to 6-digit
if (originalColor.length === 3) {
originalColor = originalColor
export function hexToRGB(hex: string): { r: number; g: number; b: number; a: number } {
// Remove the "#" if it exists
hex = hex.replace(/^#/, '')

// Handle shorthand notation (e.g., "#123" => "#112233")
if (hex.length === 3 || hex.length === 4) {
hex = hex
.split('')
.map((c) => c + c)
.map((char) => char + char)
.join('')
}

// make sure we have a 6-digit color
if (originalColor.length !== 6) {
if (hex.length !== 6 && hex.length !== 8) {
console.warn(`Incorrectly formatted color string: ${hex}.`)
return { r: 0, g: 0, b: 0 }
return { r: 0, g: 0, b: 0, a: 0 }
}

const originalBase16 = parseInt(originalColor, 16)
const r = originalBase16 >> 16
const g = (originalBase16 >> 8) & 0x00ff
const b = originalBase16 & 0x0000ff
return { r, g, b }
// Extract the rgb values
const r = parseInt(hex.slice(0, 2), 16)
const g = parseInt(hex.slice(2, 4), 16)
const b = parseInt(hex.slice(4, 6), 16)
const a = hex.length === 8 ? parseInt(hex.slice(6, 8), 16) / 255 : 1

return { r, g, b, a }
}

export function hexToRGBA(hex: string, alpha = 1): string {
Expand Down
27 changes: 0 additions & 27 deletions frontend/src/mocks/fixtures/_billing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -922,15 +922,6 @@ export const billingJson: BillingType = {
limit: null,
note: null,
},
{
key: 'recordings_playlists',
name: 'Recording playlists',
description:
'Create playlists of certain session recordings to easily find and watch them again in the future.',
unit: 'playlists',
limit: 5,
note: null,
},
{
key: 'session_replay_data_retention',
name: 'Data retention',
Expand Down Expand Up @@ -1041,15 +1032,6 @@ export const billingJson: BillingType = {
limit: null,
note: null,
},
{
key: 'recordings_playlists',
name: 'Recording playlists',
description:
'Create playlists of certain session recordings to easily find and watch them again in the future.',
unit: null,
limit: null,
note: null,
},
{
key: 'recordings_performance',
name: 'Network performance on recordings',
Expand Down Expand Up @@ -1293,15 +1275,6 @@ export const billingJson: BillingType = {
contact_support: false,
inclusion_only: false,
features: [
{
key: 'recordings_playlists',
name: 'Recording playlists',
description:
'Create playlists of certain session recordings to easily find and watch them again in the future.',
images: null,
icon_key: null,
type: null,
},
{
key: 'session_replay_data_retention',
name: 'Data retention',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ function SvelteAppClientCodeSnippet(): JSX.Element {
<CodeSnippet language={Language.JavaScript}>
{`import posthog from 'posthog-js'
import { browser } from '$app/environment';
import { onMount } from 'svelte';
export const load = async () => {
onMount(() => {
if (browser) {
posthog.init(
'${currentTeam?.api_token}',
Expand Down
31 changes: 7 additions & 24 deletions frontend/src/scenes/session-recordings/SessionRecordings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
defaultAuthorizedUrlProperties,
} from 'lib/components/AuthorizedUrlList/authorizedUrlListLogic'
import { PageHeader } from 'lib/components/PageHeader'
import { upgradeModalLogic } from 'lib/components/UpgradeModal/upgradeModalLogic'
import { VersionCheckerBanner } from 'lib/components/VersionChecker/VersionCheckerBanner'
import { FEATURE_FLAGS } from 'lib/constants'
import { useAsyncHandler } from 'lib/hooks/useAsyncHandler'
Expand All @@ -24,19 +23,15 @@ import { teamLogic } from 'scenes/teamLogic'
import { urls } from 'scenes/urls'

import { sidePanelSettingsLogic } from '~/layout/navigation-3000/sidepanel/panels/sidePanelSettingsLogic'
import { AvailableFeature, NotebookNodeType, ReplayTabs } from '~/types'
import { NotebookNodeType, ReplayTabs } from '~/types'

import { createPlaylist } from './playlist/playlistUtils'
import { SessionRecordingsPlaylist } from './playlist/SessionRecordingsPlaylist'
import { SavedSessionRecordingPlaylists } from './saved-playlists/SavedSessionRecordingPlaylists'
import { savedSessionRecordingPlaylistsLogic } from './saved-playlists/savedSessionRecordingPlaylistsLogic'
import { humanFriendlyTabName, sessionReplaySceneLogic } from './sessionReplaySceneLogic'
import SessionRecordingTemplates from './templates/SessionRecordingTemplates'

function Header(): JSX.Element {
const { guardAvailableFeature } = useValues(upgradeModalLogic)
const playlistsLogic = savedSessionRecordingPlaylistsLogic({ tab: ReplayTabs.Home })
const { playlists } = useValues(playlistsLogic)
const { tab } = useValues(sessionReplaySceneLogic)
const { currentTeam } = useValues(teamLogic)
const recordingsDisabled = currentTeam && !currentTeam?.session_recording_opt_in
Expand Down Expand Up @@ -84,17 +79,11 @@ function Header(): JSX.Element {
data-attr="session-recordings-filters-save-as-playlist"
type="primary"
onClick={(e) =>
guardAvailableFeature(
AvailableFeature.RECORDINGS_PLAYLISTS,
() => {
// choose the type of playlist handler so that analytics correctly report
// whether filters have been changed before saving
totalFiltersCount === 0
? newPlaylistHandler.onEvent?.(e)
: saveFiltersPlaylistHandler.onEvent?.(e)
},
{ currentUsage: playlists.count }
)
// choose the type of playlist handler so that analytics correctly report
// whether filters have been changed before saving
totalFiltersCount === 0
? newPlaylistHandler.onEvent?.(e)
: saveFiltersPlaylistHandler.onEvent?.(e)
}
>
Save as playlist
Expand All @@ -112,13 +101,7 @@ function Header(): JSX.Element {
{tab === ReplayTabs.Playlists && (
<LemonButton
type="primary"
onClick={(e) =>
guardAvailableFeature(
AvailableFeature.RECORDINGS_PLAYLISTS,
() => newPlaylistHandler.onEvent?.(e),
{ currentUsage: playlists.count }
)
}
onClick={(e) => newPlaylistHandler.onEvent?.(e)}
data-attr="save-recordings-playlist-button"
loading={newPlaylistHandler.loading}
>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import { IconPlus } from '@posthog/icons'
import { useValues } from 'kea'
import { upgradeModalLogic } from 'lib/components/UpgradeModal/upgradeModalLogic'
import { LemonBanner } from 'lib/lemon-ui/LemonBanner'
import { LemonButton } from 'lib/lemon-ui/LemonButton'

import { AvailableFeature, ReplayTabs } from '~/types'
import { ReplayTabs } from '~/types'

import { createPlaylist } from '../playlist/playlistUtils'
import { savedSessionRecordingPlaylistsLogic } from './savedSessionRecordingPlaylistsLogic'

export function SavedSessionRecordingPlaylistsEmptyState(): JSX.Element {
const { guardAvailableFeature } = useValues(upgradeModalLogic)
const playlistsLogic = savedSessionRecordingPlaylistsLogic({ tab: ReplayTabs.Home })
const { playlists, loadPlaylistsFailed } = useValues(playlistsLogic)
const { loadPlaylistsFailed } = useValues(playlistsLogic)
return loadPlaylistsFailed ? (
<LemonBanner type="error">Error while trying to load playlist.</LemonBanner>
) : (
Expand All @@ -24,13 +22,7 @@ export function SavedSessionRecordingPlaylistsEmptyState(): JSX.Element {
type="primary"
data-attr="add-session-playlist-button-empty-state"
icon={<IconPlus />}
onClick={() =>
guardAvailableFeature(
AvailableFeature.RECORDINGS_PLAYLISTS,
() => void createPlaylist({}, true),
{ currentUsage: playlists.count }
)
}
onClick={() => void createPlaylist({}, true)}
>
New playlist
</LemonButton>
Expand Down
Loading

0 comments on commit 7ac3492

Please sign in to comment.