Skip to content

Commit

Permalink
Merge branch 'master' into chore/upgrade-django-4-1-try3
Browse files Browse the repository at this point in the history
# Conflicts:
#	posthog/temporal/data_imports/external_data_job.py
  • Loading branch information
webjunkie committed Feb 6, 2024
2 parents f116a87 + 19b52ae commit c47cb09
Show file tree
Hide file tree
Showing 65 changed files with 1,721 additions and 242 deletions.
15 changes: 15 additions & 0 deletions cypress.e2e.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { defineConfig } from 'cypress'
import { createClient } from 'redis'
import webpackPreprocessor from '@cypress/webpack-preprocessor'
import { PNG } from 'pngjs'
import pixelmatch from 'pixelmatch'
Expand Down Expand Up @@ -93,6 +94,20 @@ export default defineConfig({
return true
})
},

async resetInsightCache() {
const redisClient = await createClient()
.on('error', (err) => console.log('Redis client error', err))
.connect()
for await (const key of redisClient.scanIterator({
TYPE: 'string',
MATCH: '*cache*',
COUNT: 100,
})) {
await redisClient.del(key)
}
return null
},
})

return config
Expand Down
66 changes: 66 additions & 0 deletions cypress/e2e/dashboard-shared.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { dashboards } from '../productAnalytics'

describe('Shared dashboard', () => {
beforeEach(() => {
cy.intercept('GET', /api\/projects\/\d+\/insights\/\?.*/).as('loadInsightList')
cy.intercept('PATCH', /api\/projects\/\d+\/insights\/\d+\/.*/).as('patchInsight')
cy.intercept('POST', /\/api\/projects\/\d+\/dashboards/).as('createDashboard')

cy.clickNavMenu('dashboards')
})

it('Dashboard sharing can be enabled', () => {
dashboards.createDashboardFromDefaultTemplate('to be shared')

cy.get('.InsightCard').should('exist')

cy.get('[data-attr=dashboard-share-button]').click()
cy.get('[data-attr=sharing-switch]').click({ force: true })

cy.contains('Embed dashboard').should('be.visible')
cy.get('[data-attr=copy-code-button]').click()
cy.window()
.its('navigator.clipboard')
.then((c) => c.readText())
.should('contain', '<iframe')
cy.window()
.its('navigator.clipboard')
.then((c) => c.readText())
.should('contain', '/embedded/')

cy.contains('Copy public link').should('be.visible')
cy.get('[data-attr=sharing-link-button]').click()
cy.window()
.its('navigator.clipboard')
.then((c) => c.readText())
.should('contain', '/shared/')
})

it('Insights load when cache is empty', () => {
cy.get('h1').should('contain', 'Dashboards')

dashboards.createDashboardFromDefaultTemplate('Foobar 3001')

cy.get('[data-attr=dashboard-share-button]').click()
cy.get('[data-attr=sharing-switch]').click({ force: true })

cy.contains('Copy public link').should('be.visible')
cy.get('[data-attr=sharing-link-button]').click()
cy.window()
.its('navigator.clipboard')
.then((clipboard) => {
cy.wrap(clipboard.readText()).as('clipboardText')
})

cy.task('resetInsightCache')

cy.window().then(async (win) => {
const text = await win.navigator.clipboard.readText()
cy.visit(text)
})

// Make sure no element with text "There are no matching events for this query" exists
cy.get('.InsightCard').should('have.length', 6)
cy.get('.insight-empty-state').should('not.exist')
})
})
38 changes: 5 additions & 33 deletions cypress/e2e/dashboard.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,39 +64,11 @@ describe('Dashboard', () => {
cy.get('[data-attr=dashboard-tags]').should('not.exist')
})

// TODO: not implemented in 3000
// it('Pinned dashboards on menu', () => {
// cy.clickNavMenu('events') // to make sure the dashboards menu item is not the active one
// cy.get('[data-attr=menu-item-pinned-dashboards]').click()
// cy.get('[data-attr=sidebar-pinned-dashboards]').should('be.visible')
// cy.get('[data-attr=sidebar-pinned-dashboards] a').should('contain', 'App Analytics')
// })

it('Share dashboard', () => {
dashboards.createDashboardFromDefaultTemplate('to be shared')

cy.get('.InsightCard').should('exist')

cy.get('[data-attr=dashboard-share-button]').click()
cy.get('[data-attr=sharing-switch]').click({ force: true })

cy.contains('Embed dashboard').should('be.visible')
cy.get('[data-attr=copy-code-button]').click()
cy.window()
.its('navigator.clipboard')
.then((c) => c.readText())
.should('contain', '<iframe')
cy.window()
.its('navigator.clipboard')
.then((c) => c.readText())
.should('contain', '/embedded/')

cy.contains('Copy public link').should('be.visible')
cy.get('[data-attr=sharing-link-button]').click()
cy.window()
.its('navigator.clipboard')
.then((c) => c.readText())
.should('contain', '/shared/')
it('Pinned dashboards on menu', () => {
cy.clickNavMenu('events') // to make sure the dashboards menu item is not the active one
cy.get('[data-attr=menu-item-pinned-dashboards-dropdown]').click()
cy.get('.Popover').should('be.visible')
cy.get('.Popover a').should('contain', 'App Analytics')
})

it('Create an empty dashboard', () => {
Expand Down
63 changes: 63 additions & 0 deletions cypress/e2e/featureFlags.cy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
import { decideResponse } from '../fixtures/api/decide'

describe('Feature Flags', () => {
let name

beforeEach(() => {

cy.intercept('https://app.posthog.com/decide/*', (req) =>
req.reply(
decideResponse({
'new-feature-flag-operators': true,
})
)
)

cy.intercept('/api/projects/1/property_definitions?type=person&search*', {
fixture: 'api/feature-flags/property_definition',
})
cy.intercept('/api/person/values/*', {
fixture: 'api/feature-flags/property_values',
})
name = 'feature-flag-' + Math.floor(Math.random() * 10000000)
cy.visit('/feature_flags')
})
Expand Down Expand Up @@ -99,4 +116,50 @@ describe('Feature Flags', () => {
cy.get('[data-attr=delete-feature-flag]').click()
cy.get('.Toastify').contains('Undo').should('be.visible')
})

it.only('Move between property types smoothly, and support relative dates', () => {
// ensure unique names to avoid clashes
cy.get('[data-attr=top-bar-name]').should('contain', 'Feature flags')
cy.get('[data-attr=new-feature-flag]').click()
cy.get('[data-attr=feature-flag-key]').click().type(`{moveToEnd}${name}`).should('have.value', name)

// select "add filter" and "property"
cy.get('[data-attr=property-select-toggle-0').click()

// select the first property
cy.get('[data-attr=taxonomic-filter-searchfield]').click()
cy.get('[data-attr=taxonomic-filter-searchfield]').type('is_demo')
cy.get('[data-attr=taxonomic-tab-person_properties]').click()
// select numeric $browser_version
cy.get('[data-attr=prop-filter-person_properties-2]').click({ force: true })
// select operator "is greater than" which isn't present for non-numeric properties
cy.get('[data-attr=taxonomic-operator]').contains('= equals').click({ force: true })
cy.get('.operator-value-option').contains('> greater than').click({ force: true })

// selects the first value
cy.get('[data-attr=prop-val]').click()
cy.get('[data-attr=prop-val-0]').click({ force: true })

// now change property type
cy.get('[data-attr=property-select-toggle-0').click()
cy.get('[data-attr=taxonomic-tab-person_properties]').click()

// select dateTime date_prop
cy.get('[data-attr=prop-filter-person_properties-3]').click({ force: true })
cy.get('[data-attr=taxonomic-operator]').contains('= equals').click({ force: true })
cy.get('.operator-value-option').contains('> after').click({ force: true })

// By default says "Select a value"
cy.get('[data-attr=taxonomic-value-select]').contains('Select a value').click()
cy.get('.Popover__content').contains('Last 7 days').click({ force: true})
cy.get('[data-attr=taxonomic-value-select]').contains('Last 7 days')

// now change property type
cy.get('[data-attr=property-select-toggle-0').click()
cy.get('[data-attr=taxonomic-tab-person_properties]').click()
// select regular prop
cy.get('[data-attr=prop-filter-person_properties-1]').click({ force: true })
cy.get('[data-attr=taxonomic-operator]').contains('= equals').click({ force: true })
cy.get('.operator-value-option').contains('> after').should('not.exist')
})
})
39 changes: 39 additions & 0 deletions cypress/fixtures/api/feature-flags/property_definition.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"count": 4,
"next": null,
"previous": null,
"results": [
{
"id": "017dde0e-1cb5-0000-68b4-44835b7c894k",
"name": "is_demo",
"is_numerical": false,
"query_usage_30_day": null,
"property_type": null,
"is_seen_on_filtered_events": null
},
{
"id": "017dde0e-1cb5-0000-68b4-44835b7c894f",
"name": "$feature/awesome-new-hidden-thing",
"is_numerical": false,
"query_usage_30_day": null,
"property_type": "String",
"is_seen_on_filtered_events": null
},
{
"id": "017dde0e-1cb5-0000-68b4-44835b7c894g",
"name": "$browser_version",
"is_numerical": true,
"query_usage_30_day": null,
"property_type": null,
"is_seen_on_filtered_events": null
},
{
"id": "017dde0e-1cb5-0000-68b4-44835b7c894h",
"name": "date_prop",
"is_numerical": false,
"query_usage_30_day": null,
"property_type": "DateTime",
"is_seen_on_filtered_events": null
}
]
}
13 changes: 13 additions & 0 deletions cypress/fixtures/api/feature-flags/property_values.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{ "name": "3", "count": 160 },
{ "name": "is_demo", "count": 160 },
{ "name": "email", "count": 36 },
{ "name": "address", "count": 35 },
{ "name": "name", "count": 35 },
{ "name": "phone", "count": 35 },
{ "name": "$browser", "count": 1 },
{ "name": "$browser_version", "count": 1 },
{ "name": "$initial_referrer", "count": 1 },
{ "name": "$initial_referring_domain", "count": 1 },
{ "name": "$os", "count": 1 }
]
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.
2 changes: 1 addition & 1 deletion frontend/src/layout/navigation/ProjectNotice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export function ProjectNotice(): JSX.Element | null {
action: {
'data-attr': 'stop-impersonation-cta',
onClick: () => logout(),
children: 'Logout',
children: 'Log out',
},
},
}
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/layout/navigation/TopBar/AccountPopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ export function AccountPopoverOverlay(): JSX.Element {
const { currentOrganization } = useValues(organizationLogic)
const { mobileLayout } = useValues(navigationLogic)
const { openSidePanel } = useActions(sidePanelStateLogic)
const { preflight, isCloudOrDev } = useValues(preflightLogic)
const { preflight, isCloudOrDev, isCloud } = useValues(preflightLogic)
const { closeAccountPopover } = useActions(navigationLogic)

return (
Expand Down Expand Up @@ -248,7 +248,7 @@ export function AccountPopoverOverlay(): JSX.Element {
<FeaturePreviewsButton />
{user?.is_staff && <InstanceSettings />}
</AccountPopoverSection>
{!isCloudOrDev && (
{!isCloud && (
<AccountPopoverSection>
<LemonButton
onClick={closeAccountPopover}
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/lib/components/DateFilter/DateFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { useActions, useValues } from 'kea'
import {
CUSTOM_OPTION_DESCRIPTION,
CUSTOM_OPTION_KEY,
CUSTOM_OPTION_VALUE,
DateFilterLogicProps,
DateFilterView,
NO_OVERRIDE_RANGE_PLACEHOLDER,
} from 'lib/components/DateFilter/types'
import { dayjs } from 'lib/dayjs'
import { IconCalendar } from 'lib/lemon-ui/icons'
Expand Down Expand Up @@ -163,7 +163,7 @@ export function DateFilter({
active={isActive}
fullWidth
>
{key === CUSTOM_OPTION_KEY ? CUSTOM_OPTION_VALUE : key}
{key === CUSTOM_OPTION_KEY ? NO_OVERRIDE_RANGE_PLACEHOLDER : key}
</LemonButton>
</Tooltip>
)
Expand Down
27 changes: 23 additions & 4 deletions frontend/src/lib/components/DateFilter/dateFilterLogic.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { actions, kea, key, listeners, path, props, reducers, selectors } from 'kea'
import { CUSTOM_OPTION_VALUE, DateFilterLogicProps, DateFilterView } from 'lib/components/DateFilter/types'
import {
DateFilterLogicProps,
DateFilterView,
NO_OVERRIDE_RANGE_PLACEHOLDER,
SELECT_FIXED_VALUE_PLACEHOLDER,
} from 'lib/components/DateFilter/types'
import { Dayjs, dayjs } from 'lib/dayjs'
import { dateFilterToText, dateStringToDayJs, formatDate, formatDateRange, isDate } from 'lib/utils'

Expand Down Expand Up @@ -95,15 +100,29 @@ export const dateFilterLogic = kea<dateFilterLogicType>([
),
],
label: [
(s) => [s.dateFrom, s.dateTo, s.isFixedRange, s.isDateToNow, s.isFixedDate, s.dateOptions],
(dateFrom, dateTo, isFixedRange, isDateToNow, isFixedDate, dateOptions) =>
(s) => [
s.dateFrom,
s.dateTo,
s.isFixedRange,
s.isDateToNow,
s.isFixedDate,
s.dateOptions,
(_, p) => p.isFixedDateMode,
],
(dateFrom, dateTo, isFixedRange, isDateToNow, isFixedDate, dateOptions, isFixedDateMode) =>
isFixedRange
? formatDateRange(dayjs(dateFrom), dayjs(dateTo))
: isDateToNow
? `${formatDate(dayjs(dateFrom))} to now`
: isFixedDate
? formatDate(dateStringToDayJs(dateFrom) ?? dayjs(dateFrom))
: dateFilterToText(dateFrom, dateTo, CUSTOM_OPTION_VALUE, dateOptions, false),
: dateFilterToText(
dateFrom,
dateTo,
isFixedDateMode ? SELECT_FIXED_VALUE_PLACEHOLDER : NO_OVERRIDE_RANGE_PLACEHOLDER,
dateOptions,
false
),
],
}),
listeners(({ actions, values, props }) => ({
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/lib/components/DateFilter/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ export type DateFilterLogicProps = {
}

export const CUSTOM_OPTION_KEY = 'Custom'
export const CUSTOM_OPTION_VALUE = 'No date range override'
export const SELECT_FIXED_VALUE_PLACEHOLDER = 'Select a value'
export const NO_OVERRIDE_RANGE_PLACEHOLDER = 'No date range override'
export const CUSTOM_OPTION_DESCRIPTION = 'Use the original date ranges of insights'
19 changes: 11 additions & 8 deletions frontend/src/lib/components/HogQLEditor/HogQLEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,18 @@ export function HogQLEditor({
className={`font-mono ${CLICK_OUTSIDE_BLOCK_CLASS}`}
minRows={3}
maxRows={6}
placeholder={
placeholder ??
(metadataSource && isActorsQuery(metadataSource)
? "Enter HogQL expression, such as:\n- properties.$geoip_country_name\n- toInt(properties.$browser_version) * 10\n- concat(properties.name, ' <', properties.email, '>')\n- is_identified ? 'user' : 'anon'"
: disablePersonProperties
? "Enter HogQL expression, such as:\n- properties.$current_url\n- toInt(properties.`Long Field Name`) * 10\n- concat(event, ' ', distinct_id)\n- if(1 < 2, 'small', 'large')"
: "Enter HogQL Expression, such as:\n- properties.$current_url\n- person.properties.$geoip_country_name\n- toInt(properties.`Long Field Name`) * 10\n- concat(event, ' ', distinct_id)\n- if(1 < 2, 'small', 'large')")
}
placeholder="properties.$browser"
/>
<div className="text-muted pt-2 text-xs">
<pre>
{placeholder ??
(metadataSource && isActorsQuery(metadataSource)
? "Enter HogQL expression, such as:\n- properties.$geoip_country_name\n- toInt(properties.$browser_version) * 10\n- concat(properties.name, ' <', properties.email, '>')\n- is_identified ? 'user' : 'anon'"
: disablePersonProperties
? "Enter HogQL expression, such as:\n- properties.$current_url\n- toInt(properties.`Long Field Name`) * 10\n- concat(event, ' ', distinct_id)\n- if(1 < 2, 'small', 'large')"
: "Enter HogQL Expression, such as:\n- properties.$current_url\n- person.properties.$geoip_country_name\n- toInt(properties.`Long Field Name`) * 10\n- concat(event, ' ', distinct_id)\n- if(1 < 2, 'small', 'large')")}
</pre>
</div>
{error ? (
<div className="text-danger flex mt-1 gap-1 text-sm max-h-20 overflow-auto">
<IconErrorOutline className="text-xl" />
Expand Down
Loading

0 comments on commit c47cb09

Please sign in to comment.