Skip to content

Commit

Permalink
Merge branch 'master' into feat/cohort-pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
dmarticus authored Jan 14, 2025
2 parents 39346d4 + 0ac0d84 commit 44c7631
Show file tree
Hide file tree
Showing 29 changed files with 724 additions and 182 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,6 @@ pyrightconfig.json
temp_test_run_data.json
.temp-deepeval-cache.json
.eslintcache

# ignore all dagster tmp directories that may be created
tmp*/
Empty file added dags/__init__.py
Empty file.
34 changes: 34 additions & 0 deletions dags/assets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import os
import django

from dagster import (
Config,
MaterializeResult,
asset,
)

# setup PostHog Django Project

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "posthog.settings")
django.setup()

from posthog.clickhouse.client import sync_execute # noqa


class ClickHouseConfig(Config):
result_path: str = "/tmp/clickhouse_version.txt"


@asset
def get_clickhouse_version(config: ClickHouseConfig) -> MaterializeResult:
version = sync_execute("SELECT version()")[0][0]
with open(config.result_path, "w") as f:
f.write(version)
return MaterializeResult(metadata={"version": version})


@asset(deps=[get_clickhouse_version])
def print_clickhouse_version(config: ClickHouseConfig):
with open(config.result_path) as f:
print(f.read()) # noqa
return MaterializeResult(metadata={"version": config.result_path})
9 changes: 9 additions & 0 deletions dags/definitions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from dagster import Definitions, load_assets_from_modules

from . import assets

all_assets = load_assets_from_modules([assets])

defs = Definitions(
assets=all_assets,
)
Empty file added dags_test/__init__.py
Empty file.
79 changes: 79 additions & 0 deletions dags_test/test_assets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import pytest
from unittest.mock import patch, mock_open

from dagster import build_op_context

from dags.assets import (
get_clickhouse_version,
print_clickhouse_version,
ClickHouseConfig,
)


@pytest.fixture
def mock_sync_execute():
with patch("dags.assets.sync_execute") as mock:
mock.return_value = [["23.8.1.2992"]]
yield mock


@pytest.fixture
def config():
return ClickHouseConfig(result_path="/tmp/test_clickhouse_version.txt")


def test_get_clickhouse_version(mock_sync_execute, config):
# Create a test context with our config
context = build_op_context(resources={}, config=config)

# Mock the file write operation
mock_file = mock_open()
with patch("builtins.open", mock_file):
result = get_clickhouse_version(context)

# Verify the SQL query was executed
mock_sync_execute.assert_called_once_with("SELECT version()")

# Verify the version was written to file
mock_file().write.assert_called_once_with("23.8.1.2992")

# Verify the result metadata
assert result.metadata == {"version": "23.8.1.2992"}


def test_print_clickhouse_version(config):
# Create a test context with our config
context = build_op_context(resources={}, config=config)

# Mock the file read operation
mock_file = mock_open(read_data="23.8.1.2992")
with patch("builtins.open", mock_file) as mock_open_:
with patch("builtins.print") as mock_print:
result = print_clickhouse_version(context)

# Verify the file was read
mock_open_.assert_called_once_with(config.result_path)

# Verify the version was printed
mock_print.assert_called_once_with("23.8.1.2992")

# Verify the result metadata
assert result.metadata == {"version": config.result_path}


def test_assets_integration(mock_sync_execute, config):
"""Test that both assets work together in sequence"""
context = build_op_context(resources={}, config=config)

# First run get_clickhouse_version
mock_write = mock_open()
with patch("builtins.open", mock_write):
get_clickhouse_version(context)
mock_write().write.assert_called_once_with("23.8.1.2992")

# Then run print_clickhouse_version
mock_read = mock_open(read_data="23.8.1.2992")
with patch("builtins.open", mock_read):
with patch("builtins.print") as mock_print:
print_clickhouse_version(context)
mock_print.assert_called_once_with("23.8.1.2992")
2 changes: 1 addition & 1 deletion frontend/src/layout/navigation-3000/navigationLogic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ export const navigation3000Logic = kea<navigation3000LogicType>([
identifier: Scene.LLMObservability,
label: 'LLM observability',
icon: <IconAI />,
to: urls.llmObservability(),
to: urls.llmObservability('dashboard'),
tag: 'beta' as const,
}
: null,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2747,7 +2747,7 @@ async function handleFetch(url: string, method: string, fetcher: () => Promise<R
error = e
}

apiStatusLogic.findMounted()?.actions.onApiResponse(response, error)
apiStatusLogic.findMounted()?.actions.onApiResponse(response?.clone(), error)

if (error || !response) {
if (error && (error as any).name === 'AbortError') {
Expand Down
25 changes: 25 additions & 0 deletions frontend/src/lib/components/AccessDenied/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useActions } from 'kea'
import { Link } from 'lib/lemon-ui/Link'
import notFoundAstrohog from 'public/not-found-astrohog.png'

import { supportLogic } from '../Support/supportLogic'

export interface AccessDeniedProps {
object: string
}

export function AccessDenied({ object }: AccessDeniedProps): JSX.Element {
const { openSupportForm } = useActions(supportLogic)

return (
<div className="flex flex-col items-center max-w-2xl p-4 my-24 mx-auto text-center">
<img src={notFoundAstrohog} className="w-64 h-64 bg-no-repeat bg-center" />
<h1 className="text-3xl font-bold mt-4 mb-0">Access denied</h1>
<p className="text-sm mt-3 mb-0">
You do not have access to this {object}. Please contact the creator of this {object} or{' '}
<Link onClick={() => openSupportForm({ kind: 'support' })}>support</Link> if you think this is a
mistake.
</p>
</div>
)
}
5 changes: 5 additions & 0 deletions frontend/src/lib/taxonomy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1423,6 +1423,11 @@ export const CORE_FILTER_DEFINITIONS_BY_GROUP = {
'The trace ID of the request made to the LLM API. Used to group together multiple generations into a single trace',
examples: ['c9222e05-8708-41b8-98ea-d4a21849e761'],
},
$ai_request_url: {
label: 'AI Request URL (LLM)',
description: 'The full URL of the request made to the LLM API',
examples: ['https://api.openai.com/v1/chat/completions'],
},
},
numerical_event_properties: {}, // Same as event properties, see assignment below
person_properties: {}, // Currently person properties are the same as event properties, see assignment below
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/queries/nodes/DataTable/renderColumnMeta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types'
import { SortingIndicator } from 'lib/lemon-ui/LemonTable/sorting'

import { getQueryFeatures, QueryFeature } from '~/queries/nodes/DataTable/queryFeatures'
import { extractExpressionComment } from '~/queries/nodes/DataTable/utils'
import { extractExpressionComment, removeExpressionComment } from '~/queries/nodes/DataTable/utils'
import { DataTableNode, DataVisualizationNode, EventsQuery } from '~/queries/schema'
import { QueryContext } from '~/queries/types'
import { isDataTableNode, isHogQLQuery, trimQuotes } from '~/queries/utils'
Expand Down Expand Up @@ -52,7 +52,7 @@ export function renderColumnMeta<T extends DataVisualizationNode | DataTableNode
} else if (key.startsWith('properties.')) {
title = (
<PropertyKeyInfo
value={trimQuotes(key.substring(11))}
value={trimQuotes(removeExpressionComment(key.substring(11)))}
type={TaxonomicFilterGroupType.EventProperties}
disableIcon
/>
Expand Down
17 changes: 15 additions & 2 deletions frontend/src/scenes/dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { LemonButton } from '@posthog/lemon-ui'
import { BindLogic, useActions, useMountedLogic, useValues } from 'kea'
import { AccessDenied } from 'lib/components/AccessDenied'
import { NotFound } from 'lib/components/NotFound'
import { useKeyboardHotkeys } from 'lib/hooks/useKeyboardHotkeys'
import { DashboardEventSource } from 'lib/utils/eventUsageLogic'
Expand Down Expand Up @@ -46,8 +47,16 @@ export function Dashboard({ id, dashboard, placement, themes }: DashboardProps =
}

function DashboardScene(): JSX.Element {
const { placement, dashboard, canEditDashboard, tiles, itemsLoading, dashboardMode, dashboardFailedToLoad } =
useValues(dashboardLogic)
const {
placement,
dashboard,
canEditDashboard,
tiles,
itemsLoading,
dashboardMode,
dashboardFailedToLoad,
accessDeniedToDashboard,
} = useValues(dashboardLogic)
const { setDashboardMode, reportDashboardViewed, abortAnyRunningQuery } = useActions(dashboardLogic)

useEffect(() => {
Expand Down Expand Up @@ -91,6 +100,10 @@ function DashboardScene(): JSX.Element {
return <NotFound object="dashboard" />
}

if (accessDeniedToDashboard) {
return <AccessDenied object="dashboard" />
}

return (
<div className="dashboard">
{placement == DashboardPlacement.Dashboard && <DashboardHeader />}
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/scenes/dashboard/dashboardLogic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ export const dashboardLogic = kea<dashboardLogicType>([
allVariables: values.variables,
}),
resetVariables: () => ({ variables: values.insightVariables }),
setAccessDeniedToDashboard: true,
})),

loaders(({ actions, props, values }) => ({
Expand Down Expand Up @@ -302,6 +303,9 @@ export const dashboardLogic = kea<dashboardLogicType>([
if (error.status === 404) {
return null
}
if (error.status === 403 && error.code === 'permission_denied') {
actions.setAccessDeniedToDashboard()
}
throw error
}
},
Expand Down Expand Up @@ -431,6 +435,12 @@ export const dashboardLogic = kea<dashboardLogicType>([
setPageVisibility: (_, { visible }) => visible,
},
],
accessDeniedToDashboard: [
false,
{
setAccessDeniedToDashboard: () => true,
},
],
dashboardFailedToLoad: [
false,
{
Expand Down
18 changes: 16 additions & 2 deletions frontend/src/scenes/feature-flags/FeatureFlag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { LemonDialog, LemonSegmentedButton, LemonSkeleton, LemonSwitch } from '@
import { useActions, useValues } from 'kea'
import { Form, Group } from 'kea-forms'
import { router } from 'kea-router'
import { AccessDenied } from 'lib/components/AccessDenied'
import { ActivityLog } from 'lib/components/ActivityLog/ActivityLog'
import { CopyToClipboardInline } from 'lib/components/CopyToClipboard'
import { NotFound } from 'lib/components/NotFound'
Expand Down Expand Up @@ -86,8 +87,16 @@ function focusVariantKeyField(index: number): void {
}

export function FeatureFlag({ id }: { id?: string } = {}): JSX.Element {
const { props, featureFlag, featureFlagLoading, featureFlagMissing, isEditingFlag, newCohortLoading, activeTab } =
useValues(featureFlagLogic)
const {
props,
featureFlag,
featureFlagLoading,
featureFlagMissing,
isEditingFlag,
newCohortLoading,
activeTab,
accessDeniedToFeatureFlag,
} = useValues(featureFlagLogic)
const { featureFlags } = useValues(enabledFeaturesLogic)
const {
deleteFeatureFlag,
Expand All @@ -113,6 +122,7 @@ export function FeatureFlag({ id }: { id?: string } = {}): JSX.Element {
if (featureFlagMissing) {
return <NotFound object="feature flag" />
}

if (featureFlagLoading) {
return (
<div className="space-y-2">
Expand All @@ -124,6 +134,10 @@ export function FeatureFlag({ id }: { id?: string } = {}): JSX.Element {
)
}

if (accessDeniedToFeatureFlag) {
return <AccessDenied object="feature flag" />
}

const tabs = [
{
label: 'Overview',
Expand Down
10 changes: 8 additions & 2 deletions frontend/src/scenes/feature-flags/featureFlagLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ export const featureFlagLogic = kea<featureFlagLogicType>([
errors?: any
) => ({ filters, active, errors }),
setScheduledChangeOperation: (changeType: ScheduledChangeOperationType) => ({ changeType }),
setAccessDeniedToFeatureFlag: true,
}),
forms(({ actions, values }) => ({
featureFlag: {
Expand Down Expand Up @@ -449,6 +450,7 @@ export const featureFlagLogic = kea<featureFlagLogicType>([
},
},
],
accessDeniedToFeatureFlag: [false, { setAccessDeniedToFeatureFlag: () => true }],
propertySelectErrors: [
null as any,
{
Expand Down Expand Up @@ -527,8 +529,12 @@ export const featureFlagLogic = kea<featureFlagLogicType>([
try {
const retrievedFlag: FeatureFlagType = await api.featureFlags.get(props.id)
return variantKeyToIndexFeatureFlagPayloads(retrievedFlag)
} catch (e) {
actions.setFeatureFlagMissing()
} catch (e: any) {
if (e.status === 403 && e.code === 'permission_denied') {
actions.setAccessDeniedToFeatureFlag()
} else {
actions.setFeatureFlagMissing()
}
throw e
}
}
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/scenes/insights/Insight.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { LemonBanner, LemonButton } from '@posthog/lemon-ui'
import { BindLogic, useActions, useMountedLogic, useValues } from 'kea'
import { AccessDenied } from 'lib/components/AccessDenied'
import { DebugCHQueries } from 'lib/components/CommandPalette/DebugCHQueries'
import { isObject } from 'lib/utils'
import { InsightPageHeader } from 'scenes/insights/InsightPageHeader'
Expand Down Expand Up @@ -35,7 +36,7 @@ export function Insight({ insightId }: InsightSceneProps): JSX.Element {
filtersOverride,
variablesOverride,
})
const { insightProps } = useValues(logic)
const { insightProps, accessDeniedToInsight } = useValues(logic)

// insightDataLogic
const { query, showQueryEditor, showDebugPanel } = useValues(insightDataLogic(insightProps))
Expand All @@ -52,6 +53,10 @@ export function Insight({ insightId }: InsightSceneProps): JSX.Element {
}
}

if (accessDeniedToInsight) {
return <AccessDenied object="insight" />
}

return (
<BindLogic logic={insightLogic} props={insightProps}>
<div className="Insight">
Expand Down
Loading

0 comments on commit 44c7631

Please sign in to comment.