Skip to content

Commit

Permalink
feat(debug): tabs + better view for non-hogql-query nodes (#21867)
Browse files Browse the repository at this point in the history
  • Loading branch information
mariusandra authored Apr 26, 2024
1 parent ad906da commit c96a72c
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 151 deletions.
2 changes: 2 additions & 0 deletions frontend/src/queries/QueryEditor/QueryEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface QueryEditorProps {
query: string
setQuery?: (query: string) => void
className?: string
aboveButton?: JSX.Element
context?: QueryContext
}

Expand Down Expand Up @@ -78,6 +79,7 @@ export function QueryEditor(props: QueryEditorProps): JSX.Element {
<strong>Error parsing JSON:</strong> {error}
</div>
) : null}
{props.aboveButton}
<LemonButton
onClick={saveQuery}
type="primary"
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/queries/nodes/DataNode/ElapsedTime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { QueryTiming } from '~/queries/schema'

export interface TimingsProps {
timings: QueryTiming[]
elapsedTime: number
elapsedTime?: number
}

export function Timings({ timings, elapsedTime }: TimingsProps): JSX.Element | null {
Expand All @@ -26,7 +26,7 @@ export function Timings({ timings, elapsedTime }: TimingsProps): JSX.Element | n
<div>{time.toFixed(3)}s</div>
</div>
))}
{timings.length > 0 ? (
{elapsedTime !== undefined && timings.length > 0 ? (
<div className={clsx('flex justify-between items-start space-x-2')}>
<div>+ HTTP overhead</div>
<div>{(elapsedTime / 1000 - timings[timings.length - 1].t).toFixed(3)}s</div>
Expand Down
44 changes: 20 additions & 24 deletions frontend/src/scenes/debug/DebugScene.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
import { useActions, useValues } from 'kea'
import { CodeEditor } from 'lib/components/CodeEditors'
import { PageHeader } from 'lib/components/PageHeader'
import { LemonButton } from 'lib/lemon-ui/LemonButton'
import { LemonDivider } from 'lib/lemon-ui/LemonDivider'
import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel'
import { LemonSelect } from 'lib/lemon-ui/LemonSelect'
import { HogQLDebug } from 'scenes/debug/HogQLDebug'
import { Modifiers } from 'scenes/debug/Modifiers'
import { QueryTabs } from 'scenes/debug/QueryTabs'
import { SceneExport } from 'scenes/sceneTypes'

import { stringifiedExamples } from '~/queries/examples'
import { dataNodeLogic, DataNodeLogicProps } from '~/queries/nodes/DataNode/dataNodeLogic'
import { Query } from '~/queries/Query/Query'
import { QueryEditor } from '~/queries/QueryEditor/QueryEditor'
import { DataNode, HogQLQuery, Node } from '~/queries/schema'
import { isDataTableNode, isInsightVizNode } from '~/queries/utils'

import { debugSceneLogic } from './debugSceneLogic'

Expand Down Expand Up @@ -48,28 +45,27 @@ function QueryDebug({ query, setQuery, queryKey }: QueryDebugProps): JSX.Element
/>
) : (
<div className="space-y-4">
<QueryEditor query={query} setQuery={setQuery} />
<Modifiers
setQuery={
parsed?.source
? (query) => setQuery(JSON.stringify({ ...parsed, source: query }, null, 2))
: (query) => setQuery(JSON.stringify(query, null, 2))
}
query={parsed?.source ?? parsed}
response={response}
/>
<LemonDivider />
<Query
uniqueKey={queryKey}
<QueryEditor
query={query}
setQuery={(query) => setQuery(JSON.stringify(query, null, 2))}
setQuery={setQuery}
aboveButton={
<Modifiers
setQuery={
parsed?.source
? (query) => setQuery(JSON.stringify({ ...parsed, source: query }, null, 2))
: (query) => setQuery(JSON.stringify(query, null, 2))
}
query={parsed?.source ?? parsed}
response={response}
/>
}
/>
{response && parsed && (isDataTableNode(parsed as Node) || isInsightVizNode(parsed as Node)) ? (
<CodeEditor
className="border"
language="json"
value={JSON.stringify(response, null, 2)}
height={500}
{parsed ? (
<QueryTabs
query={parsed as Node}
queryKey={queryKey}
response={response}
setQuery={(query) => setQuery(JSON.stringify(query, null, 2))}
/>
) : null}
</div>
Expand Down
127 changes: 10 additions & 117 deletions frontend/src/scenes/debug/HogQLDebug.tsx
Original file line number Diff line number Diff line change
@@ -1,157 +1,50 @@
import { BindLogic, useValues } from 'kea'
import { CodeEditor } from 'lib/components/CodeEditors'
import { CodeSnippet, Language } from 'lib/components/CodeSnippet'
import { LemonTable } from 'lib/lemon-ui/LemonTable'
import { LemonDivider } from 'lib/lemon-ui/LemonDivider'
import { Modifiers } from 'scenes/debug/Modifiers'

import { dataNodeLogic, DataNodeLogicProps } from '~/queries/nodes/DataNode/dataNodeLogic'
import { DateRange } from '~/queries/nodes/DataNode/DateRange'
import { ElapsedTime, Timings } from '~/queries/nodes/DataNode/ElapsedTime'
import { ElapsedTime } from '~/queries/nodes/DataNode/ElapsedTime'
import { Reload } from '~/queries/nodes/DataNode/Reload'
import { EventPropertyFilters } from '~/queries/nodes/EventsNode/EventPropertyFilters'
import { HogQLQueryEditor } from '~/queries/nodes/HogQLQuery/HogQLQueryEditor'
import { DataNode, HogQLQuery, HogQLQueryResponse } from '~/queries/schema'

import { QueryTabs } from './QueryTabs'

interface HogQLDebugProps {
queryKey: string
query: HogQLQuery
setQuery: (query: DataNode) => void
}

function toLineColumn(hogql: string, position: number): { line: number; column: number } {
const lines = hogql.split('\n')
let line = 0
let column = 0
for (let i = 0; i < lines.length; i++) {
if (position < lines[i].length) {
line = i + 1
column = position + 1
break
}
position -= lines[i].length + 1
}
return { line, column }
}

function toLine(hogql: string, position: number): number {
return toLineColumn(hogql, position).line
}

function toColumn(hogql: string, position: number): number {
return toLineColumn(hogql, position).column
}

export function HogQLDebug({ query, setQuery, queryKey }: HogQLDebugProps): JSX.Element {
const dataNodeLogicProps: DataNodeLogicProps = { query, key: queryKey, dataNodeCollectionId: queryKey }
const {
dataLoading,
response: _response,
responseErrorObject,
elapsedTime,
} = useValues(dataNodeLogic(dataNodeLogicProps))
const { dataLoading, response: _response } = useValues(dataNodeLogic(dataNodeLogicProps))
const response = _response as HogQLQueryResponse | null
const clickHouseTime = response?.timings?.find(({ k }) => k === './clickhouse_execute')?.t

return (
<BindLogic logic={dataNodeLogic} props={dataNodeLogicProps}>
<div className="space-y-2">
<HogQLQueryEditor query={query} setQuery={setQuery} />
<Modifiers setQuery={setQuery} query={query} response={response} />
<LemonDivider className="my-4" />
<div className="flex gap-2">
<Reload />
<DateRange key="date-range" query={query} setQuery={setQuery} />
<EventPropertyFilters key="event-property" query={query} setQuery={setQuery} />
</div>
<Modifiers setQuery={setQuery} query={query} response={response} />
{dataLoading ? (
<>
<h2>Running query...</h2>
<div className="flex">
Time elapsed: <ElapsedTime />
Time elapsed:&nbsp;
<ElapsedTime />
</div>
</>
) : (
<>
{response?.error ? (
<>
<h2 className="text-danger">Error Running Query!</h2>
<CodeSnippet language={Language.Text} wrap>
{response.error}
</CodeSnippet>
</>
) : null}
{response?.hogql ? (
<>
<h2>Executed HogQL</h2>
<CodeSnippet language={Language.SQL} wrap>
{response.hogql}
</CodeSnippet>
</>
) : null}
{response?.clickhouse ? (
<>
<h2>
Executed ClickHouse SQL
{clickHouseTime !== undefined
? ` (${Math.floor(clickHouseTime * 1000) / 1000}s)`
: ''}
</h2>
<CodeSnippet language={Language.SQL} wrap>
{response.clickhouse}
</CodeSnippet>
</>
) : null}
{response?.metadata ? (
<>
<h2>Metadata</h2>
<LemonTable
dataSource={[
...response.metadata.errors.map((error) => ({
type: 'error',
line: toLine(response.hogql ?? '', error.start ?? 0),
column: toColumn(response.hogql ?? '', error.start ?? 0),
...error,
})),
...response.metadata.warnings.map((warn) => ({
type: 'warning',
line: toLine(response.hogql ?? '', warn.start ?? 0),
column: toColumn(response.hogql ?? '', warn.start ?? 0),
...warn,
})),
...response.metadata.notices.map((notice) => ({
type: 'notice',
line: toLine(response.hogql ?? '', notice.start ?? 0),
column: toColumn(response.hogql ?? '', notice.start ?? 0),
...notice,
})),
].sort((a, b) => (a.start ?? 0) - (b.start ?? 0))}
columns={[
{ title: 'Line', dataIndex: 'line', key: 'line', width: '40px' },
{ title: 'Column', dataIndex: 'column', key: 'column', width: '40px' },
{ title: 'Type', dataIndex: 'type', key: 'type', width: '80px' },
{ title: 'Message', dataIndex: 'message', key: 'message' },
]}
/>
</>
) : null}
{response?.explain ? (
<>
<h2>Explained ClickHouseSQL</h2>
<CodeSnippet wrap>{response.explain.join('\n')}</CodeSnippet>
</>
) : null}
{response?.timings && elapsedTime !== null ? (
<>
<h2>Time spent</h2>
<Timings timings={response.timings} elapsedTime={elapsedTime} />
</>
) : null}
<h2>Raw response</h2>
<CodeEditor
className="border"
language="json"
value={JSON.stringify(response ?? responseErrorObject, null, 2)}
height={800}
/>
<QueryTabs query={query} response={response} setQuery={setQuery} queryKey={queryKey} />
</>
)}
</div>
Expand Down
17 changes: 9 additions & 8 deletions frontend/src/scenes/debug/Modifiers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ export function Modifiers({ setQuery, query, response = null }: ModifiersProps):
if (query === null) {
return null
}
const labelClassName = 'flex flex-col gap-1 items-start'
return (
<div className="flex gap-2">
<LemonLabel>
POE:
<LemonLabel className={labelClassName}>
<div>POE:</div>
<LemonSelect
options={[
{ value: 'disabled', label: 'Disabled' },
Expand All @@ -42,8 +43,8 @@ export function Modifiers({ setQuery, query, response = null }: ModifiersProps):
value={query.modifiers?.personsOnEventsMode ?? response?.modifiers?.personsOnEventsMode}
/>
</LemonLabel>
<LemonLabel>
Persons ArgMax:
<LemonLabel className={labelClassName}>
<div>Persons ArgMax:</div>
<LemonSelect
options={[
{ value: 'v1', label: 'V1' },
Expand All @@ -58,8 +59,8 @@ export function Modifiers({ setQuery, query, response = null }: ModifiersProps):
value={query.modifiers?.personsArgMaxVersion ?? response?.modifiers?.personsArgMaxVersion}
/>
</LemonLabel>
<LemonLabel>
In Cohort Via:
<LemonLabel className={labelClassName}>
<div>In Cohort Via:</div>
<LemonSelect
options={[
{ value: 'auto', label: 'auto' },
Expand All @@ -76,8 +77,8 @@ export function Modifiers({ setQuery, query, response = null }: ModifiersProps):
value={query.modifiers?.inCohortVia ?? response?.modifiers?.inCohortVia}
/>
</LemonLabel>
<LemonLabel>
Materialization Mode:
<LemonLabel className={labelClassName}>
<div>Materialization Mode:</div>
<LemonSelect
options={[
{ value: 'auto', label: 'auto' },
Expand Down
Loading

0 comments on commit c96a72c

Please sign in to comment.