Skip to content

Commit

Permalink
feat(notebook): Allow creating early access from notebooks (#17264)
Browse files Browse the repository at this point in the history
  • Loading branch information
neilkakkar authored Aug 31, 2023
1 parent 7dcfb52 commit 5fdb0f6
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 5 deletions.
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 @@ -16,7 +16,7 @@ import { earlyAccessFeaturesLogic } from './earlyAccessFeaturesLogic'
import { teamLogic } from 'scenes/teamLogic'
import { lemonToast } from '@posthog/lemon-ui'

const NEW_EARLY_ACCESS_FEATURE: NewEarlyAccessFeatureType = {
export const NEW_EARLY_ACCESS_FEATURE: NewEarlyAccessFeatureType = {
name: '',
description: '',
stage: EarlyAccessFeatureStage.Draft,
Expand Down Expand Up @@ -54,7 +54,9 @@ export const earlyAccessFeatureLogic = kea<earlyAccessFeatureLogicType>([
}
return NEW_EARLY_ACCESS_FEATURE
},
saveEarlyAccessFeature: async (updatedEarlyAccessFeature: Partial<EarlyAccessFeatureType>) => {
saveEarlyAccessFeature: async (
updatedEarlyAccessFeature: Partial<EarlyAccessFeatureType | NewEarlyAccessFeatureType>
) => {
let result: EarlyAccessFeatureType
if (props.id === 'new') {
result = await api.earlyAccessFeatures.create(
Expand Down
34 changes: 33 additions & 1 deletion frontend/src/scenes/feature-flags/featureFlagLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
FeatureFlagGroupType,
UserBlastRadiusType,
DashboardBasicType,
NewEarlyAccessFeatureType,
EarlyAccessFeatureType,
} from '~/types'
import api from 'lib/api'
import { router, urlToAction } from 'kea-router'
Expand All @@ -37,6 +39,7 @@ import { featureFlagPermissionsLogic } from './featureFlagPermissionsLogic'
import { userLogic } from 'scenes/userLogic'
import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic'
import { dashboardsLogic } from 'scenes/dashboard/dashboards/dashboardsLogic'
import { NEW_EARLY_ACCESS_FEATURE } from 'scenes/early-access-features/earlyAccessFeatureLogic'

const getDefaultRollbackCondition = (): FeatureFlagRollbackConditions => ({
operator: 'gt',
Expand Down Expand Up @@ -402,9 +405,17 @@ export const featureFlagLogic = kea<featureFlagLogicType>([
},
}
},
createEarlyAccessFeatureSuccess: (state, { newEarlyAccessFeature }) => {
if (!state) {
return state
}
return {
...state,
features: [...(state.features || []), newEarlyAccessFeature],
}
},
},
],

featureFlagMissing: [false, { setFeatureFlagMissing: () => true }],
isEditingFlag: [
false,
Expand Down Expand Up @@ -503,6 +514,21 @@ export const featureFlagLogic = kea<featureFlagLogicType>([
},
},
],
// used to generate a new early access feature
// but all subsequent operations after generation should occur via the earlyAccessFeatureLogic
newEarlyAccessFeature: [
null as EarlyAccessFeatureType | null,
{
createEarlyAccessFeature: async () => {
const updatedEarlyAccessFeature = {
...NEW_EARLY_ACCESS_FEATURE,
name: `Early access: ${values.featureFlag.key}`,
feature_flag_id: values.featureFlag.id,
}
return await api.earlyAccessFeatures.create(updatedEarlyAccessFeature as NewEarlyAccessFeatureType)
},
},
],
})),
listeners(({ actions, values, props }) => ({
submitNewDashboardSuccessWithResult: async ({ result }) => {
Expand Down Expand Up @@ -837,6 +863,12 @@ export const featureFlagLogic = kea<featureFlagLogicType>([
}
},
],
hasEarlyAccessFeatures: [
(s) => [s.featureFlag],
(featureFlag) => {
return (featureFlag?.features?.length || 0) > 0
},
],
}),
urlToAction(({ actions, props }) => ({
[urls.featureFlag(props.id ?? 'new')]: (_, __, ___, { method }) => {
Expand Down
41 changes: 39 additions & 2 deletions frontend/src/scenes/notebooks/Nodes/NotebookNodeFlag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createPostHogWidgetNode } from 'scenes/notebooks/Nodes/NodeWrapper'
import { FeatureFlagType, NotebookNodeType } from '~/types'
import { BindLogic, useActions, useValues } from 'kea'
import { featureFlagLogic, FeatureFlagLogicProps } from 'scenes/feature-flags/featureFlagLogic'
import { IconFlag, IconRecording } from 'lib/lemon-ui/icons'
import { IconFlag, IconRecording, IconRocketLaunch } from 'lib/lemon-ui/icons'
import clsx from 'clsx'
import { LemonButton, LemonDivider } from '@posthog/lemon-ui'
import { urls } from 'scenes/urls'
Expand All @@ -14,15 +14,26 @@ import { buildCodeExampleContent } from './NotebookNodeFlagCodeExample'
import { FeatureFlagReleaseConditions } from 'scenes/feature-flags/FeatureFlagReleaseConditions'
import api from 'lib/api'
import { notebookLogic } from '../Notebook/notebookLogic'
import { buildEarlyAccessFeatureContent } from './NotebookNodeEarlyAccessFeature'
import { notebookNodeFlagLogic } from './NotebookNodeFlagLogic'

const Component = (props: NotebookNodeViewProps<NotebookNodeFlagAttributes>): JSX.Element => {
const { id } = props.node.attrs
const { featureFlag, featureFlagLoading, recordingFilterForFlag } = useValues(featureFlagLogic({ id }))
const {
featureFlag,
featureFlagLoading,
recordingFilterForFlag,
hasEarlyAccessFeatures,
newEarlyAccessFeatureLoading,
} = useValues(featureFlagLogic({ id }))
const { createEarlyAccessFeature } = useActions(featureFlagLogic({ id }))
const { expanded } = useValues(notebookNodeLogic)
const { insertAfter } = useActions(notebookNodeLogic)

const { nextNode } = useValues(notebookLogic)

const { shouldDisableInsertEarlyAccessFeature } = useValues(notebookNodeFlagLogic({ id, insertAfter }))

return (
<div>
<BindLogic logic={featureFlagLogic} props={{ id }}>
Expand Down Expand Up @@ -56,6 +67,32 @@ const Component = (props: NotebookNodeViewProps<NotebookNodeFlagAttributes>): JS

<LemonDivider className="my-0" />
<div className="p-2 mr-1 flex justify-end gap-2">
<LemonButton
type="secondary"
size="small"
icon={<IconRocketLaunch />}
loading={newEarlyAccessFeatureLoading}
onClick={(e) => {
// prevent expanding the node if it isn't expanded
e.stopPropagation()
if (!hasEarlyAccessFeatures) {
createEarlyAccessFeature()
} else {
if ((featureFlag?.features?.length || 0) <= 0) {
return
}
if (!shouldDisableInsertEarlyAccessFeature(nextNode) && featureFlag.features) {
insertAfter(buildEarlyAccessFeatureContent(featureFlag.features[0].id))
}
}
}}
disabledReason={
shouldDisableInsertEarlyAccessFeature(nextNode) &&
'Early access feature already exists below'
}
>
{hasEarlyAccessFeatures ? 'View' : 'Create'} early access feature
</LemonButton>
<LemonButton
type="secondary"
size="small"
Expand Down
43 changes: 43 additions & 0 deletions frontend/src/scenes/notebooks/Nodes/NotebookNodeFlagLogic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { connect, kea, key, listeners, path, props, selectors } from 'kea'
import { JSONContent, Node } from '../Notebook/utils'
import { FeatureFlagLogicProps, featureFlagLogic } from 'scenes/feature-flags/featureFlagLogic'
import { buildEarlyAccessFeatureContent } from './NotebookNodeEarlyAccessFeature'
import { NotebookNodeType } from '~/types'

import type { notebookNodeFlagLogicType } from './NotebookNodeFlagLogicType'

export type NotebookNodeFlagLogicProps = {
id: FeatureFlagLogicProps['id']
insertAfter: (content: JSONContent) => void
}

export const notebookNodeFlagLogic = kea<notebookNodeFlagLogicType>([
props({} as NotebookNodeFlagLogicProps),
path((key) => ['scenes', 'notebooks', 'Notebook', 'Nodes', 'notebookNodeFlagLogic', key]),
key(({ id }) => id),

connect((props: NotebookNodeFlagLogicProps) => ({
actions: [featureFlagLogic({ id: props.id }), ['createEarlyAccessFeatureSuccess']],
values: [featureFlagLogic({ id: props.id }), ['featureFlag', 'hasEarlyAccessFeatures']],
})),
listeners(({ props }) => ({
createEarlyAccessFeatureSuccess: async ({ newEarlyAccessFeature }) => {
props.insertAfter(buildEarlyAccessFeatureContent(newEarlyAccessFeature.id))
},
})),
selectors({
shouldDisableInsertEarlyAccessFeature: [
(s) => [s.featureFlag, s.hasEarlyAccessFeatures],
(featureFlag, hasEarlyAccessFeatures) =>
(nextNode: Node | null): boolean => {
return (
(nextNode?.type.name === NotebookNodeType.EarlyAccessFeature &&
hasEarlyAccessFeatures &&
featureFlag.features &&
nextNode?.attrs.id === featureFlag.features[0].id) ||
false
)
},
],
}),
])

0 comments on commit 5fdb0f6

Please sign in to comment.