Merge branch 'supabase:master' into master

This commit is contained in:
Sara Tavares authored and GitHub committed 2022-10-24 16:55:31 +01:00
commit 1e5592d13b
18 files changed
+281 -199

No files matched your search

+1 -1
View File
@@ -134,7 +134,7 @@ Our approach for client libraries is modular. Each sub-library is a standalone i
<td>-</td>
<td>-</td>
<td><a href="https://github.com/supabase-community/storage-go" target="_blank" rel="noopener noreferrer">storage-go</a></td>
<td>-</td>
<td><a href="https://github.com/supabase-community/functions-go" target="_blank" rel="noopener noreferrer">functions-go</a></td>
</tr>
<tr>
<td>Java</td>
@@ -33,7 +33,7 @@ const LogEventChart: React.FC<Props> = ({ data, onBarClick }) => {
chartSize="tiny"
data={transformedData}
attribute="count"
label="Events"
label="Logs / Time"
onBarClick={(v?: { activePayload?: { payload: any }[] }) => {
if (!v || !v?.activePayload?.[0]?.payload) return
const unixOrIsoTimestamp = v.activePayload[0].payload.timestamp
@@ -1,5 +1,5 @@
import { FC } from 'react'
import { IconX } from 'ui'
import { Button, Divider, IconX } from 'ui'
import { LogData, QueryType } from './Logs.types'
@@ -9,9 +9,15 @@ import FunctionInvocationSelectionRender from './LogSelectionRenderers/FunctionI
import FunctionLogsSelectionRender from './LogSelectionRenderers/FunctionLogsSelectionRender'
import DefaultExplorerSelectionRenderer from './LogSelectionRenderers/DefaultExplorerSelectionRenderer'
import DefaultPreviewSelectionRenderer from './LogSelectionRenderers/DefaultPreviewSelectionRenderer'
import { isDefaultLogPreviewFormat, LogsEndpointParams } from '.'
import {
isDefaultLogPreviewFormat,
isUnixMicro,
LogsEndpointParams,
unixMicroToIsoTimestamp,
} from '.'
import useSingleLog from 'hooks/analytics/useSingleLog'
import Connecting from 'components/ui/Loading/Loading'
import CopyButton from 'components/ui/CopyButton'
export interface LogSelectionProps {
log: LogData | null
@@ -64,6 +70,23 @@ const LogSelection: FC<LogSelectionProps> = ({
}
}
const selectionText = () => {
if (!fullLog) return ''
if (queryType) {
return `Log ID
${fullLog.id}\n
Log Timestamp (UTC)
${isUnixMicro(fullLog.timestamp) ? unixMicroToIsoTimestamp(fullLog.timestamp) : fullLog.timestamp}\n
Log Event Message
${fullLog.event_message}\n
Log Metadata
${JSON.stringify(fullLog.metadata, null, 2)}
`
}
return JSON.stringify(fullLog, null, 2)
}
return (
<div
className={[
@@ -123,22 +146,24 @@ const LogSelection: FC<LogSelectionProps> = ({
</div>
</div>
</div>
<div
className="
relative
h-px
flex-grow
bg-scale-300
"
>
<div
className="absolute top-6 right-6 cursor-pointer text-scale-900 transition hover:text-scale-1200"
onClick={onClose}
>
<IconX size={14} strokeWidth={2} />
<div className="relative h-px flex-grow bg-scale-300">
<div className="pt-4 px-4 flex flex-col gap-4">
<div className="flex flex-row justify-between items-center">
<div className={`transition ${!isLoading ? 'opacity-100' : 'opacity-0'}`}>
<CopyButton text={selectionText()} type="default" title="Copy log to clipboard" />
</div>
<Button
type="text"
className="cursor-pointer transition hover:text-scale-1200 h-8 w-8 px-0 py-0 flex items-center justify-center"
onClick={onClose}
>
<IconX size={14} strokeWidth={2} className="text-scale-900" />
</Button>
</div>
<div className="h-px w-full bg-scale-600 rounded " />
</div>
{isLoading && <Connecting />}
<div className="flex flex-col space-y-6 bg-scale-300 py-8">
<div className="flex flex-col space-y-6 bg-scale-300 py-4">
{!isLoading && <Formatter />}
</div>
</div>
@@ -1,7 +1,7 @@
import React from 'react'
import { LOGS_TAILWIND_CLASSES } from '../Logs.constants'
import LogsDivider from '../Logs.Divider'
import { jsonSyntaxHighlight, ResponseCodeFormatter } from '../LogsFormatters'
import { jsonSyntaxHighlight, ResponseCodeFormatter, SelectionDetailedRow } from '../LogsFormatters'
const DatabaseApiSelectionRender = ({ log }: any) => {
const request = log?.metadata[0]?.request?.[0]
@@ -13,27 +13,16 @@ const DatabaseApiSelectionRender = ({ log }: any) => {
const clientInfo = request?.headers?.[0]?.x_client_info
const referer = request?.headers?.[0]?.referer
const DetailedRow = ({ label, value }: { label: string; value: string | React.ReactNode }) => {
return (
<div className="grid grid-cols-12">
<span className="text-scale-900 text-sm col-span-4 whitespace-pre-wrap">{label}</span>
<span className="text-scale-1200 text-sm col-span-8 whitespace-pre-wrap break-all">
{value}
</span>
</div>
)
}
return (
<>
<div className={`${LOGS_TAILWIND_CLASSES.log_selection_x_padding} space-y-2`}>
<DetailedRow label="Status" value={<ResponseCodeFormatter value={status} />} />
<DetailedRow label="Method" value={method} />
<DetailedRow label="Timestamp" value={log.timestamp} />
<DetailedRow label="IP Address" value={ipAddress} />
<DetailedRow label="Origin Country" value={countryOrigin} />
{clientInfo && <DetailedRow label="Client" value={clientInfo} />}
{referer && <DetailedRow label="Referer" value={referer} />}
<SelectionDetailedRow label="Status" value={status} valueRender={<ResponseCodeFormatter value={status} />} />
<SelectionDetailedRow label="Method" value={method} />
<SelectionDetailedRow label="Timestamp" value={log.timestamp} />
<SelectionDetailedRow label="IP Address" value={ipAddress} />
<SelectionDetailedRow label="Origin Country" value={countryOrigin} />
{clientInfo && <SelectionDetailedRow label="Client" value={clientInfo} />}
{referer && <SelectionDetailedRow label="Referer" value={referer} />}
</div>
<LogsDivider />
<div className={`${LOGS_TAILWIND_CLASSES.log_selection_x_padding}`}>
@@ -2,21 +2,13 @@ import { Alert } from 'ui'
import React from 'react'
import { LOGS_TAILWIND_CLASSES } from '../Logs.constants'
import LogsDivider from '../Logs.Divider'
import { jsonSyntaxHighlight, SeverityFormatter } from '../LogsFormatters'
import { jsonSyntaxHighlight, SelectionDetailedRow, SeverityFormatter } from '../LogsFormatters'
const DatabasePostgresSelectionRender = ({ log }: any) => {
const postgresUsername = log?.metadata[0]?.parsed[0]?.user_name
const sessionId = log?.metadata[0]?.parsed[0]?.session_id
const hint = log?.metadata[0]?.parsed[0]?.hint
const DetailedRow = ({ label, value }: { label: string; value: string | React.ReactNode }) => {
return (
<div className="grid grid-cols-12">
<p className="col-span-4 whitespace-pre-wrap text-sm text-scale-900">{label}</p>
<p className="col-span-8 whitespace-pre-wrap text-base text-scale-1200">{value}</p>
</div>
)
}
const errorSeverity = log?.metadata[0]?.parsed[0]?.error_severity
return (
<>
@@ -29,13 +21,17 @@ const DatabasePostgresSelectionRender = ({ log }: any) => {
</div>
<LogsDivider />
<div className={`${LOGS_TAILWIND_CLASSES.log_selection_x_padding} space-y-2`}>
<DetailedRow label="Severity" value={<SeverityFormatter value={log.error_severity} />} />
<DetailedRow label="Postgres Username" value={postgresUsername} />
<DetailedRow label="Session ID" value={sessionId} />
<SelectionDetailedRow
label="Severity"
value={errorSeverity}
valueRender={<SeverityFormatter value={errorSeverity} />}
/>
<SelectionDetailedRow label="Postgres Username" value={postgresUsername} />
<SelectionDetailedRow label="Session ID" value={sessionId} />
</div>
{hint && (
<div className={`mt-4 ${LOGS_TAILWIND_CLASSES.log_selection_x_padding}`}>
<Alert variant="warning" withIcon title={log?.metadata[0]?.parsed[0]?.hint} />
<Alert variant="warning" withIcon title={hint} />
</div>
)}
<LogsDivider />
@@ -1,30 +1,7 @@
import { LOGS_TAILWIND_CLASSES } from '../Logs.constants'
import { jsonSyntaxHighlight } from '../LogsFormatters'
import { jsonSyntaxHighlight, SelectionDetailedRow } from '../LogsFormatters'
const DefaultExplorerSelectionRenderer = ({ log }: any) => {
const DetailedRow = ({
label,
value,
code,
}: {
label: string
value: string | React.ReactNode
code?: boolean
}) => {
return (
<div className="grid grid-cols-12">
<span className="text-scale-900 text-sm col-span-4 whitespace-prep-wrap">{label}</span>
<span
className={`text-scale-1200 text-sm col-span-8 whitespace-prep-wrap ${
code && 'text-xs font-mono'
}`}
>
{value}
</span>
</div>
)
}
const DetailedJsonRow = ({
label,
value,
@@ -63,7 +40,7 @@ const DefaultExplorerSelectionRenderer = ({ log }: any) => {
{value && typeof value === 'object' ? (
<DetailedJsonRow label={key} value={value} />
) : (
<DetailedRow label={key} value={value === null ? 'NULL ' : String(value)} />
<SelectionDetailedRow label={key} value={value === null ? 'NULL ' : String(value)} />
)}
</div>
)
@@ -1,55 +1,30 @@
import { isUnixMicro, unixMicroToIsoTimestamp } from '..'
import { isUnixMicro, PreviewLogData, unixMicroToIsoTimestamp } from '..'
import { LOGS_TAILWIND_CLASSES } from '../Logs.constants'
import { jsonSyntaxHighlight } from '../LogsFormatters'
import { jsonSyntaxHighlight, SelectionDetailedRow } from '../LogsFormatters'
const DefaultPreviewSelectionRenderer = ({ log }: any) => {
const DetailedRow = ({
label,
value,
code,
}: {
label: string
value: string | React.ReactNode
code?: boolean
}) => {
return (
<div className="grid grid-cols-12">
<span className="text-scale-900 text-sm col-span-4 whitespace-prep-wrap">{label}</span>
<span
className={`text-scale-1200 text-sm col-span-8 whitespace-prep-wrap ${
code && 'text-xs font-mono'
}`}
>
{value}
</span>
</div>
)
}
return (
<div className={`${LOGS_TAILWIND_CLASSES.log_selection_x_padding} space-y-6`}>
<div className="flex flex-col gap-3">
<h3 className="text-scale-900 text-sm">Event Message</h3>
<div className="text-xs text-wrap font-mono text-scale-1200 whitespace-pre-wrap overflow-x-auto">
{log.event_message}
</div>
</div>
<DetailedRow
label="ISO Timestamp"
value={isUnixMicro(log.timestamp) ? unixMicroToIsoTimestamp(log.timestamp) : log.timestamp}
/>
<div className="flex flex-col gap-3">
<h3 className="text-scale-900 text-sm">Metadata</h3>
<pre
className=" className={`text-scale-1200 text-sm col-span-8 overflow-x-auto text-xs font-mono`}"
dangerouslySetInnerHTML={{
__html: jsonSyntaxHighlight(log.metadata),
}}
/>
const DefaultPreviewSelectionRenderer = ({ log }: { log: PreviewLogData }) => (
<div className={`${LOGS_TAILWIND_CLASSES.log_selection_x_padding} space-y-6`}>
<div className="flex flex-col gap-3">
<h3 className="text-scale-900 text-sm">Event Message</h3>
<div className="text-xs text-wrap font-mono text-scale-1200 whitespace-pre-wrap overflow-x-auto">
{log.event_message}
</div>
</div>
)
}
<SelectionDetailedRow
label="ISO Timestamp"
value={isUnixMicro(log.timestamp) ? unixMicroToIsoTimestamp(log.timestamp) : String(log.timestamp)}
/>
<div className="flex flex-col gap-3">
<h3 className="text-scale-900 text-sm">Metadata</h3>
<pre
className=" className={`text-scale-1200 text-sm col-span-8 overflow-x-auto text-xs font-mono`}"
dangerouslySetInnerHTML={{
__html: jsonSyntaxHighlight(log.metadata!),
}}
/>
</div>
</div>
)
export default DefaultPreviewSelectionRenderer
@@ -2,7 +2,7 @@ import dayjs from 'dayjs'
import { filterFunctionsRequestResponse } from 'lib/logs'
import { PreviewLogData } from '..'
import { LOGS_TAILWIND_CLASSES } from '../Logs.constants'
import { jsonSyntaxHighlight, ResponseCodeFormatter } from '../LogsFormatters'
import { jsonSyntaxHighlight, ResponseCodeFormatter, SelectionDetailedRow } from '../LogsFormatters'
const FunctionInvocationSelectionRender = ({ log }: { log: PreviewLogData }) => {
const metadata = log.metadata?.[0]
@@ -15,39 +15,22 @@ const FunctionInvocationSelectionRender = ({ log }: { log: PreviewLogData }) =>
const deploymentId = metadata.deployment_id
const timestamp = dayjs(log.timestamp / 1000)
const DetailedRow = ({
label,
value,
code,
}: {
label: string
value: string | React.ReactNode
code?: boolean
}) => {
return (
<div className="grid grid-cols-12">
<span className="text-scale-900 text-sm col-span-4 whitespace-pre-wrap">{label}</span>
<span
className={`text-scale-1200 text-sm col-span-8 whitespace-pre-wrap ${
code && 'text-xs font-mono'
}`}
>
{value}
</span>
</div>
)
}
return (
<>
<div className={`${LOGS_TAILWIND_CLASSES.log_selection_x_padding} space-y-2`}>
<DetailedRow label="Status" value={<ResponseCodeFormatter value={status} />} />
<DetailedRow label="Method" value={method} />
<DetailedRow label="Timestamp" value={dayjs(timestamp).format('DD MMM, YYYY HH:mm')} />
<DetailedRow label="Execution Time" value={`${executionTimeMs}ms`} />
<DetailedRow label="Deployment ID" value={deploymentId} />
<DetailedRow label="Log ID" value={log.id} />
<DetailedRow label="Request Path" value={requestUrl.pathname + requestUrl.search} />
<SelectionDetailedRow label="Status" value={status} valueRender={<ResponseCodeFormatter value={status} />} />
<SelectionDetailedRow label="Method" value={method} />
<SelectionDetailedRow
label="Timestamp"
value={dayjs(timestamp).format('DD MMM, YYYY HH:mm')}
/>
<SelectionDetailedRow label="Execution Time" value={`${executionTimeMs}ms`} />
<SelectionDetailedRow label="Deployment ID" value={deploymentId} />
<SelectionDetailedRow label="Log ID" value={log.id} />
<SelectionDetailedRow
label="Request Path"
value={requestUrl.pathname + requestUrl.search}
/>
</div>
<div className={`${LOGS_TAILWIND_CLASSES.log_selection_x_padding}`}>
<h3 className="text-lg text-scale-1200 mb-4">Request body</h3>
@@ -1,34 +1,11 @@
import dayjs from 'dayjs'
import { LOGS_TAILWIND_CLASSES } from '../Logs.constants'
import { jsonSyntaxHighlight, SeverityFormatter } from '../LogsFormatters'
import { jsonSyntaxHighlight, SelectionDetailedRow, SeverityFormatter } from '../LogsFormatters'
const FunctionLogsSelectionRender = ({ log }: any) => {
const timestamp = dayjs(log.timestamp / 1000)
const metadata = log.metadata[0]
const DetailedRow = ({
label,
value,
code,
}: {
label: string
value: string | React.ReactNode
code?: boolean
}) => {
return (
<div className="grid grid-cols-12">
<span className="text-scale-900 text-sm col-span-4 whitespace-pre-wrap">{label}</span>
<span
className={`text-scale-1200 text-sm col-span-8 whitespace-pre-wrap ${
code && 'text-xs font-mono'
}`}
>
{value}
</span>
</div>
)
}
return (
<>
<div className={`${LOGS_TAILWIND_CLASSES.log_selection_x_padding}`}>
@@ -39,11 +16,15 @@ const FunctionLogsSelectionRender = ({ log }: any) => {
</div>
<div className="h-px w-full bg-panel-border-interior-light dark:bg-panel-border-interior-dark"></div>
<div className={`${LOGS_TAILWIND_CLASSES.log_selection_x_padding} space-y-2`}>
<DetailedRow label="Severity" value={<SeverityFormatter value={metadata.level} />} />
<DetailedRow label="Deployment version" value={metadata.version} />
<DetailedRow label="Timestamp" value={timestamp.format('DD MMM, YYYY HH:mm')} />
<DetailedRow label="Execution ID" value={metadata.execution_id} />
<DetailedRow label="Deployment ID" value={metadata.deployment_id} />
<SelectionDetailedRow
label="Severity"
value={metadata.level}
valueRender={<SeverityFormatter value={metadata.level} />}
/>
<SelectionDetailedRow label="Deployment version" value={metadata.version} />
<SelectionDetailedRow label="Timestamp" value={timestamp.format('DD MMM, YYYY HH:mm')} />
<SelectionDetailedRow label="Execution ID" value={metadata.execution_id} />
<SelectionDetailedRow label="Deployment ID" value={metadata.deployment_id} />
</div>
<div className={`${LOGS_TAILWIND_CLASSES.log_selection_x_padding}`}>
<h3 className="text-lg text-scale-1200 mb-4">Metadata</h3>
@@ -215,7 +215,9 @@ export const SQL_FILTER_TEMPLATES: any = {
'method.get': `request.method = 'GET'`,
'method.post': `request.method = 'POST'`,
'method.del': `request.method = 'DEL'`,
'method.put': `request.method = 'PUT'`,
'method.patch': `request.method = 'PATCH'`,
'method.delete': `request.method = 'DELETE'`,
'method.options': `request.method = 'OPTIONS'`,
},
function_edge_logs: {
@@ -373,11 +375,26 @@ export const FILTER_OPTIONS: FilterTableSet = {
label: 'OPTIONS',
description: '',
},
{
key: 'put',
label: 'PUT',
description: '',
},
{
key: 'post',
label: 'POST',
description: '',
},
{
key: 'patch',
label: 'PATCH',
description: '',
},
{
key: 'delete',
label: 'DELETE',
description: '',
},
],
},
},
@@ -8,11 +8,41 @@ import { IconAlertCircle, IconInfo } from 'ui'
import dayjs from 'dayjs'
import React from 'react'
import { isUnixMicro, unixMicroToIsoTimestamp } from '.'
import CopyButton from 'components/ui/CopyButton'
export const RowLayout: React.FC = ({ children }) => (
<div className="flex h-full w-full items-center gap-4">{children}</div>
)
export const SelectionDetailedRow = ({
label,
value,
valueRender,
}: {
label: string
value: string
valueRender?: React.ReactNode
}) => {
return (
<div className="grid grid-cols-12 group">
<span className="text-scale-900 text-sm col-span-4 whitespace-pre-wrap">{label}</span>
<span className="text-scale-1200 text-sm col-span-6 whitespace-pre-wrap break-all">
{valueRender ?? value}
</span>
<CopyButton
bounceIconOnCopy
text={value}
className="group-hover:opacity-100 opacity-0 my-auto transition col-span-2 h-4 w-4 px-0 py-0"
type="text"
title="Copy to clipboard"
>
{''}
</CopyButton>
</div>
)
}
// used for column renderers
export const TextFormatter: React.FC<{ className?: string; value: string }> = ({
value,
className,
@@ -1,11 +1,12 @@
import Split from 'react-split'
import Editor from '@monaco-editor/react'
import DataGrid from '@supabase/react-data-grid'
import classNames from 'classnames'
import { CSVLink } from 'react-csv'
import { debounce } from 'lodash'
import { observer } from 'mobx-react-lite'
import { useEffect, useRef, useState } from 'react'
import { Button, Dropdown, IconChevronDown } from 'ui'
import { Button, Dropdown, IconCheck, IconChevronDown, IconClipboard } from 'ui'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useKeyboardShortcuts, useStore, useWindowDimensions, checkPermissions } from 'hooks'
@@ -306,21 +307,38 @@ const UtilityTabResults = observer(() => {
const Results = ({ results }) => {
const [cellPosition, setCellPosition] = useState(undefined)
const [copiedCell, setCopiedCell] = useState(undefined)
useKeyboardShortcuts(
{
'Command+c': (event) => {
event.stopPropagation()
onCopyCell()
onCopySelectedCell()
},
'Control+c': (event) => {
event.stopPropagation()
onCopyCell()
onCopySelectedCell()
},
},
['INPUT', 'TEXTAREA']
)
useEffect(() => {
let timeoutId = 0
if (copiedCell) {
timeoutId = setTimeout(() => {
setCopiedCell(undefined)
}, 1000)
}
return () => {
// we need to clear previous timeout to prevent checkmark flickering
// when clicking `Copy` button multiple times in a short time
timeoutId && clearTimeout(timeoutId)
}
}, [copiedCell])
if (results?.error) {
return (
<div className="bg-table-header-light dark:bg-table-header-dark">
@@ -336,8 +354,33 @@ const Results = ({ results }) => {
)
}
const formatter = (column, row) => {
return <span className="font-mono text-xs">{JSON.stringify(row[column])}</span>
const handleCopyClick = (column, row, rowIndex) => {
copyToClipboard(formatClipboardValue(row[column]), () => {
setCopiedCell(`${column},${rowIndex}`)
})
}
const formatter = (column, row, rowIndex) => {
const isCopied = copiedCell === `${column},${rowIndex}`
return (
<div className="group sb-grid-select-cell__formatter overflow-hidden">
<span className="font-mono text-xs truncate">{JSON.stringify(row[column])}</span>
{row[column] && (
<Button
type="outline"
icon={isCopied ? <IconCheck size="tiny" /> : <IconClipboard size="tiny" />}
onClick={() => handleCopyClick(column, row, rowIndex)}
className={classNames(
'mx-1 group-hover:block group-hover:opacity-50 hover:opacity-100',
!isCopied && 'hidden'
)}
title="Copy"
/>
)}
</div>
)
}
const columnRender = (name) => {
return <div className="flex h-full items-center justify-center font-mono">{name}</div>
@@ -345,7 +388,7 @@ const Results = ({ results }) => {
const columns = Object.keys(results[0]).map((key) => ({
key,
name: key,
formatter: ({ row }) => formatter(key, row),
formatter: ({ row }) => formatter(key, row, results.indexOf(row)),
headerRenderer: () => columnRender(key),
resizable: true,
width: 120,
@@ -355,7 +398,7 @@ const Results = ({ results }) => {
setCellPosition(position)
}
function onCopyCell() {
function onCopySelectedCell() {
if (columns && cellPosition) {
const { idx, rowIdx } = cellPosition
const column = columns[idx]
+47
View File
@@ -0,0 +1,47 @@
import { Button, ButtonProps } from 'ui'
import { copyToClipboard } from 'lib/helpers'
import { IconClipboard } from 'ui'
import { useEffect, useState } from 'react'
export interface CopyButtonProps extends ButtonProps {
text: string
// used for text-less feedback
bounceIconOnCopy?: boolean
}
const CopyButton: React.FC<CopyButtonProps> = ({
text,
children,
onClick,
bounceIconOnCopy,
...props
}) => {
const [showCopied, setShowCopied] = useState(false)
useEffect(() => {
if (!showCopied) return
const timer = setTimeout(() => setShowCopied(false), 2000)
return () => clearTimeout(timer)
}, [showCopied])
return (
<Button
onClick={(e) => {
setShowCopied(true)
copyToClipboard(text)
onClick?.(e)
}}
icon={
<IconClipboard
size="tiny"
className={[showCopied && bounceIconOnCopy ? 'animate-bounce' : '', 'transition'].join(
' '
)}
/>
}
{...props}
>
{children ?? (showCopied ? 'Copied' : 'Copy')}
</Button>
)
}
export default CopyButton
+7 -4
View File
@@ -14,7 +14,7 @@ import {
LogsTableName,
PREVIEWER_DATEPICKER_HELPERS,
} from 'components/interfaces/Settings/Logs'
import { Dispatch, SetStateAction, useEffect, useState } from 'react'
import { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react'
import useSWR from 'swr'
import useSWRInfinite, { SWRInfiniteKeyLoader } from 'swr/infinite'
import { API_URL } from 'lib/constants'
@@ -47,18 +47,21 @@ function useLogsPreview(
const [latestRefresh, setLatestRefresh] = useState<string>(new Date().toISOString())
const [filters, setFilters] = useState<Filters>({ ...filterOverride })
const isFirstRender = useRef<boolean>(true)
const [params, setParams] = useState<LogsEndpointParams>({
project: projectRef,
sql: '',
sql: genDefaultQuery(table, filters),
iso_timestamp_start: defaultHelper.calcFrom(),
iso_timestamp_end: defaultHelper.calcTo(),
})
useEffect(() => {
if (filters !== {}) {
refresh()
if (isFirstRender.current) {
isFirstRender.current = false
return
}
refresh()
}, [JSON.stringify(filters)])
// handle url generation for log pagination
@@ -0,0 +1,12 @@
import { screen } from '@testing-library/dom'
import userEvent from '@testing-library/user-event'
import CopyButton from 'components/ui/CopyButton'
import { render } from 'tests/helpers'
test('shows copied text', async () => {
const callback = jest.fn()
render(<CopyButton text="some text" onClick={callback} />)
userEvent.click(await screen.findByText('Copy'))
await screen.findByText('Copied')
expect(callback).toBeCalled()
})
@@ -34,5 +34,5 @@ test('renders chart', async () => {
// )
// userEvent.click(paths[0])
// expect(mock).toBeCalledTimes(1)
await screen.findByText('Events')
await screen.findByText(/Logs \/ Time/)
})
@@ -23,6 +23,10 @@ test('can display log data', async () => {
userEvent.click(row)
await screen.findByText(/my_key/)
await screen.findByText(/something_value/)
// render copy button
userEvent.click(await screen.findByText(/Copy/))
await screen.findByText(/Copied/)
})
test('dedupes log lines with exact id', async () => {
@@ -309,7 +309,7 @@ test('log event chart hide', async () => {
return { result: [] }
})
render(<LogsPreviewer projectRef="123" tableName={LogsTableName.EDGE} />)
await screen.findByText('Events')
await screen.findByText(/Logs \/ Time/)
const toggle = await screen.findByText(/Chart/)
userEvent.click(toggle)
await expect(screen.findByText('Events')).rejects.toThrow()