Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(data-warehouse): editing views #21261

Merged
merged 10 commits into from
Apr 2, 2024
130 changes: 73 additions & 57 deletions frontend/src/queries/nodes/HogQLQuery/HogQLQueryEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ const kindToSortText = (kind: AutocompleteCompletionItem['kind'], label: string)
export interface HogQLQueryEditorProps {
query: HogQLQuery
setQuery?: (query: HogQLQuery) => void
onChange?: (query: string) => void
embedded?: boolean
editorFooter?: (hasErrors: boolean, errors: string | null, isValidView: boolean) => JSX.Element
}

let uniqueNode = 0
Expand All @@ -105,7 +107,14 @@ export function HogQLQueryEditor(props: HogQLQueryEditorProps): JSX.Element {
null as [Monaco, importedEditor.IStandaloneCodeEditor] | null
)
const [monaco, editor] = monacoAndEditor ?? []
const hogQLQueryEditorLogicProps = { query: props.query, setQuery: props.setQuery, key, editor, monaco }
const hogQLQueryEditorLogicProps = {
query: props.query,
setQuery: props.setQuery,
onChange: props.onChange,
key,
editor,
monaco,
}
const logic = hogQLQueryEditorLogic(hogQLQueryEditorLogicProps)
const { queryInput, hasErrors, error, prompt, aiAvailable, promptError, promptLoading, isValidView } =
useValues(logic)
Expand Down Expand Up @@ -357,62 +366,69 @@ export function HogQLQueryEditor(props: HogQLQueryEditorProps): JSX.Element {
</div>
</div>
<div className="flex flex-row">
<div className="flex-1">
<LemonButton
onClick={saveQuery}
type="primary"
disabledReason={
!props.setQuery
? 'No permission to update'
: hasErrors
? error ?? 'Query has errors'
: undefined
}
center
fullWidth
data-attr="hogql-query-editor-save"
>
{!props.setQuery ? 'No permission to update' : 'Update and run'}
</LemonButton>
</div>
{featureFlags[FEATURE_FLAGS.DATA_WAREHOUSE] ? (
<LemonButton
className="ml-2"
onClick={saveAsView}
type="primary"
center
disabledReason={
hasErrors
? error ?? 'Query has errors'
: !isValidView
? 'All fields must have an alias'
: ''
}
data-attr="hogql-query-editor-save-as-view"
>
Save as View
</LemonButton>
) : null}
{featureFlags[FEATURE_FLAGS.DATA_WAREHOUSE] && (
<LemonButtonWithDropdown
className="ml-2"
icon={<IconInfo />}
type="secondary"
size="small"
dropdown={{
overlay: (
<div>
Save a query as a view that can be referenced in another query. This is useful
for modeling data and organizing large queries into readable chunks.{' '}
<Link to="https://posthog.com/docs/data-warehouse">More Info</Link>{' '}
</div>
),
placement: 'right-start',
fallbackPlacements: ['left-start'],
actionable: true,
closeParentPopoverOnClickInside: true,
}}
/>
{props.editorFooter ? (
props.editorFooter(hasErrors, error, isValidView)
) : (
<>
<div className="flex-1">
<LemonButton
onClick={saveQuery}
type="primary"
disabledReason={
!props.setQuery
? 'No permission to update'
: hasErrors
? error ?? 'Query has errors'
: undefined
}
center
fullWidth
data-attr="hogql-query-editor-save"
>
{!props.setQuery ? 'No permission to update' : 'Update and run'}
</LemonButton>
</div>
{featureFlags[FEATURE_FLAGS.DATA_WAREHOUSE] ? (
<LemonButton
className="ml-2"
onClick={saveAsView}
type="primary"
center
disabledReason={
hasErrors
? error ?? 'Query has errors'
: !isValidView
? 'All fields must have an alias'
: ''
}
data-attr="hogql-query-editor-save-as-view"
>
Save as View
</LemonButton>
) : null}
{featureFlags[FEATURE_FLAGS.DATA_WAREHOUSE] && (
<LemonButtonWithDropdown
className="ml-2"
icon={<IconInfo />}
type="secondary"
size="small"
dropdown={{
overlay: (
<div>
Save a query as a view that can be referenced in another query. This is
useful for modeling data and organizing large queries into readable
chunks.{' '}
<Link to="https://posthog.com/docs/data-warehouse">More Info</Link>{' '}
</div>
),
placement: 'right-start',
fallbackPlacements: ['left-start'],
actionable: true,
closeParentPopoverOnClickInside: true,
}}
/>
)}
</>
)}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface HogQLQueryEditorLogicProps {
key: number
query: HogQLQuery
setQuery?: (query: HogQLQuery) => void
onChange?: (query: string) => void
monaco?: Monaco | null
editor?: editor.IStandaloneCodeEditor | null
}
Expand Down Expand Up @@ -139,6 +140,7 @@ export const hogQLQueryEditorLogic = kea<hogQLQueryEditorLogicType>([
}
actions.setIsValidView(response?.isValidView || false)
actions.setModelMarkers(markers)
props.onChange?.(queryInput)
},
draftFromPrompt: async () => {
if (!values.aiAvailable) {
Expand Down
133 changes: 4 additions & 129 deletions frontend/src/scenes/data-warehouse/external/DataWarehouseTables.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
import { IconBrackets, IconDatabase } from '@posthog/icons'
import { LemonButton, Link } from '@posthog/lemon-ui'
import { Link } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { DatabaseTableTree, TreeItem } from 'lib/components/DatabaseTableTree/DatabaseTableTree'
import { EmptyMessage } from 'lib/components/EmptyMessage/EmptyMessage'
import { FEATURE_FLAGS } from 'lib/constants'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { humanFriendlyDetailedTime } from 'lib/utils'
import { DatabaseTable } from 'scenes/data-management/database/DatabaseTable'
import { urls } from 'scenes/urls'

import { NodeKind } from '~/queries/schema'

import { DataWarehouseRowType, DataWarehouseTableType } from '../types'
import { viewLinkLogic } from '../viewLinkLogic'
import { ViewLinkModal } from '../ViewLinkModal'
import { dataWarehouseSceneLogic } from './dataWarehouseSceneLogic'
import SourceModal from './SourceModal'
import { TableData } from './TableData'

export const DataWarehouseTables = (): JSX.Element => {
const {
Expand All @@ -24,52 +17,11 @@ export const DataWarehouseTables = (): JSX.Element => {
dataWarehouseLoading,
posthogTables,
savedQueriesFormatted,
allTables,
selectedRow,
dataWarehouseSavedQueriesLoading,
} = useValues(dataWarehouseSceneLogic)
const { toggleSourceModal, selectRow, deleteDataWarehouseSavedQuery, deleteDataWarehouseTable } =
useActions(dataWarehouseSceneLogic)
const { toggleSourceModal, selectRow } = useActions(dataWarehouseSceneLogic)
const { featureFlags } = useValues(featureFlagLogic)
const { toggleJoinTableModal, selectSourceTable } = useActions(viewLinkLogic)

const deleteButton = (selectedRow: DataWarehouseTableType | null): JSX.Element => {
if (!selectedRow) {
return <></>
}

if (selectedRow.type === DataWarehouseRowType.View) {
return (
<LemonButton
type="secondary"
onClick={() => {
deleteDataWarehouseSavedQuery(selectedRow.payload)
}}
>
Delete
</LemonButton>
)
}

if (selectedRow.type === DataWarehouseRowType.ExternalTable) {
return (
<LemonButton
type="secondary"
onClick={() => {
deleteDataWarehouseTable(selectedRow.payload)
}}
>
Delete
</LemonButton>
)
}

if (selectedRow.type === DataWarehouseRowType.PostHogTable) {
return <></>
}

return <></>
}

const treeItems = (): TreeItem[] => {
const items: TreeItem[] = [
Expand Down Expand Up @@ -126,84 +78,7 @@ export const DataWarehouseTables = (): JSX.Element => {
<div className="sm:col-span-3 md:col-span-1 max-h-160">
<DatabaseTableTree onSelectRow={selectRow} items={treeItems()} selectedRow={selectedRow} />
</div>
{selectedRow ? (
<div className="px-4 py-3 col-span-2">
<div className="flex flex-row justify-between items-center">
<h3>{selectedRow.name}</h3>
<div className="flex flex-row gap-2 justify-between">
{deleteButton(selectedRow)}
<LemonButton
type="primary"
onClick={() => {
selectSourceTable(selectedRow.name)
toggleJoinTableModal()
}}
>
Add Join
</LemonButton>
<Link
to={urls.insightNew(
undefined,
undefined,
JSON.stringify({
kind: NodeKind.DataTableNode,
full: true,
source: {
kind: NodeKind.HogQLQuery,
// TODO: Use `hogql` tag?
query: `SELECT ${selectedRow.columns
.filter(({ table, fields, chain }) => !table && !fields && !chain)
.map(({ key }) => key)} FROM ${selectedRow.name} LIMIT 100`,
},
})
)}
>
<LemonButton type="primary">Query</LemonButton>
</Link>
</div>
</div>
{selectedRow.type == DataWarehouseRowType.ExternalTable && (
<div className="flex flex-col">
<>
<span className="card-secondary mt-2">Last Synced At</span>
<span>
{selectedRow.payload.external_schema?.last_synced_at
? humanFriendlyDetailedTime(
selectedRow.payload.external_schema?.last_synced_at,
'MMMM DD, YYYY',
'h:mm A'
)
: 'Not yet synced'}
</span>
</>

<>
<span className="card-secondary mt-2">Files URL pattern</span>
<span>{selectedRow.payload.url_pattern}</span>
</>

<>
<span className="card-secondary mt-2">File format</span>
<span>{selectedRow.payload.format}</span>
</>
</div>
)}

<div className="mt-2">
<span className="card-secondary">Columns</span>
<DatabaseTable table={selectedRow.name} tables={allTables} />
</div>
</div>
) : (
<div className="px-4 py-3 h-100 col-span-2 flex justify-center items-center">
<EmptyMessage
title="No table selected"
description="Please select a table from the list on the left"
buttonText="Learn more about data warehouse tables"
buttonTo="https://posthog.com/docs/data-warehouse"
/>
</div>
)}
<TableData />
</div>
<SourceModal isOpen={isSourceModalOpen} onClose={() => toggleSourceModal(false)} />
<ViewLinkModal />
Expand Down
Loading
Loading