Skip to content

Commit

Permalink
Merge branch 'master' into experiments/trends-continuous
Browse files Browse the repository at this point in the history
  • Loading branch information
danielbachhuber committed Dec 13, 2024
2 parents 4be08a0 + 5dd300a commit f619d41
Show file tree
Hide file tree
Showing 105 changed files with 3,883 additions and 1,558 deletions.

Large diffs are not rendered by default.

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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
65 changes: 65 additions & 0 deletions frontend/src/lib/integrations/IntegrationScopesWarning.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import api from 'lib/api'
import { LemonBanner } from 'lib/lemon-ui/LemonBanner'
import { Link } from 'lib/lemon-ui/Link'
import { useMemo } from 'react'

import { HogFunctionInputSchemaType, IntegrationType } from '~/types'

export function IntegrationScopesWarning({
integration,
schema,
}: {
integration: IntegrationType
schema?: HogFunctionInputSchemaType
}): JSX.Element {
const getScopes = useMemo((): string[] => {
const scopes: any[] = []
const possibleScopeLocation = [integration.config.scope, integration.config.scopes]

possibleScopeLocation.map((scope) => {
if (typeof scope === 'string') {
scopes.push(scope.split(' '))
scopes.push(scope.split(','))
}
if (typeof scope === 'object') {
scopes.push(scope)
}
})
return scopes
.filter((scope: any) => typeof scope === 'object')
.reduce((a, b) => (a.length > b.length ? a : b), [])
}, [integration.config])

const requiredScopes = schema?.requiredScopes?.split(' ') || []
const missingScopes = requiredScopes.filter((scope: string) => !getScopes.includes(scope))

if (missingScopes.length === 0 || getScopes.length === 0) {
return <></>
}
return (
<div className="p-2">
<LemonBanner
type="error"
action={{
children: 'Reconnect',
disableClientSideRouting: true,
to: api.integrations.authorizeUrl({
kind: integration.kind,
next: window.location.pathname,
}),
}}
>
<span>Required scopes are missing: [{missingScopes.join(', ')}].</span>
{integration.kind === 'hubspot' ? (
<span>
Note that some features may not be available on your current HubSpot plan. Check out{' '}
<Link to="https://developers.hubspot.com/beta-docs/guides/apps/authentication/scopes">
this page
</Link>{' '}
for more details.
</span>
) : null}
</LemonBanner>
</div>
)
}
9 changes: 7 additions & 2 deletions frontend/src/lib/integrations/IntegrationView.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { LemonBanner } from '@posthog/lemon-ui'
import api from 'lib/api'
import { UserActivityIndicator } from 'lib/components/UserActivityIndicator/UserActivityIndicator'
import { IntegrationScopesWarning } from 'lib/integrations/IntegrationScopesWarning'

import { IntegrationType } from '~/types'
import { HogFunctionInputSchemaType, IntegrationType } from '~/types'

export function IntegrationView({
integration,
suffix,
schema,
}: {
integration: IntegrationType
suffix?: JSX.Element
schema?: HogFunctionInputSchemaType
}): JSX.Element {
const errors = (integration.errors && integration.errors?.split(',')) || []

Expand All @@ -36,7 +39,7 @@ export function IntegrationView({
{suffix}
</div>

{errors.length > 0 && (
{errors.length > 0 ? (
<div className="p-2">
<LemonBanner
type="error"
Expand All @@ -54,6 +57,8 @@ export function IntegrationView({
: `There was an error with this integration: ${errors[0]}`}
</LemonBanner>
</div>
) : (
<IntegrationScopesWarning integration={integration} schema={schema} />
)}
</div>
)
Expand Down
135 changes: 113 additions & 22 deletions frontend/src/scenes/dashboard/newDashboardLogic.test.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,132 @@
import { NodeKind } from '~/queries/schema'

import { applyTemplate } from './newDashboardLogic'

describe('template function in newDashboardLogic', () => {
it('ignores unused variables', () => {
expect(
applyTemplate({ a: 'hello', b: 'hi' }, [
{
id: 'VARIABLE_1',
name: 'a',
default: {
event: '$pageview',
applyTemplate(
{ a: 'hello', b: 'hi' },
[
{
id: 'VARIABLE_1',
name: 'a',
default: {
event: '$pageview',
},
description: 'The description of the variable',
required: true,
type: 'event',
},
description: 'The description of the variable',
required: true,
type: 'event',
},
])
],
null
)
).toEqual({ a: 'hello', b: 'hi' })
})
it('uses identified variables', () => {
expect(
applyTemplate({ a: '{VARIABLE_1}', b: 'hi' }, [
{
id: 'VARIABLE_1',
name: 'a',
default: {
event: '$pageview',
applyTemplate(
{ a: '{VARIABLE_1}', b: 'hi' },
[
{
id: 'VARIABLE_1',
name: 'a',
default: {
event: '$pageview',
},
description: 'The description of the variable',
required: true,
type: 'event',
},
description: 'The description of the variable',
required: true,
type: 'event',
},
])
],
null
)
).toEqual({
a: {
event: '$pageview',
},
b: 'hi',
})
})

it('replaces variables in query based tiles', () => {
expect(
applyTemplate(
{ a: '{VARIABLE_1}' },
[
{
id: 'VARIABLE_1',
name: 'a',
default: {
id: '$pageview',
},
description: 'The description of the variable',
required: true,
type: 'event',
},
],
NodeKind.TrendsQuery
)
).toEqual({
a: {
event: '$pageview',
kind: 'EventsNode',
math: 'total',
},
})
})

it("removes the math property from query based tiles that don't support it", () => {
expect(
applyTemplate(
{ a: '{VARIABLE_1}' },
[
{
id: 'VARIABLE_1',
name: 'a',
default: {
id: '$pageview',
},
description: 'The description of the variable',
required: true,
type: 'event',
},
],
NodeKind.LifecycleQuery
)
).toEqual({
a: {
event: '$pageview',
kind: 'EventsNode',
},
})
})

it('removes the math property from retention insight tiles', () => {
expect(
applyTemplate(
{ a: '{VARIABLE_1}' },
[
{
id: 'VARIABLE_1',
name: 'a',
default: {
id: '$pageview',
math: 'dau' as any,
type: 'events' as any,
},
description: 'The description of the variable',
required: true,
type: 'event',
},
],
NodeKind.RetentionQuery
)
).toEqual({
a: {
id: '$pageview',
type: 'events',
},
})
})
})
43 changes: 39 additions & 4 deletions frontend/src/scenes/dashboard/newDashboardLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ import api from 'lib/api'
import { DashboardRestrictionLevel } from 'lib/constants'
import { lemonToast } from 'lib/lemon-ui/LemonToast/LemonToast'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { MathAvailability } from 'scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow'
import { teamLogic } from 'scenes/teamLogic'
import { urls } from 'scenes/urls'

import { dashboardsModel } from '~/models/dashboardsModel'
import { legacyEntityToNode, sanitizeRetentionEntity } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode'
import { getQueryBasedDashboard } from '~/queries/nodes/InsightViz/utils'
import { NodeKind } from '~/queries/schema'
import { isInsightVizNode } from '~/queries/utils'
import { DashboardTemplateType, DashboardTemplateVariableType, DashboardTile, DashboardType, JsonType } from '~/types'

import type { newDashboardLogicType } from './newDashboardLogicType'
Expand All @@ -35,32 +39,63 @@ export interface NewDashboardLogicProps {
}

// Currently this is a very generic recursive function incase we want to add template variables to aspects beyond events
export function applyTemplate(obj: DashboardTile | JsonType, variables: DashboardTemplateVariableType[]): JsonType {
export function applyTemplate(
obj: DashboardTile | JsonType,
variables: DashboardTemplateVariableType[],
queryKind: NodeKind | null
): JsonType {
if (typeof obj === 'string') {
if (obj.startsWith('{') && obj.endsWith('}')) {
const variableId = obj.substring(1, obj.length - 1)
const variable = variables.find((variable) => variable.id === variableId)
if (variable && variable.default) {
// added for future compatibility - at the moment we only have event variables
const isEventVariable = variable.type === 'event'

if (queryKind && isEventVariable) {
let mathAvailability = MathAvailability.None
if (queryKind === NodeKind.TrendsQuery) {
mathAvailability = MathAvailability.All
} else if (queryKind === NodeKind.StickinessQuery) {
mathAvailability = MathAvailability.ActorsOnly
} else if (queryKind === NodeKind.FunnelsQuery) {
mathAvailability = MathAvailability.FunnelsOnly
}
return (
queryKind === NodeKind.RetentionQuery
? sanitizeRetentionEntity(variable.default as any)
: legacyEntityToNode(variable.default as any, true, mathAvailability)
) as JsonType
}

return variable.default as JsonType
}
return obj
}
}
if (Array.isArray(obj)) {
return obj.map((item) => applyTemplate(item, variables))
return obj.map((item) => applyTemplate(item, variables, queryKind))
}
if (typeof obj === 'object' && obj !== null) {
const newObject: JsonType = {}
for (const [key, value] of Object.entries(obj)) {
newObject[key] = applyTemplate(value, variables)
newObject[key] = applyTemplate(value, variables, queryKind)
}
return newObject
}
return obj
}

function makeTilesUsingVariables(tiles: DashboardTile[], variables: DashboardTemplateVariableType[]): JsonType[] {
return tiles.map((tile: DashboardTile) => applyTemplate(tile, variables))
return tiles.map((tile: DashboardTile) => {
const isQueryBased = 'query' in tile && tile.query != null
const queryKind: NodeKind | null = isQueryBased
? isInsightVizNode(tile.query as any)
? (tile.query as any)?.source.kind
: (tile.query as any)?.kind
: null
return applyTemplate(tile, variables, queryKind)
})
}

export const newDashboardLogic = kea<newDashboardLogicType>([
Expand Down
25 changes: 16 additions & 9 deletions frontend/src/scenes/experiments/ExperimentView/Goal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,11 @@ export function Goal(): JSX.Element {
const [isModalOpen, setIsModalOpen] = useState(false)
const metricType = getMetricType(0)

// :FLAG: CLEAN UP AFTER MIGRATION
const isDataWarehouseMetric =
featureFlags[FEATURE_FLAGS.EXPERIMENTS_HOGQL] &&
(experiment.metrics[0] as ExperimentTrendsQuery).count_query.series[0].kind === NodeKind.DataWarehouseNode

return (
<div>
<div>
Expand Down Expand Up @@ -322,16 +327,18 @@ export function Goal(): JSX.Element {
Change goal
</LemonButton>
</div>
{metricType === InsightType.TRENDS && !experimentMathAggregationForTrends() && (
<>
<LemonDivider className="" vertical />
<div className="">
<div className="mt-auto ml-auto">
<ExposureMetric experimentId={experimentId} />
{metricType === InsightType.TRENDS &&
!experimentMathAggregationForTrends() &&
!isDataWarehouseMetric && (
<>
<LemonDivider className="" vertical />
<div className="">
<div className="mt-auto ml-auto">
<ExposureMetric experimentId={experimentId} />
</div>
</div>
</div>
</>
)}
</>
)}
</div>
)}
<PrimaryMetricModal
Expand Down
Loading

0 comments on commit f619d41

Please sign in to comment.