Skip to content

Commit

Permalink
Merge branch 'master' into ch-24.3
Browse files Browse the repository at this point in the history
  • Loading branch information
fuziontech authored Oct 11, 2024
2 parents c71b964 + 9f60b9c commit 9138acb
Show file tree
Hide file tree
Showing 111 changed files with 1,910 additions and 710 deletions.
26 changes: 13 additions & 13 deletions docker/clickhouse/user_defined_function.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<name>value</name>
</argument>
<format>JSONEachRow</format>
<command>aggregate_funnel</command>
<command>aggregate_funnel steps</command>
<lifetime>600</lifetime>
</function>

Expand Down Expand Up @@ -63,7 +63,7 @@
<name>value</name>
</argument>
<format>JSONEachRow</format>
<command>aggregate_funnel</command>
<command>aggregate_funnel steps</command>
<lifetime>600</lifetime>
</function>

Expand Down Expand Up @@ -97,7 +97,7 @@
<name>value</name>
</argument>
<format>JSONEachRow</format>
<command>aggregate_funnel</command>
<command>aggregate_funnel steps</command>
<lifetime>600</lifetime>
</function>

Expand Down Expand Up @@ -138,7 +138,7 @@
<function>
<type>executable_pool</type>
<name>aggregate_funnel_trends</name>
<return_type>Array(Tuple(DateTime, Int8, Nullable(String)))</return_type>
<return_type>Array(Tuple(UInt64, Int8, Nullable(String)))</return_type>
<return_name>result</return_name>
<argument>
<type>UInt8</type>
Expand Down Expand Up @@ -169,19 +169,19 @@
<name>prop_vals</name>
</argument>
<argument>
<type>Array(Tuple(Nullable(Float64), Nullable(DateTime), Nullable(String), Array(Int8)))</type>
<type>Array(Tuple(Nullable(Float64), UInt64, Nullable(String), Array(Int8)))</type>
<name>value</name>
</argument>
<format>JSONEachRow</format>
<command>aggregate_funnel_trends.py</command>
<command>aggregate_funnel trends</command>
<lifetime>600</lifetime>
</function>

<function>
<type>executable_pool</type>
<name>aggregate_funnel_array_trends</name>
<!-- Return type for trends is a start interval time, a success flag (1 or -1), and a breakdown value -->
<return_type>Array(Tuple(DateTime, Int8, Array(String)))</return_type>
<return_type>Array(Tuple(UInt64, Int8, Array(String)))</return_type>
<return_name>result</return_name>
<argument>
<type>UInt8</type>
Expand All @@ -208,19 +208,19 @@
<name>prop_vals</name>
</argument>
<argument>
<type>Array(Tuple(Nullable(Float64), Nullable(DateTime), Array(String), Array(Int8)))</type>
<type>Array(Tuple(Nullable(Float64), UInt64, Array(String), Array(Int8)))</type>
<name>value</name>
</argument>
<format>JSONEachRow</format>
<command>aggregate_funnel_array_trends.py</command>
<command>aggregate_funnel trends</command>
<lifetime>600</lifetime>
</function>

<function>
<type>executable_pool</type>
<name>aggregate_funnel_cohort_trends</name>
<!-- Return type for trends is a start interval time, a success flag (1 or -1), and a breakdown value -->
<return_type>Array(Tuple(DateTime, Int8, UInt64))</return_type>
<return_type>Array(Tuple(UInt64, Int8, UInt64))</return_type>
<return_name>result</return_name>
<argument>
<type>UInt8</type>
Expand All @@ -247,11 +247,11 @@
<name>prop_vals</name>
</argument>
<argument>
<type>Array(Tuple(Nullable(Float64), Nullable(DateTime), UInt64, Array(Int8)))</type>
<type>Array(Tuple(Nullable(Float64), UInt64, UInt64, Array(Int8)))</type>
<name>value</name>
</argument>
<format>JSONEachRow</format>
<command>aggregate_funnel_cohort_trends.py</command>
<command>aggregate_funnel trends</command>
<lifetime>600</lifetime>
</function>

Expand Down Expand Up @@ -285,7 +285,7 @@
<name>prop_vals</name>
</argument>
<argument>
<type>Array(Tuple(Nullable(Float64), Nullable(DateTime), Array(String), Array(Int8)))</type>
<type>Array(Tuple(Nullable(Float64), UInt64, Array(String), Array(Int8)))</type>
<name>value</name>
</argument>
<format>JSONEachRow</format>
Expand Down
29 changes: 28 additions & 1 deletion ee/api/billing.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,34 @@ def purchase_credits(self, request: Request, *args: Any, **kwargs: Any) -> HttpR
res = billing_manager.purchase_credits(organization, request.data)
return Response(res, status=status.HTTP_200_OK)

@action(methods=["POST"], detail=False, url_path="activate/authorize")
def authorize(self, request: Request, *args: Any, **kwargs: Any) -> HttpResponse:
license = get_cached_instance_license()
if not license:
return Response(
{"success": True},
status=status.HTTP_200_OK,
)

organization = self._get_org_required()
billing_manager = self.get_billing_manager()
res = billing_manager.authorize(organization)
return Response(res, status=status.HTTP_200_OK)

@action(methods=["POST"], detail=False, url_path="activate/authorize/status")
def authorize_status(self, request: Request, *args: Any, **kwargs: Any) -> HttpResponse:
license = get_cached_instance_license()
if not license:
return Response(
{"success": True},
status=status.HTTP_200_OK,
)

organization = self._get_org_required()
billing_manager = self.get_billing_manager()
res = billing_manager.authorize_status(organization, request.data)
return Response(res, status=status.HTTP_200_OK)

@action(methods=["PATCH"], detail=False)
def license(self, request: Request, *args: Any, **kwargs: Any) -> HttpResponse:
license = get_cached_instance_license()
Expand All @@ -291,7 +319,6 @@ def license(self, request: Request, *args: Any, **kwargs: Any) -> HttpResponse:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

license = License(key=serializer.validated_data["license"])

res = requests.get(
f"{BILLING_SERVICE_URL}/api/billing",
headers=BillingManager(license).get_auth_headers(organization),
Expand Down
21 changes: 21 additions & 0 deletions ee/billing/billing_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,3 +376,24 @@ def purchase_credits(self, organization: Organization, data: dict[str, Any]):
handle_billing_service_error(res)

return res.json()

def authorize(self, organization: Organization):
res = requests.post(
f"{BILLING_SERVICE_URL}/api/activate/authorize",
headers=self.get_auth_headers(organization),
)

handle_billing_service_error(res)

return res.json()

def authorize_status(self, organization: Organization, data: dict[str, Any]):
res = requests.post(
f"{BILLING_SERVICE_URL}/api/activate/authorize/status",
headers=self.get_auth_headers(organization),
json=data,
)

handle_billing_service_error(res)

return res.json()
Binary file modified frontend/__snapshots__/scenes-other-toolbar--actions--dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/__snapshots__/scenes-other-toolbar--actions--light.png
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.
Binary file modified frontend/__snapshots__/scenes-other-toolbar--default--dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/__snapshots__/scenes-other-toolbar--default--light.png
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.
Binary file modified frontend/__snapshots__/scenes-other-toolbar--experiments--dark.png
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.
Binary file modified frontend/__snapshots__/scenes-other-toolbar--heatmap--dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/__snapshots__/scenes-other-toolbar--heatmap--light.png
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.
Binary file modified frontend/__snapshots__/scenes-other-toolbar--inspect--dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified frontend/__snapshots__/scenes-other-toolbar--inspect--light.png
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.
Binary file modified frontend/__snapshots__/scenes-other-toolbar--minimized--dark.png
1 change: 1 addition & 0 deletions frontend/src/lib/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ export const FEATURE_FLAGS = {
BIGQUERY_DWH: 'bigquery-dwh', // owner: @Gilbert09 #team-data-warehouse
REPLAY_DEFAULT_SORT_ORDER_EXPERIMENT: 'replay-order-by-experiment', // owner: #team-replay
ENVIRONMENTS: 'environments', // owner: @Twixes #team-product-analytics
BILLING_PAYMENT_ENTRY_IN_APP: 'billing-payment-entry-in-app', // owner: @zach
LEGACY_ACTION_WEBHOOKS: 'legacy-action-webhooks', // owner: @mariusandra #team-cdp
} as const
export type FeatureFlagKey = (typeof FEATURE_FLAGS)[keyof typeof FEATURE_FLAGS]
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lib/utils/getAppContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ declare global {
POSTHOG_APP_CONTEXT?: AppContext
SENTRY_DSN?: string
SENTRY_ENVIRONMENT?: string
STRIPE_PUBLIC_KEY?: string
}
}

Expand Down
1 change: 1 addition & 0 deletions frontend/src/scenes/appScenes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export const appScenes: Record<Scene, () => any> = {
[Scene.Signup]: () => import('./authentication/signup/SignupContainer'),
[Scene.InviteSignup]: () => import('./authentication/InviteSignup'),
[Scene.Billing]: () => import('./billing/Billing'),
[Scene.BillingAuthorizationStatus]: () => import('./billing/AuthorizationStatus'),
[Scene.Login]: () => import('./authentication/Login'),
[Scene.Login2FA]: () => import('./authentication/Login2FA'),
[Scene.SavedInsights]: () => import('./saved-insights/SavedInsights'),
Expand Down
17 changes: 17 additions & 0 deletions frontend/src/scenes/billing/AuthorizationStatus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { SpinnerOverlay } from '@posthog/lemon-ui'
import { useActions } from 'kea'
import { useEffect } from 'react'

import { paymentEntryLogic } from './paymentEntryLogic'

// note(@zach): this page is only used when a payment method is entered into the payment entry modal
// that requires the user to be redirect to another url, this is where they get redirected back to
export const AuthorizationStatus = (): JSX.Element => {
const { pollAuthorizationStatus } = useActions(paymentEntryLogic)

useEffect(() => {
pollAuthorizationStatus()
}, [])

return <SpinnerOverlay sceneLevel />
}
44 changes: 32 additions & 12 deletions frontend/src/scenes/billing/BillingCTAHero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@ import { LemonButton } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { BillingUpgradeCTA } from 'lib/components/BillingUpgradeCTA'
import { BlushingHog } from 'lib/components/hedgehogs'
import { FEATURE_FLAGS } from 'lib/constants'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import useResizeObserver from 'use-resize-observer'

import { BillingProductV2Type } from '~/types'

import { billingLogic } from './billingLogic'
import { billingProductLogic } from './billingProductLogic'
import { paymentEntryLogic } from './paymentEntryLogic'
import { PlanComparisonModal } from './PlanComparison'

export const BillingCTAHero = ({ product }: { product: BillingProductV2Type }): JSX.Element => {
const { width, ref: billingHeroRef } = useResizeObserver()
const { featureFlags } = useValues(featureFlagLogic)

const { showPaymentEntryModal } = useActions(paymentEntryLogic)

const { redirectPath } = useValues(billingLogic)
const { isPlanComparisonModalOpen, billingProductLoading } = useValues(billingProductLogic({ product }))
Expand All @@ -31,18 +37,32 @@ export const BillingCTAHero = ({ product }: { product: BillingProductV2Type }):
<p className="italic">P.S. You still keep the monthly free allotment for every product!</p>
</div>
<div className="flex justify-start space-x-2">
<BillingUpgradeCTA
className="mt-4 inline-block"
to={`/api/billing/activate?products=all_products:&redirect_path=${redirectPath}`}
type="primary"
status="alt"
data-attr="billing-page-core-upgrade-cta"
disableClientSideRouting
loading={!!billingProductLoading}
onClick={() => setBillingProductLoading(product.type)}
>
Upgrade now
</BillingUpgradeCTA>
{featureFlags[FEATURE_FLAGS.BILLING_PAYMENT_ENTRY_IN_APP] == 'test' ? (
<BillingUpgradeCTA
className="mt-4 inline-block"
type="primary"
status="alt"
data-attr="billing-page-core-upgrade-cta"
disableClientSideRouting
loading={!!billingProductLoading}
onClick={showPaymentEntryModal}
>
Upgrade now
</BillingUpgradeCTA>
) : (
<BillingUpgradeCTA
className="mt-4 inline-block"
to={`/api/billing/activate?products=all_products:&redirect_path=${redirectPath}`}
type="primary"
status="alt"
data-attr="billing-page-core-upgrade-cta"
disableClientSideRouting
loading={!!billingProductLoading}
onClick={() => setBillingProductLoading(product.type)}
>
Upgrade now
</BillingUpgradeCTA>
)}
<LemonButton
className="mt-4 inline-block"
onClick={() => toggleIsPlanComparisonModalOpen()}
Expand Down
92 changes: 92 additions & 0 deletions frontend/src/scenes/billing/PaymentEntryModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { LemonButton, LemonModal, Spinner } from '@posthog/lemon-ui'
import { Elements, PaymentElement, useElements, useStripe } from '@stripe/react-stripe-js'
import { loadStripe } from '@stripe/stripe-js'
import { useActions, useValues } from 'kea'
import { useEffect } from 'react'

import { paymentEntryLogic } from './paymentEntryLogic'

const stripePromise = loadStripe(window.STRIPE_PUBLIC_KEY!)

export const PaymentForm = (): JSX.Element => {
const { error, isLoading } = useValues(paymentEntryLogic)
const { setError, hidePaymentEntryModal, pollAuthorizationStatus, setLoading } = useActions(paymentEntryLogic)

const stripe = useStripe()
const elements = useElements()

// @ts-expect-error
const handleSubmit = async (event): Promise<void> => {
event.preventDefault()
if (!stripe || !elements) {
return
}
setLoading(true)
const result = await stripe.confirmPayment({
elements,
confirmParams: {
return_url: `${window.location.origin}/billing/authorization_status`,
},
redirect: 'if_required',
})

if (result.error) {
setLoading(false)
setError(result.error.message)
} else {
pollAuthorizationStatus()
}
}

return (
<div>
<PaymentElement />
{error && <div className="error">{error}</div>}
<div className="flex justify-end space-x-2 mt-2">
<LemonButton disabled={isLoading} type="secondary" onClick={hidePaymentEntryModal}>
Cancel
</LemonButton>
<LemonButton loading={isLoading} type="primary" onClick={(event) => void handleSubmit(event)}>
Submit
</LemonButton>
</div>
</div>
)
}

interface PaymentEntryModalProps {
redirectPath?: string | null
}

export const PaymentEntryModal = ({ redirectPath = null }: PaymentEntryModalProps): JSX.Element | null => {
const { clientSecret, paymentEntryModalOpen } = useValues(paymentEntryLogic)
const { hidePaymentEntryModal, initiateAuthorization } = useActions(paymentEntryLogic)

useEffect(() => {
initiateAuthorization(redirectPath)
}, [redirectPath])

return (
<LemonModal
onClose={hidePaymentEntryModal}
width="max(44vw)"
isOpen={paymentEntryModalOpen}
title="Add your payment details"
description="Your card will not be charged."
>
<div>
{clientSecret ? (
<Elements stripe={stripePromise} options={{ clientSecret }}>
<PaymentForm />
</Elements>
) : (
<div className="min-h-40 flex justify-center items-center">
<div className="text-4xl">
<Spinner />
</div>
</div>
)}
</div>
</LemonModal>
)
}
4 changes: 3 additions & 1 deletion frontend/src/scenes/billing/UnsubscribeCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { LemonButton, Link } from '@posthog/lemon-ui'
import { useActions } from 'kea'
import { supportLogic } from 'lib/components/Support/supportLogic'
import { UNSUBSCRIBE_SURVEY_ID } from 'lib/constants'

import { BillingProductV2Type } from '~/types'
Expand All @@ -8,6 +9,7 @@ import { billingProductLogic } from './billingProductLogic'

export const UnsubscribeCard = ({ product }: { product: BillingProductV2Type }): JSX.Element => {
const { reportSurveyShown, setSurveyResponse } = useActions(billingProductLogic({ product }))
const { openSupportForm } = useActions(supportLogic)

return (
<div className="p-5 gap-4 flex">
Expand All @@ -26,7 +28,7 @@ export const UnsubscribeCard = ({ product }: { product: BillingProductV2Type }):
reduce your bill
</Link>{' '}
or{' '}
<Link to="mailto:[email protected]?subject=Help%20reducing%20PostHog%20bill" target="_blank">
<Link to="" onClick={() => openSupportForm({ target_area: 'billing', isEmailFormOpen: true })}>
chat with support.
</Link>{' '}
Check out more about our pricing on our{' '}
Expand Down
Loading

0 comments on commit 9138acb

Please sign in to comment.