From 767c928ccba6da7a7d65810080ffe218fcd7ea77 Mon Sep 17 00:00:00 2001 From: TzeYiing Date: Thu, 24 Mar 2022 16:48:07 +0100 Subject: [PATCH] cleaned up code --- .../interfaces/Settings/Logs/LogPanel.tsx | 6 - .../interfaces/Settings/Logs/LogSelection.tsx | 4 +- .../interfaces/Settings/Logs/LogTable.tsx | 329 +++++++----------- .../Settings/Logs/Logs.constants.ts | 18 +- .../interfaces/Settings/Logs/Logs.helpers.ts | 29 +- .../interfaces/Settings/Logs/Logs.types.ts | 3 +- .../Settings/Logs/PreviewFilterPanel.tsx | 44 +-- .../project/[ref]/settings/logs/[type].tsx | 25 +- .../project/[ref]/settings/logs/explorer.tsx | 34 +- studio/tests/pages/projects/LogTable.test.js | 6 +- .../pages/projects/logs-explorer.test.js | 2 +- studio/tsconfig.json | 21 +- 12 files changed, 175 insertions(+), 346 deletions(-) diff --git a/studio/components/interfaces/Settings/Logs/LogPanel.tsx b/studio/components/interfaces/Settings/Logs/LogPanel.tsx index 0a4f2db519f..6a030392dca 100644 --- a/studio/components/interfaces/Settings/Logs/LogPanel.tsx +++ b/studio/components/interfaces/Settings/Logs/LogPanel.tsx @@ -11,7 +11,6 @@ import { IconSearch, IconClock, Popover, - IconPlay, } from '@supabase/ui' import { LogSearchCallback, LogTemplate } from '.' import dayjs from 'dayjs' @@ -30,8 +29,6 @@ interface Props { onSelectTemplate: (template: LogTemplate) => void isShowingEventChart: boolean onToggleEventChart: () => void - - editorControls: React.ReactNode } dayjs.extend(utc) @@ -53,8 +50,6 @@ const LogPanel: FC = ({ onSelectTemplate, isShowingEventChart, onToggleEventChart, - - editorControls, }) => { const [search, setSearch] = useState('') const [to, setTo] = useState({ value: '', error: '' }) @@ -266,7 +261,6 @@ const LogPanel: FC = ({ )} - {editorControls} ) diff --git a/studio/components/interfaces/Settings/Logs/LogSelection.tsx b/studio/components/interfaces/Settings/Logs/LogSelection.tsx index fc6fb93f982..236e9eae67f 100644 --- a/studio/components/interfaces/Settings/Logs/LogSelection.tsx +++ b/studio/components/interfaces/Settings/Logs/LogSelection.tsx @@ -11,7 +11,7 @@ import DatabasePostgresSelectionRender from './LogSelectionRenderers/DatabasePos interface Props { log: LogData onClose: () => void - queryType: QueryType + queryType?: QueryType } /** @@ -38,11 +38,9 @@ const LogSelection: FC = ({ log, onClose, queryType }) => { switch (queryType) { case 'api': return DatabaseApiSelectionHeaderRender(log) - break default: return null - break } } diff --git a/studio/components/interfaces/Settings/Logs/LogTable.tsx b/studio/components/interfaces/Settings/Logs/LogTable.tsx index 59657136efe..82de85cc2d3 100644 --- a/studio/components/interfaces/Settings/Logs/LogTable.tsx +++ b/studio/components/interfaces/Settings/Logs/LogTable.tsx @@ -1,11 +1,10 @@ import dayjs from 'dayjs' import { useEffect, useState, useMemo } from 'react' -import { Badge, Button, IconDownload, IconDownloadCloud, IconEye, Typography } from '@supabase/ui' -import DataGrid, { Row } from '@supabase/react-data-grid' +import { Button, IconDownloadCloud, IconEye, Typography } from '@supabase/ui' +import DataGrid from '@supabase/react-data-grid' import LogSelection from './LogSelection' -import { LogData, Mode, QueryType } from './Logs.types' -import { isNil } from 'lodash' +import { LogData, QueryType } from './Logs.types' import { SeverityFormatter, ResponseCodeFormatter, HeaderFormmater } from './LogsFormatters' // column renders @@ -15,7 +14,7 @@ import DatabasePostgresColumnRender from './LogColumnRenderers/DatabasePostgresC interface Props { isCustomQuery: boolean data?: Array - queryType: QueryType + queryType?: QueryType } type LogMap = { [id: string]: LogData } @@ -24,193 +23,130 @@ type LogMap = { [id: string]: LogData } * * When in custom data display mode, the side panel will not open when focusing on logs. */ -const LogTable = ({ isCustomQuery, data = [], queryType }: Props) => { +const LogTable = ({ data = [], queryType }: Props) => { const [focusedLog, setFocusedLog] = useState(null) const columnNames = Object.keys(data[0] || {}) + const hasId = columnNames.includes('id') + const hasTimestamp = columnNames.includes('timestamp') - // console.log('queryType', queryType) - - // console.log('focusedLog', focusedLog) - - isCustomQuery = false - - // whether the data structure is LogData format. - // const hasLogDataFormat = - // columnNames.includes('timestamp') && - // columnNames.includes('event_message') && - // columnNames.length === 4 - - const hasLogDataFormat = !isCustomQuery - - const DEFAULT_COLUMNS = (hasLogDataFormat ? ['timestamp', 'event_message'] : columnNames).map( - (v) => ({ - key: v, - name: v, - // width: hasLogDataFormat && v === 'timestamp' ? 210 : undefined, - resizable: true, - // headerRenderer: () => { - // return
{v}
- // }, - // formatter: ({ row }: any) => { - // let value = row[v] - // if (hasLogDataFormat && v === 'timestamp') { - // value = dayjs(Number(row['timestamp']) / 1000).toISOString() - // } - // return ( - //

- // {value} - //

- // ) - // }, - }) - ) - + const DEFAULT_COLUMNS = columnNames.map((v) => ({ key: v, name: v, resizable: true })) let columns - // console.log('QUERY TYPE', queryType) - - switch (queryType) { - case 'api': - if (isCustomQuery) { - columns = DEFAULT_COLUMNS + if (!queryType) { + columns = DEFAULT_COLUMNS + } else { + switch (queryType) { + case 'api': + columns = DatabaseApiColumnRender break - } - // console.log('is api, running column') - columns = DatabaseApiColumnRender - - break - - case 'database': - if (isCustomQuery) { - columns = DEFAULT_COLUMNS + case 'database': + columns = DatabasePostgresColumnRender break - } - // console.log('is api, running column') - columns = DatabasePostgresColumnRender - - break - - case 'fn_edge': - if (isCustomQuery) { - columns = DEFAULT_COLUMNS - break - } - - columns = [ - { - key: 'timestamp', - headerRenderer: () => ( -
- -
- ), - name: 'timestamp', - formatter: (data: any) => ( - - {dayjs(data?.row?.timestamp / 1000).format('DD MMM')} - - {dayjs(data?.row?.timestamp / 1000).format('HH:mm:ss')} + case 'fn_edge': + columns = [ + { + key: 'timestamp', + headerRenderer: () => ( +
+ +
+ ), + name: 'timestamp', + formatter: (data: any) => ( + + + {dayjs(data?.row?.timestamp / 1000).format('DD MMM')} + + + {dayjs(data?.row?.timestamp / 1000).format('HH:mm:ss')} + - {/* {data?.row?.timestamp} */} -
- ), - width: 128, - }, - { - key: 'status_code', - headerRenderer: () => , - name: 'status_code', - formatter: (data: any) => ( - - ), + ), + width: 128, + }, + { + key: 'status_code', + headerRenderer: () => , + name: 'status_code', + formatter: (data: any) => ( + + ), + width: 0, + resizable: true, + }, + { + key: 'method', + headerRenderer: () => , + width: 0, + resizable: true, + }, + { + key: 'id', + headerRenderer: () => , + name: 'id', + resizable: true, + }, + ] + break + case 'functions': + columns = [ + { + key: 'timestamp', + headerRenderer: () => ( +
+ +
+ ), + name: 'timestamp', + formatter: (data: any) => ( + + + {dayjs(data?.row?.timestamp / 1000).format('DD MMM')} + + + {dayjs(data?.row?.timestamp / 1000).format('HH:mm:ss')} + + + ), + width: 128, + resizable: true, + }, + { + key: 'level', + headerRenderer: () => , + name: 'level', + formatter: (data: any) => , + width: 24, + resizable: true, + }, + { + key: 'event_message', + headerRenderer: () => , + resizable: true, + }, + ] + break - width: 0, - resizable: true, - }, - { - key: 'method', - headerRenderer: () => , - width: 0, - resizable: true, - }, - { - key: 'id', - headerRenderer: () => , - name: 'id', - resizable: true, - }, - ] - break - case 'functions': - if (isCustomQuery) { + default: columns = DEFAULT_COLUMNS break - } - columns = [ - { - key: 'timestamp', - headerRenderer: () => ( -
- -
- ), - name: 'timestamp', - formatter: (data: any) => ( - - - {dayjs(data?.row?.timestamp / 1000).format('DD MMM')} - - - {dayjs(data?.row?.timestamp / 1000).format('HH:mm:ss')} - - {/* {data?.row?.timestamp} */} - - ), - width: 128, - resizable: true, - }, - { - key: 'level', - headerRenderer: () => , - name: 'level', - formatter: (data: any) => , - width: 24, - resizable: true, - }, - { - key: 'event_message', - headerRenderer: () => , - resizable: true, - }, - ] - break - - default: - columns = DEFAULT_COLUMNS - break + } } + const stringData = JSON.stringify(data) const logMap = useMemo(() => { - if (!hasLogDataFormat) return {} as LogMap + if (!hasId) return {} as LogMap const logData = data as LogData[] return logData.reduce((acc: LogMap, d: LogData) => { acc[d.id] = d return acc }, {}) as LogMap - }, [JSON.stringify(data)]) + }, [stringData]) - const stringData = JSON.stringify(data) useEffect(() => { - if (!hasLogDataFormat) return - if (isNil(data)) return + if (!hasId || data === null) return if (focusedLog && !(focusedLog.id in logMap)) { setFocusedLog(null) } @@ -218,20 +154,21 @@ const LogTable = ({ isCustomQuery, data = [], queryType }: Props) => { if (!data) return null - // console.log(data) - // console.log('logMap', logMap) - // console.log(columns) - // [Joshen] Hmm quite hacky now, but will do - const maxHeight = isCustomQuery ? 'calc(100vh - 42px - 10rem)' : 'calc(100vh - 42px - 3rem)' + const maxHeight = !queryType ? 'calc(100vh - 42px - 10rem)' : 'calc(100vh - 42px - 3rem)' const logDataRows = useMemo(() => { - if (!hasLogDataFormat) return data - return Object.values(logMap).sort((a, b) => b.timestamp - a.timestamp) + if (hasId && hasTimestamp) { + return Object.values(logMap).sort((a, b) => b.timestamp - a.timestamp) + } else { + const deduped = [...new Set(data)] + return deduped + } }, [stringData]) + return ( <> - {isCustomQuery && ( + {!queryType && (
- setFocusedLog(null)} - log={focusedLog} - queryType={queryType} - /> -
- {/* ))} */} + {hasId && focusedLog && ( +
+ setFocusedLog(null)} + log={focusedLog} + queryType={queryType} + /> +
+ )} ) diff --git a/studio/components/interfaces/Settings/Logs/Logs.constants.ts b/studio/components/interfaces/Settings/Logs/Logs.constants.ts index 6945b065499..c51fbcc0f20 100644 --- a/studio/components/interfaces/Settings/Logs/Logs.constants.ts +++ b/studio/components/interfaces/Settings/Logs/Logs.constants.ts @@ -209,7 +209,23 @@ export const genQueryParams = (params: { [k: string]: string }) => { return qs } -export const FILTER_OPTIONS: any = { + +interface Filter { + label: string + key: string + options: { + key: string + label: string + description?: string + }[] +} +type FilterOptions = { + [table: string] : { + [filterName: string]: Filter + } +} + +export const FILTER_OPTIONS: FilterOptions = { // Postgres logs postgres_logs: { severity: { diff --git a/studio/components/interfaces/Settings/Logs/Logs.helpers.ts b/studio/components/interfaces/Settings/Logs/Logs.helpers.ts index ed9dfce7ab9..e76e774bcdc 100644 --- a/studio/components/interfaces/Settings/Logs/Logs.helpers.ts +++ b/studio/components/interfaces/Settings/Logs/Logs.helpers.ts @@ -1,19 +1,12 @@ -import { Severity } from '@sentry/browser' -import { filter } from 'lodash' import { LogsTableName, SQL_FILTER_TEMPLATES } from './Logs.constants' import { FilterObject } from './Logs.types' export function filterSqlWhereBuilder( - filters: FilterObject, + filters: FilterObject | any, table: LogsTableName, - searchQuery: string ) { - // console.log('raw filters', filters) - // remove any filter arrays that are empty - const filtersSanitized: any = Object.values(filters).filter((x) => x && x.length > 0) - - // console.log('filtersSanitized', filtersSanitized) + const filtersSanitized: any = Object.values(filters).filter((x: any) => x && x.length > 0) const keys = Object.keys(filters) @@ -25,17 +18,13 @@ export function filterSqlWhereBuilder( keys.map((x: string, i) => { // do not parse empty key - if (!x) { - return - } + if (!x) return let count = 0 - const filterKeyArray: string[] = [] filters[x].map((value: string, i: number) => { // first line should be WHERE - const last = i === filters[x].length - 1 if (count === 0) { @@ -55,11 +44,8 @@ export function filterSqlWhereBuilder( } }) - // console.log('filterKeyArray', filterKeyArray) whereArray.push(...filterKeyArray) - // console.log('filters length', filtersSanitized.length) - // if there are multiple filters in an 'and' sequence // then `and` is inserted between them if (filtersSanitized.length > 0 && i < filtersSanitized.length - 1) { @@ -68,21 +54,12 @@ export function filterSqlWhereBuilder( return filterKeyArray }) - - // console.log('whereArray', whereArray) - - // console.log('sqlWhereArray', sqlWhereArray) return whereArray } export function filterReducer(state: FilterObject, action: any) { - // console.log(state, action) - const oldState = { ...state } - const newState = { ...oldState, ...action } - console.log('newState', newState) - return newState } diff --git a/studio/components/interfaces/Settings/Logs/Logs.types.ts b/studio/components/interfaces/Settings/Logs/Logs.types.ts index e3bb0010fb2..9d2d9d9fc6d 100644 --- a/studio/components/interfaces/Settings/Logs/Logs.types.ts +++ b/studio/components/interfaces/Settings/Logs/Logs.types.ts @@ -21,7 +21,8 @@ export interface LogData { id: string timestamp: number event_message: string - metadata: Metadata + metadata: Metadata, + [other: string]: unknown; } export interface LogTemplate { diff --git a/studio/components/interfaces/Settings/Logs/PreviewFilterPanel.tsx b/studio/components/interfaces/Settings/Logs/PreviewFilterPanel.tsx index f73996d3a09..431030dae26 100644 --- a/studio/components/interfaces/Settings/Logs/PreviewFilterPanel.tsx +++ b/studio/components/interfaces/Settings/Logs/PreviewFilterPanel.tsx @@ -2,33 +2,25 @@ import React, { FC, useEffect, useState } from 'react' import { Button, Input, - Dropdown, Typography, - IconChevronDown, IconRefreshCw, IconX, - Toggle, IconSearch, IconClock, Popover, - IconLink, IconExternalLink, - IconCalendar, - IconEye, - Checkbox, - Form, } from '@supabase/ui' import { LogSearchCallback, LogTemplate } from '.' import dayjs from 'dayjs' import utc from 'dayjs/plugin/utc' import { FILTER_OPTIONS, LogsTableName } from './Logs.constants' import { LogsFilter } from './Logs.filter' -import { DatePicker } from 'components/ui/DatePicker' import DatePickers from './Logs.DatePickers' interface Props { defaultSearchValue?: string defaultToValue?: string + defaultFromValue?: string templates?: any isLoading: boolean isCustomQuery: boolean @@ -42,10 +34,6 @@ interface Props { dispatchWhereFilters: (x: any) => void whereFilters: any table: LogsTableName - te: { value: string; error: string } - ts: { value: string; error: string } - setTo: ({}) => void - setFrom: ({}) => void } dayjs.extend(utc) @@ -69,10 +57,6 @@ const PreviewFilterPanel: FC = ({ dispatchWhereFilters, whereFilters: filters, table, - // te: to, - // ts: from, - // setTo, - // setFrom, }) => { const [search, setSearch] = useState('') @@ -189,8 +173,7 @@ const PreviewFilterPanel: FC = ({ onSubmit={(e) => { // prevent redirection e.preventDefault() - setSearch(localSearchValue) - handleSearch(localSearchValue) + handleSearch() }} > = ({ onChange={(e) => setlocalSearchValue(e.target.value)} onBlur={(e: React.FocusEvent) => { setSearch(e.target.value) - handleSearch(localSearchValue) + handleSearch() }} icon={
@@ -211,7 +194,7 @@ const PreviewFilterPanel: FC = ({ actions={ hasEdits && (
- {Object.values(FILTER_OPTIONS[table]).map((x) => { - // console.log('filter option', x) - return ( - - ) - })} + {Object.values(FILTER_OPTIONS[table]).map((x) => ( + + ))}
diff --git a/studio/pages/project/[ref]/settings/logs/[type].tsx b/studio/pages/project/[ref]/settings/logs/[type].tsx index b458ce9d805..26820acdf0d 100644 --- a/studio/pages/project/[ref]/settings/logs/[type].tsx +++ b/studio/pages/project/[ref]/settings/logs/[type].tsx @@ -1,4 +1,3 @@ -import useSWR from 'swr' import React, { useEffect, useReducer, useState } from 'react' import { NextPage } from 'next' import { useRouter } from 'next/router' @@ -9,38 +8,24 @@ import { IconAlertCircle, IconRewind, Button, - IconInfo, Card, Input, } from '@supabase/ui' import { withAuth } from 'hooks' -import { get } from 'lib/common/fetch' -import { API_URL } from 'lib/constants' import { SettingsLayout } from 'components/layouts/' -import CodeEditor from 'components/ui/CodeEditor' import { LogTable, - LogEventChart, - Count, - Logs, LogTemplate, TEMPLATES, - LogData, LogSearchCallback, LOG_TYPE_LABEL_MAPPING, - genDefaultQuery, - genCountQuery, LogsTableName, filterSqlWhereBuilder, - FilterObject, filterReducer, + QueryType, } from 'components/interfaces/Settings/Logs' -import { uuidv4 } from 'lib/helpers' -import useSWRInfinite, { SWRInfiniteKeyLoader } from 'swr/infinite' -import { isUndefined } from 'lodash' import dayjs from 'dayjs' -import InformationBox from 'components/ui/InformationBox' import useLogsPreview from 'hooks/analytics/useLogsPreview' import PreviewFilterPanel from 'components/interfaces/Settings/Logs/PreviewFilterPanel' @@ -125,7 +110,7 @@ export const LogPage: NextPage = () => { } const handleSearch: LogSearchCallback = ({ query, to, from, fromMicro, toMicro }) => { - let toValue, fromValue + let toValue if (to || toMicro) { toValue = toMicro ? toMicro : dayjs(to).valueOf() * 1000 @@ -196,10 +181,6 @@ export const LogPage: NextPage = () => { dispatchWhereFilters={dispatchWhereFilters} whereFilters={whereFilters} table={table} - te={te} - ts={ts} - setTo={setTo} - setFrom={setFrom} /> {/* {showChart && (
@@ -223,7 +204,7 @@ export const LogPage: NextPage = () => {
)} - +