diff --git a/apps/docs/content/guides/platform/access-control.mdx b/apps/docs/content/guides/platform/access-control.mdx index 7ca44c31e80..2483a8f1ddb 100644 --- a/apps/docs/content/guides/platform/access-control.mdx +++ b/apps/docs/content/guides/platform/access-control.mdx @@ -260,14 +260,6 @@ The table below shows the actions each role can take on the resources belonging | | View | | | | | | | List | | | | | | | Run | | | | | -| Events Collections | Create | | | | | -| | Update | | | | | -| | Delete | | | | | -| | View | | | | | -| | List | | | | | -| Warehouse Access Tokens | Create | | | | | -| | Revoke | | | | | -| | List | | | | | | **Branching** | | | | | | | Production Branch | Read | | | | | | | Write | | | | | diff --git a/apps/studio/components/interfaces/DataWarehouse/CollectionForm.tsx b/apps/studio/components/interfaces/DataWarehouse/CollectionForm.tsx deleted file mode 100644 index 5bb7e906d59..00000000000 --- a/apps/studio/components/interfaces/DataWarehouse/CollectionForm.tsx +++ /dev/null @@ -1,154 +0,0 @@ -import { zodResolver } from '@hookform/resolvers/zod' -import { useForm } from 'react-hook-form' -import { z } from 'zod' - -import { - Button, - FormControl_Shadcn_, - FormField_Shadcn_, - FormMessage_Shadcn_, - Form_Shadcn_, - Input_Shadcn_, - Modal, -} from 'ui' -import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' -import { useCurrentOrgPlan } from 'hooks/misc/useCurrentOrgPlan' -import { Admonition } from 'ui-patterns' -import { useRouter } from 'next/router' -import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' - -export interface CollectionFormValues { - name: string - retention_days: number -} - -interface CollectionFormProps { - initialValues?: Partial - onSubmit: (values: CollectionFormValues) => void - isLoading?: boolean - onCancelClick: () => void -} - -export const CollectionForm = ({ - initialValues, - onSubmit, - isLoading, - onCancelClick, -}: CollectionFormProps) => { - const { plan, isLoading: planLoading } = useCurrentOrgPlan() - const router = useRouter() - const currentOrg = useSelectedOrganization() - - function getMaxRetentionDays() { - if (planLoading) return 1 - if (plan?.id === 'free') return 3 - else return 90 - } - const maxRetentionDays = getMaxRetentionDays() - - const FormSchema = z.object({ - name: z.string().min(1), - retention_days: z.coerce.number().min(1).max(maxRetentionDays).multipleOf(1).positive().int(), - }) - - const form = useForm({ - resolver: zodResolver(FormSchema), - defaultValues: { - name: initialValues?.name || '', - retention_days: initialValues?.retention_days || maxRetentionDays, - }, - }) - - const handleSubmit = form.handleSubmit(onSubmit) - - const formatDays = (days: number) => { - return days === 1 ? `1 day` : `${days} days` - } - - const retentionDescription = (days: number) => { - if (!Boolean(days) || days < 1) - return <>Your plan allows for a maximum of {formatDays(maxRetentionDays)}. - return ( - <> - Your logs will be removed after {formatDays(days)}.
- Your plan allows for a maximum of {formatDays(maxRetentionDays)}. - - ) - } - - return ( - -
- - ( - - - - - - )} - /> - ( - - -
- - - days - -
-
-
- )} - /> -
- - {plan?.id === 'free' && ( - <> - - - Upgrade to the pro plan to increase retention to 90 days. - - - } - /> - - )} - - - - - - -
- ) -} diff --git a/apps/studio/components/interfaces/DataWarehouse/CreateWarehouseCollection.tsx b/apps/studio/components/interfaces/DataWarehouse/CreateWarehouseCollection.tsx deleted file mode 100644 index 4b2126065e4..00000000000 --- a/apps/studio/components/interfaces/DataWarehouse/CreateWarehouseCollection.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { useRouter } from 'next/router' -import { toast } from 'sonner' - -import { useParams } from 'common' -import { useCreateCollection } from 'data/analytics/warehouse-collections-create-mutation' -import { Modal } from 'ui' -import { CollectionForm } from './CollectionForm' - -export const CreateWarehouseCollectionModal = ({ - open, - onOpenChange, -}: { - open: boolean - onOpenChange: (open: boolean) => void -}) => { - const router = useRouter() - const { ref } = useParams() - - const { mutate: createCollection, isLoading } = useCreateCollection({ - onSuccess: (data) => { - onOpenChange(false) - toast.success('Collection created successfully') - router.push(`/project/${ref}/logs/collections/${data.token}`) - }, - onError: (error) => { - toast.error(error.message) - }, - }) - - return ( - onOpenChange(false)} - header="Create collection" - visible={open} - hideFooter - > - onOpenChange(false)} - isLoading={isLoading} - onSubmit={(vals) => { - if (!ref) { - return - } - createCollection({ - projectRef: ref, - name: vals.name, - retention_days: vals.retention_days, - }) - }} - /> - - ) -} diff --git a/apps/studio/components/interfaces/DataWarehouse/TestCollectionDialog.tsx b/apps/studio/components/interfaces/DataWarehouse/TestCollectionDialog.tsx deleted file mode 100644 index 489a9e29860..00000000000 --- a/apps/studio/components/interfaces/DataWarehouse/TestCollectionDialog.tsx +++ /dev/null @@ -1,186 +0,0 @@ -import { Select, SelectContent, SelectItem, SelectTrigger } from '@ui/components/shadcn/ui/select' -import CopyButton from 'components/ui/CopyButton' -import type { WarehouseAccessTokensData } from 'data/analytics/warehouse-access-tokens-query' -import type { WarehouseCollectionsData } from 'data/analytics/warehouse-collections-query' -import { useRouter } from 'next/router' -import { useEffect, useState } from 'react' -import { - Button, - Checkbox_Shadcn_, - CodeBlock, - Input_Shadcn_, - Label_Shadcn_, - Separator, - Sheet, - SheetContent, - SheetDescription, - SheetFooter, - SheetHeader, - SheetSection, - SheetTitle, - SheetTrigger, - Tooltip, - TooltipContent, - TooltipTrigger, -} from 'ui' -import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' - -export function TestCollectionDialog({ - accessTokens, - collectionToken, - collections, - open, - onOpenChange, - onSubmit, -}: { - accessTokens: WarehouseAccessTokensData['data'] - collections: WarehouseCollectionsData - collectionToken: string - projectRef: string - open: boolean - onOpenChange: (open: boolean) => void - onSubmit: () => void -}) { - const router = useRouter() - const BASE_WAREHOUSE_URL = `https://api.warehouse.tech/api/events` - const [testAccessToken, setTestAccessToken] = useState('') - const [selectedCollection, setSelectedCollection] = useState(collectionToken || '') - const [showAccessToken, setShowAccessToken] = useState(false) - - useEffect(() => { - setSelectedCollection(collectionToken) - }, [collectionToken]) - - useEffect(() => { - if (accessTokens.length > 0) { - setTestAccessToken(accessTokens[0].token) - } - }, [accessTokens]) - - const selectedAccessToken = accessTokens.find((token) => token.token === testAccessToken) - const selectedCollectionName = collections?.find((col) => col.token === selectedCollection)?.name - - function getcURL(accessToken: string) { - return `curl -X "POST" "${BASE_WAREHOUSE_URL}?source=${selectedCollection}" \\ - -H 'Content-Type: application/json' \\ - -H 'Authorization: Bearer ${accessToken}' \\ - -d $'{ - "event_message": "Test event message", - "metadata": { - "ip_address": "100.100.100.100", - "request_method": "POST", - "custom_user_data": { - "foo": "bar" - }, - "datacenter": "aws", - "request_headers": { - "connection": "close", - "user_agent": "chrome" - } - } - }' -` - } - - return ( - - - - - - - - {accessTokens.length === 0 && ( - Create an access token to connect to your collection - )} - - - - - Send events to this collection - - Use the following cURL command to send events to this collection - - - - - - - - - - - - - - - - - - - - - - -
-
- setShowAccessToken(!showAccessToken)} - /> - Show access token -
- -
- -
- - {getcURL(showAccessToken ? testAccessToken : '********************')} - -
-
- - -

- Once you send an event, refresh the page to see it in the collection. -

-
- -
-
-
-
- ) -} diff --git a/apps/studio/components/interfaces/DataWarehouse/WarehouseCollectionDetail.tsx b/apps/studio/components/interfaces/DataWarehouse/WarehouseCollectionDetail.tsx deleted file mode 100644 index f0be4402a71..00000000000 --- a/apps/studio/components/interfaces/DataWarehouse/WarehouseCollectionDetail.tsx +++ /dev/null @@ -1,323 +0,0 @@ -import { useKeyboardShortcuts } from 'components/grid/components/common/Hooks' -import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState' -import AlertError from 'components/ui/AlertError' -import LoadingOpacity from 'components/ui/LoadingOpacity' -import ShimmerLine from 'components/ui/ShimmerLine' -import { useWarehouseAccessTokensQuery } from 'data/analytics/warehouse-access-tokens-query' -import { useWarehouseLogDetailQuery } from 'data/analytics/warehouse-collection-log-detail-query' -import { useWarehouseCollectionsQuery } from 'data/analytics/warehouse-collections-query' -import { useWarehouseQueryQuery } from 'data/analytics/warehouse-query' -import dayjs from 'dayjs' -import { useSelectedLog } from 'hooks/analytics/useSelectedLog' -import { useFlag } from 'hooks/ui/useFlag' -import { RefreshCcw, Rewind, Terminal } from 'lucide-react' -import Link from 'next/link' -import { useRouter } from 'next/router' -import { useEffect, useState } from 'react' -import { Button, Tooltip, TooltipContent, TooltipTrigger } from 'ui' -import { LogsDatePicker } from '../Settings/Logs/Logs.DatePickers' -import { DatetimeHelper } from '../Settings/Logs/Logs.types' -import LogTable from '../Settings/Logs/LogTable' -import { TestCollectionDialog } from './TestCollectionDialog' - -const INTERVALS: DatetimeHelper[] = [ - { - text: 'Last hour', - calcFrom: () => dayjs().subtract(1, 'hour').toISOString(), - calcTo: () => dayjs().toISOString(), - default: true, - }, - { - text: 'Last 12 hours', - calcFrom: () => dayjs().subtract(12, 'hour').toISOString(), - calcTo: () => dayjs().toISOString(), - }, - { - text: 'Last day', - calcFrom: () => dayjs().subtract(1, 'day').toISOString(), - calcTo: () => dayjs().toISOString(), - }, - { - text: 'Last 7 days', - calcFrom: () => dayjs().subtract(7, 'day').toISOString(), - calcTo: () => dayjs().toISOString(), - }, -] - -export const WarehouseCollectionDetail = () => { - const router = useRouter() - const collectionToken = router.query.collectionToken as string - const projectRef = router.query.ref as string - const accessTokens = useWarehouseAccessTokensQuery({ projectRef }) - const [testDialogOpen, setTestDialogOpen] = useState(false) - const hasWarehouse = useFlag('warehouse') - - const { data: collections, isLoading: collectionsLoading } = useWarehouseCollectionsQuery( - { projectRef }, - { enabled: hasWarehouse } - ) - const collection = collections?.find((c) => c.token === collectionToken) - const [search, setSearch] = useState('') - const [filters, setFilters] = useState({ - limit: 100, - offset: 0, - search: '', - interval: { - to: INTERVALS[0].calcTo, - from: INTERVALS[0].calcFrom, - }, - }) - const [sql, setSQL] = useState('') - - useEffect(() => { - if (collection) { - const from = filters.interval.from() - const to = filters.interval.to() - - const sql = `select id, timestamp from \`${collection.name}\` -where timestamp >= TIMESTAMP('${from}') -and timestamp <= TIMESTAMP('${to}') -order by timestamp desc limit ${filters.limit} offset ${filters.offset} - ` - - setSQL(sql) - } - }, [filters.interval, filters.limit, filters.offset, collection]) - - const { - isLoading: queryLoading, - data: queryData, - isError, - isRefetching, - } = useWarehouseQueryQuery( - { ref: projectRef, sql }, - { - enabled: !!sql && hasWarehouse, - } - ) - - const formatResults = (results: any) => { - if (!results || !results.length) { - return [] - } - - const r = results.map(({ timestamp, ...r }: any) => { - return { - timestamp: new Date(timestamp / 1000).toLocaleString(), - ...r, - } - }) - - return r - } - - const results = formatResults(queryData?.result) - - function loadMore() { - setFilters({ ...filters, offset: filters.offset + filters.limit }) - } - - const isLoading = queryLoading || collectionsLoading || isRefetching - - function refreshResults(e?: React.FormEvent) { - e?.preventDefault() - setFilters({ - ...filters, - search, - interval: { - to: () => dayjs().toISOString(), - from: () => filters.interval.from() || dayjs().subtract(1, 'hour').toISOString(), - }, - }) - } - - const queryUrl = `/project/${projectRef}/logs/explorer?q=${encodeURIComponent( - sql || '' - )}&source=warehouse` - - useKeyboardShortcuts( - { - r: () => { - document.getElementById('refresh-button')?.focus() - }, - s: () => { - document.getElementById('search-input')?.focus() - }, - }, - ['INPUT', 'BUTTON', 'TEXTAREA'] - ) - - function KeyboardShortcut({ children }: { children: React.ReactNode }) { - return ( - - {children} - - ) - } - - // Selected Log Detail - const [selectedLogId, setSelectedLogId] = useSelectedLog() - const { - data: selectedLogDetail, - isLoading: selectedLogDetailLoading, - error: selectedLogDetailError, - } = useWarehouseLogDetailQuery({ - ref: projectRef, - collectionName: collection?.name, - logId: selectedLogId ?? undefined, - }) - - return ( -
- - -
-
-
-

- {collection?.name} -

- - - - - - Open query in Collections Explorer - -
- - { - refreshResults() - setTestDialogOpen(false) - }} - /> -
-
- - {isError && ( -
- -
- )} - - {!isError && ( - setSelectedLogId(log?.id ?? null)} - selectedLog={selectedLogDetail?.result[0] ?? undefined} - selectedLogError={selectedLogDetailError ?? undefined} - isSelectedLogLoading={!!selectedLogId && selectedLogDetailLoading} - data={results} - showHeader={false} - EmptyState={ - -
-

Try adjusting your filters, send a test event or refresh the results.

-
- - -
-
-
- } - /> - )} -
- - - {!isError && ( -
-
- {results.length > 0 && ( - - )} - {filters.offset !== 0 && ( - <> - - - )} -
-
- )} - - ) -} diff --git a/apps/studio/components/interfaces/DataWarehouse/WarehouseMenuItem.tsx b/apps/studio/components/interfaces/DataWarehouse/WarehouseMenuItem.tsx deleted file mode 100644 index 9b2c656e254..00000000000 --- a/apps/studio/components/interfaces/DataWarehouse/WarehouseMenuItem.tsx +++ /dev/null @@ -1,154 +0,0 @@ -import { PermissionAction } from '@supabase/shared-types/out/constants' -import { Database, EditIcon, TrashIcon } from 'lucide-react' -import { useRouter } from 'next/router' -import { useState } from 'react' -import { toast } from 'sonner' - -import { useParams } from 'common' -import { useDeleteCollectionMutation } from 'data/analytics/warehouse-collections-delete-mutation' -import { useUpdateCollection } from 'data/analytics/warehouse-collections-update-mutation' -import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' -import { - DropdownMenuItem, - DropdownMenuSeparator, - Modal, - Tooltip, - TooltipContent, - TooltipTrigger, -} from 'ui' - -import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' -import { LogsSidebarItem } from '../Settings/Logs/SidebarV2/SidebarItem' -import { CollectionForm } from './CollectionForm' - -type Props = { - item: { - id: number - token: string - name: string - retention_days: number - } -} - -export const WarehouseMenuItem = ({ item }: Props) => { - const router = useRouter() - const { ref, collectionToken } = useParams() - const projectRef = ref || 'default' - - const [showUpdateDialog, setShowUpdateDialog] = useState(false) - const [showDeleteDialog, setDeleteDialog] = useState(false) - - const canUpdateCollection = useCheckPermissions(PermissionAction.ANALYTICS_WRITE, 'logflare') - - const updateCollection = useUpdateCollection({ - onSuccess: () => { - toast.success('Collection updated successfully') - setShowUpdateDialog(false) - }, - }) - const deleteCollection = useDeleteCollectionMutation({ - onSuccess: () => { - // If the current collection is deleted, redirect to default logs view - if (item.token === collectionToken) { - router.push(`/project/${projectRef}/logs/explorer`) - } - setDeleteDialog(false) - toast.success('Collection deleted successfully') - }, - }) - - const isLoading = updateCollection.isLoading || deleteCollection.isLoading - - return ( - <> - } - href={`/project/${projectRef}/logs/collections/${item.token}`} - dropdownItems={ - <> - - - { - e.preventDefault() - setShowUpdateDialog(true) - }} - > - -
Update collection
-
-
- {!canUpdateCollection && ( - - You need additional permissions to update collections - - )} -
- - - - { - e.preventDefault() - setDeleteDialog(true) - }} - > - -
Delete collection
-
-
- {!canUpdateCollection && ( - - You need additional permissions to delete collections - - )} -
- - } - >
- - setShowUpdateDialog(false)} - hideFooter - header={`Update collection "${item.name}"`} - > - setShowUpdateDialog(false)} - isLoading={isLoading} - initialValues={{ name: item.name, retention_days: item.retention_days }} - onSubmit={({ name, retention_days }) => { - updateCollection.mutate({ - projectRef, - collectionToken: item.token, - name, - retention_days, - }) - }} - /> - - setDeleteDialog(false)} - confirmLabel="Delete collection" - title={`Delete collection "${item.name}"`} - size="small" - onConfirm={() => { - deleteCollection.mutate({ projectRef, collectionToken: item.token }) - }} - > -

- All data in this collection will be deleted. -
-

This action cannot be undone.
-

-
- - ) -} diff --git a/apps/studio/components/interfaces/Settings/Logs/LogSelection.tsx b/apps/studio/components/interfaces/Settings/Logs/LogSelection.tsx index ffa1e699e22..0f42bf05878 100644 --- a/apps/studio/components/interfaces/Settings/Logs/LogSelection.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/LogSelection.tsx @@ -22,7 +22,6 @@ export interface LogSelectionProps { onClose: () => void queryType?: QueryType projectRef: string - collectionName?: string isLoading: boolean error?: string | object } @@ -41,8 +40,6 @@ const LogSelection = ({ log, onClose, queryType, isLoading, error }: LogSelectio if (!log) return switch (queryType) { - // case 'warehouse': - // return case 'api': const status = log?.metadata?.[0]?.response?.[0]?.status_code const method = log?.metadata?.[0]?.request?.[0]?.method diff --git a/apps/studio/components/interfaces/Settings/Logs/LogSelectionRenderers/WarehouseSelectionRenderer.tsx b/apps/studio/components/interfaces/Settings/Logs/LogSelectionRenderers/WarehouseSelectionRenderer.tsx deleted file mode 100644 index 43047d1882e..00000000000 --- a/apps/studio/components/interfaces/Settings/Logs/LogSelectionRenderers/WarehouseSelectionRenderer.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { cn } from 'ui' -import { LOGS_TAILWIND_CLASSES } from '../Logs.constants' -import { jsonSyntaxHighlight, SelectionDetailedRow } from '../LogsFormatters' - -const DetailedJsonRow = ({ - label, - value, - code, -}: { - label: string - value: Object - code?: boolean -}) => { - return ( -
- {label} - -
-      
-    
- ) -} - -export const WarehouseSelectionRenderer = ({ log }: any) => { - return ( -
- {Object.entries(log).map(([key, value], index) => { - return ( -
- {value && typeof value === 'object' ? ( - - ) : ( - - )} -
- ) - })} -
- ) -} diff --git a/apps/studio/components/interfaces/Settings/Logs/LogTable.tsx b/apps/studio/components/interfaces/Settings/Logs/LogTable.tsx index 7e043920a4b..bb5e4015af7 100644 --- a/apps/studio/components/interfaces/Settings/Logs/LogTable.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/LogTable.tsx @@ -49,8 +49,6 @@ interface Props { onSave?: () => void hasEditorValue?: boolean className?: string - collectionName?: string // Used for warehouse queries - warehouseError?: string EmptyState?: ReactNode showHeader?: boolean showHistogramToggle?: boolean @@ -79,7 +77,6 @@ const LogTable = ({ onSave, hasEditorValue, className, - collectionName, EmptyState, showHeader = true, showHistogramToggle = true, @@ -154,9 +151,6 @@ const LogTable = ({ columns } else { switch (queryType) { - case 'warehouse': - columns = DEFAULT_COLUMNS - break case 'api': columns = DatabaseApiColumnRender break @@ -460,7 +454,6 @@ const LogTable = ({ log={selectedLog} error={selectedLogError} queryType={queryType} - collectionName={collectionName} /> )} diff --git a/apps/studio/components/interfaces/Settings/Logs/Logs.constants.ts b/apps/studio/components/interfaces/Settings/Logs/Logs.constants.ts index c84fe325cc8..9044fd26bd9 100644 --- a/apps/studio/components/interfaces/Settings/Logs/Logs.constants.ts +++ b/apps/studio/components/interfaces/Settings/Logs/Logs.constants.ts @@ -369,7 +369,6 @@ export enum LogsTableName { POSTGREST = 'postgrest_logs', SUPAVISOR = 'supavisor_logs', PGBOUNCER = 'pgbouncer_logs', - WAREHOUSE = 'warehouse_logs', PG_UPGRADE = 'pg_upgrade_logs', PG_CRON = 'pg_cron_logs', } @@ -384,7 +383,6 @@ export const LOGS_TABLES = { storage: LogsTableName.STORAGE, postgrest: LogsTableName.POSTGREST, supavisor: LogsTableName.SUPAVISOR, - warehouse: LogsTableName.WAREHOUSE, pg_upgrade: LogsTableName.PG_UPGRADE, pg_cron: LogsTableName.POSTGRES, pgbouncer: LogsTableName.PGBOUNCER, @@ -401,7 +399,6 @@ export const LOGS_SOURCE_DESCRIPTION = { [LogsTableName.POSTGREST]: 'RESTful API web server logs', [LogsTableName.SUPAVISOR]: 'Shared connection pooler logs for PostgreSQL', [LogsTableName.PGBOUNCER]: 'Dedicated connection pooler for PostgreSQL', - [LogsTableName.WAREHOUSE]: 'Logs obtained from a data warehouse collection', [LogsTableName.PG_UPGRADE]: 'Logs generated by the Postgres version upgrade process', [LogsTableName.PG_CRON]: 'Postgres logs from pg_cron cron jobs', } diff --git a/apps/studio/components/interfaces/Settings/Logs/Logs.types.ts b/apps/studio/components/interfaces/Settings/Logs/Logs.types.ts index d1f606a72ff..377f542ebb9 100644 --- a/apps/studio/components/interfaces/Settings/Logs/Logs.types.ts +++ b/apps/studio/components/interfaces/Settings/Logs/Logs.types.ts @@ -90,7 +90,6 @@ export type QueryType = | 'storage' | 'supavisor' | 'postgrest' - | 'warehouse' | 'pg_upgrade' | 'pg_cron' | 'pgbouncer' @@ -145,9 +144,3 @@ export interface DatetimeHelper { disabled?: boolean availableIn?: PlanId[] } - -export interface WarehouseCollection { - name: string - id: number - token: string -} diff --git a/apps/studio/components/interfaces/Settings/Logs/LogsQueryPanel.tsx b/apps/studio/components/interfaces/Settings/Logs/LogsQueryPanel.tsx index 5ea8b4292a3..f67aef39e04 100644 --- a/apps/studio/components/interfaces/Settings/Logs/LogsQueryPanel.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/LogsQueryPanel.tsx @@ -6,7 +6,6 @@ import { ReactNode, useState } from 'react' import { IS_PLATFORM } from 'common' import Table from 'components/to-be-cleaned/Table' import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' -import { useFlag } from 'hooks/ui/useFlag' import { logConstants } from 'shared-data' import { Badge, @@ -28,23 +27,16 @@ import { LogsTableName, } from './Logs.constants' import { DatePickerValue, LogsDatePicker } from './Logs.DatePickers' -import { LogsWarning, LogTemplate, WarehouseCollection } from './Logs.types' -import { WarehouseQueryTemplate } from './Warehouse.utils' +import { LogsWarning, LogTemplate } from './Logs.types' -export type SourceType = 'logs' | 'warehouse' export interface LogsQueryPanelProps { templates?: LogTemplate[] - warehouseTemplates?: WarehouseQueryTemplate[] defaultFrom: string defaultTo: string warnings: LogsWarning[] - warehouseCollections: WarehouseCollection[] - dataSource: SourceType onSelectTemplate: (template: LogTemplate) => void - onSelectWarehouseTemplate: (template: WarehouseQueryTemplate) => void onSelectSource: (source: string) => void onDateChange: (value: DatePickerValue) => void - onDataSourceChange: (sourceType: SourceType) => void } function DropdownMenuItemContent({ name, desc }: { name: ReactNode; desc?: string }) { @@ -58,17 +50,12 @@ function DropdownMenuItemContent({ name, desc }: { name: ReactNode; desc?: strin const LogsQueryPanel = ({ templates = [], - warehouseTemplates = [], defaultFrom, defaultTo, warnings, - warehouseCollections, - dataSource, onSelectTemplate, - onSelectWarehouseTemplate, onSelectSource, onDateChange, - onDataSourceChange, }: LogsQueryPanelProps) => { const [showReference, setShowReference] = useState(false) @@ -78,15 +65,12 @@ const LogsQueryPanel = ({ projectEdgeFunctionAll: edgeFunctionsEnabled, } = useIsFeatureEnabled(['project_auth:all', 'project_storage:all', 'project_edge_function:all']) - const warehouseEnabled = useFlag('warehouse') - const logsTableNames = Object.entries(LogsTableName) .filter(([key]) => { if (key === 'AUTH') return authEnabled if (key === 'STORAGE') return storageEnabled if (key === 'FN_EDGE') return edgeFunctionsEnabled if (key === 'PG_CRON') return false - if (key === 'WAREHOUSE') return false return true }) .map(([, value]) => value) @@ -117,90 +101,31 @@ const LogsQueryPanel = ({
- {warehouseEnabled && ( - - - - - - onDataSourceChange('logs')}> - - - onDataSourceChange('warehouse')}> - - Collections NEW - - } - desc="Query your collections" - /> - - - - )} - - {dataSource === 'warehouse' && ( - - - - - - {warehouseTemplates.map((template) => ( - onSelectWarehouseTemplate(template)} - > - - - ))} - {warehouseCollections.length === 0 && ( - + + + + + + {logsTableNames + .sort((a, b) => a.localeCompare(b)) + .map((source) => ( + onSelectSource(source)}> - )} - - - )} + ))} + + - {dataSource === 'logs' && ( - - - - - - {dataSource === 'logs' && - logsTableNames - .sort((a, b) => a.localeCompare(b)) - .map((source) => ( - onSelectSource(source)}> - - - ))} - - - )} - - {dataSource === 'logs' && IS_PLATFORM && ( + {IS_PLATFORM && (
- {dataSource === 'logs' && ( - -

Field Reference

-
- } - visible={showReference} - cancelText="Close" - onCancel={() => setShowReference(false)} - hideFooter - triggerElement={ + + +

Field Reference

- } - > - -
-

- The following table shows all the available paths that can be queried from each - respective source. Do note that to access nested keys, you would need to perform - the necessary{' '} - - unnesting joins - - -

-
-
- - setShowReference(false)} + icon={} + /> +
+ } + visible={showReference} + cancelText="Close" + onCancel={() => setShowReference(false)} + hideFooter + triggerElement={ + + } + > + +
+

+ The following table shows all the available paths that can be queried from each + respective source. Do note that to access nested keys, you would need to perform + the necessary{' '} + - - Path - , - - Type - , - ]} - body={schema.fields - .sort((a: any, b: any) => a.path - b.path) - .map((field) => ( - - ))} + unnesting joins + - - ))} - - - )} + +

+ + + + + {logConstants.schemas.map((schema) => ( + +
+ Path + , + + Type + , + ]} + body={schema.fields + .sort((a: any, b: any) => a.path - b.path) + .map((field) => ( + + ))} + /> + + ))} + + diff --git a/apps/studio/components/interfaces/Settings/Logs/Warehouse.utils.ts b/apps/studio/components/interfaces/Settings/Logs/Warehouse.utils.ts deleted file mode 100644 index fdb60183e09..00000000000 --- a/apps/studio/components/interfaces/Settings/Logs/Warehouse.utils.ts +++ /dev/null @@ -1,26 +0,0 @@ -export type WarehouseQueryTemplate = { - query: string - name: string - description: string -} - -export function createWarehouseQueryTemplates( - collections: { name: string }[] -): WarehouseQueryTemplate[] { - return collections.flatMap((collection) => [ - { - query: `select count(*) as event_count from \`${collection.name}\` -where timestamp > timestamp_sub(current_timestamp(), interval 1 day)`, - name: `${collection.name} - Daily Event Count`, - description: `Count events in the last 24 hours from ${collection.name} collection`, - }, - { - query: `select id, timestamp, event_message from \`${collection.name}\` - where timestamp > timestamp_sub(current_timestamp(), interval 1 day) - and event_message like '%YOUR_TEXT_HERE%' - order by timestamp desc limit 10`, - name: `${collection.name} - Filter by text`, - description: `Select last 10 events from ${collection.name} collection`, - }, - ]) -} diff --git a/apps/studio/components/interfaces/Sidebar.tsx b/apps/studio/components/interfaces/Sidebar.tsx index e729bc0b732..44bfc0797c0 100644 --- a/apps/studio/components/interfaces/Sidebar.tsx +++ b/apps/studio/components/interfaces/Sidebar.tsx @@ -223,8 +223,6 @@ const ProjectLinks = () => { const isNewAPIDocsEnabled = useIsAPIDocsSidePanelEnabled() const { securityLints, errorLints } = useLints() - const showWarehouse = useFlag('warehouse') - const activeRoute = router.pathname.split('/')[3] const { @@ -310,14 +308,11 @@ const ProjectLinks = () => { ) } else if (route.key === 'logs') { - // TODO: Undo this when warehouse flag is removed - const label = showWarehouse ? 'Logs & Analytics' : route.label - const newRoute = { ...route, label } return ( ) } else { diff --git a/apps/studio/components/layouts/LogsLayout/LogsSidebarMenuV2.tsx b/apps/studio/components/layouts/LogsLayout/LogsSidebarMenuV2.tsx index 86f999ecd7f..785bb992b68 100644 --- a/apps/studio/components/layouts/LogsLayout/LogsSidebarMenuV2.tsx +++ b/apps/studio/components/layouts/LogsLayout/LogsSidebarMenuV2.tsx @@ -9,13 +9,9 @@ import { useFeaturePreviewModal, useUnifiedLogsPreview, } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext' -import { CreateWarehouseCollectionModal } from 'components/interfaces/DataWarehouse/CreateWarehouseCollection' -import { WarehouseMenuItem } from 'components/interfaces/DataWarehouse/WarehouseMenuItem' import SavedQueriesItem from 'components/interfaces/Settings/Logs/Logs.SavedQueriesItem' import { LogsSidebarItem } from 'components/interfaces/Settings/Logs/SidebarV2/SidebarItem' import { ButtonTooltip } from 'components/ui/ButtonTooltip' -import { useWarehouseCollectionsQuery } from 'data/analytics/warehouse-collections-query' -import { useWarehouseTenantQuery } from 'data/analytics/warehouse-tenant-query' import { useContentQuery } from 'data/content/content-query' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import { useCurrentOrgPlan } from 'hooks/misc/useCurrentOrgPlan' @@ -27,14 +23,7 @@ import { Collapsible_Shadcn_, CollapsibleContent_Shadcn_, CollapsibleTrigger_Shadcn_, - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, Separator, - Tooltip, - TooltipContent, - TooltipTrigger, } from 'ui' import { InnerSideBarEmptyPanel, @@ -90,16 +79,11 @@ export function LogsSidebarMenuV2() { const router = useRouter() const { ref } = useParams() as { ref: string } - const warehouseEnabled = useFlag('warehouse') const unifiedLogsFlagEnabled = useFlag('unifiedLogs') const { selectFeaturePreview } = useFeaturePreviewModal() const { enable: enableUnifiedLogs } = useUnifiedLogsPreview() const [searchText, setSearchText] = useState('') - const [createCollectionOpen, setCreateCollectionOpen] = useState(false) - const canCreateCollection = useCheckPermissions(PermissionAction.ANALYTICS_WRITE, 'logflare') - - const { data: tenantData } = useWarehouseTenantQuery({ projectRef: ref }) const { projectAuthAll: authEnabled, @@ -107,11 +91,6 @@ export function LogsSidebarMenuV2() { realtimeAll: realtimeEnabled, } = useIsFeatureEnabled(['project_storage:all', 'project_auth:all', 'realtime:all']) - const { data: whCollections, isLoading: whCollectionsLoading } = useWarehouseCollectionsQuery( - { projectRef: ref }, - { enabled: IS_PLATFORM && warehouseEnabled && !!tenantData } - ) - const { plan: orgPlan, isLoading: isOrgPlanLoading } = useCurrentOrgPlan() const isFreePlan = !isOrgPlanLoading && orgPlan?.id === 'free' @@ -223,9 +202,6 @@ export function LogsSidebarMenuV2() { const filteredOperationalLogs = OPERATIONAL_COLLECTIONS.filter((collection) => { return collection?.name.toLowerCase().includes(searchText.toLowerCase()) }) - const filteredWarehouse = whCollections?.filter((collection) => { - return collection.name.toLowerCase().includes(searchText.toLowerCase()) - }) return (
@@ -285,56 +261,11 @@ export function LogsSidebarMenuV2() { > - {warehouseEnabled ? ( - - - - - - {!canCreateCollection && ( - - You need additional permissions to create a collection - - )} - - - - ) : ( -
@@ -361,17 +292,6 @@ export function LogsSidebarMenuV2() { /> ) })} - {whCollectionsLoading && warehouseEnabled ? ( -
- -
- ) : filteredWarehouse?.length ? ( -
- {filteredWarehouse.map((collection) => ( - - ))} -
- ) : null} {OPERATIONAL_COLLECTIONS.length > 0 && ( <> diff --git a/apps/studio/data/analytics/keys.ts b/apps/studio/data/analytics/keys.ts index 5f1e97e656d..fa77be8e2c4 100644 --- a/apps/studio/data/analytics/keys.ts +++ b/apps/studio/data/analytics/keys.ts @@ -1,19 +1,4 @@ export const analyticsKeys = { - // warehouse - warehouseQuery: (projectRef: string, query: string) => - ['projects', projectRef, 'warehouse', 'query', query] as const, - warehouseTenant: (projectRef: string) => ['projects', projectRef, 'warehouse', 'tenant'] as const, - warehouseCollections: (projectRef: string) => - ['projects', projectRef, 'warehouse', 'collections'] as const, - warehouseCollectionsCreate: (projectRef: string) => - ['projects', projectRef, 'warehouse', 'collections', 'create'] as const, - warehouseEndpoints: (projectRef: string) => - ['projects', projectRef, 'warehouse', 'endpoints'] as const, - warehouseBackends: (projectRef: string) => - ['projects', projectRef, 'warehouse', 'backends'] as const, - warehouseAccessTokens: (projectRef: string) => - ['projects', projectRef, 'warehouse', 'access-tokens'] as const, - // logs/reports endpoints functionsInvStats: ( projectRef: string | undefined, diff --git a/apps/studio/data/analytics/warehouse-access-tokens-query.ts b/apps/studio/data/analytics/warehouse-access-tokens-query.ts deleted file mode 100644 index bfa30ccb304..00000000000 --- a/apps/studio/data/analytics/warehouse-access-tokens-query.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { useQuery, UseQueryOptions } from '@tanstack/react-query' -import { analyticsKeys } from './keys' -import { get, handleError } from 'data/fetchers' -import { ResponseError } from 'types' - -export type WarehouseAccessTokensVariables = { - projectRef: string -} - -export async function getWarehouseAccessTokens( - { projectRef }: WarehouseAccessTokensVariables, - signal?: AbortSignal -) { - if (!projectRef) { - throw new Error('projectRef is required') - } - - // TODO: Remove typecast when codegen types are fixed - const response = await get(`/platform/projects/{ref}/analytics/warehouse/access-tokens`, { - params: { path: { ref: projectRef } }, - signal, - }) - - if (response.error) { - handleError(response) - } - - return response -} - -export type WarehouseAccessTokensData = Awaited> -export type WarehouseAccessTokensError = ResponseError -/** - * This will be deprecated or rewritten in favor of the new project API keys - */ -export const useWarehouseAccessTokensQuery = ( - { projectRef }: WarehouseAccessTokensVariables, - { - enabled = true, - ...options - }: UseQueryOptions = {} -) => - useQuery( - analyticsKeys.warehouseAccessTokens(projectRef), - ({ signal }) => getWarehouseAccessTokens({ projectRef }, signal), - { - enabled: enabled && typeof projectRef !== 'undefined', - ...options, - } - ) diff --git a/apps/studio/data/analytics/warehouse-collection-log-detail-query.ts b/apps/studio/data/analytics/warehouse-collection-log-detail-query.ts deleted file mode 100644 index 4d8beefd712..00000000000 --- a/apps/studio/data/analytics/warehouse-collection-log-detail-query.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { useWarehouseQueryQuery } from './warehouse-query' - -export function useWarehouseLogDetailQuery({ - ref, - collectionName, - logId, - columns = ['id', 'timestamp'], -}: { - ref: string - collectionName?: string - logId?: string - columns?: string[] -}) { - const enabled = !!logId && !!collectionName - const sql = `select ${columns.join(',')} from \`${collectionName}\` - where id = '${logId}' and timestamp > '2024-01-01' limit 1` - - return useWarehouseQueryQuery({ ref, sql }, { enabled }) -} diff --git a/apps/studio/data/analytics/warehouse-collections-create-mutation.ts b/apps/studio/data/analytics/warehouse-collections-create-mutation.ts deleted file mode 100644 index f1ec421ae3d..00000000000 --- a/apps/studio/data/analytics/warehouse-collections-create-mutation.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query' -import { handleError, post } from 'data/fetchers' -import { toast } from 'sonner' -import type { ResponseError } from 'types' -import { analyticsKeys } from './keys' - -export type CreateCollectionArgs = { - projectRef: string - name: string - retention_days: number -} - -export async function createCollection({ projectRef, name, retention_days }: CreateCollectionArgs) { - const { data, error } = await post('/platform/projects/{ref}/analytics/warehouse/collections', { - params: { path: { ref: projectRef } }, - body: { name, retention_days }, - } as any) - - if (error) handleError(error) - return data -} - -type CreateCollectionData = Awaited> - -export const useCreateCollection = ({ - onSuccess, - onError, - ...options -}: Omit< - UseMutationOptions, - 'mutationFn' -> = {}) => { - const queryClient = useQueryClient() - - return useMutation( - (vars) => { - // temporal fix - // prevent users from creating collections with the same name - // this will be handled by the backend - // - jordi - - const existingCollectionNames = queryClient - .getQueryData(analyticsKeys.warehouseCollections(vars.projectRef)) - ?.map((c) => c.name) - - if (existingCollectionNames?.includes(vars.name)) { - throw new Error(`Collection with the name ${vars.name} already exists`) - } - - return createCollection(vars) - }, - { - async onSuccess(data, variables, context) { - const { projectRef } = variables - - await queryClient.invalidateQueries(analyticsKeys.warehouseCollections(projectRef)) - - await onSuccess?.(data, variables, context) - }, - async onError(data, variables, context) { - if (onError === undefined) { - toast.error(`Failed to create collection: ${data.message}`) - } else { - onError(data, variables, context) - } - }, - ...options, - } - ) -} diff --git a/apps/studio/data/analytics/warehouse-collections-delete-mutation.ts b/apps/studio/data/analytics/warehouse-collections-delete-mutation.ts deleted file mode 100644 index 376e5f378f3..00000000000 --- a/apps/studio/data/analytics/warehouse-collections-delete-mutation.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query' -import { toast } from 'sonner' - -import { del, handleError } from 'data/fetchers' -import type { ResponseError } from 'types' -import { analyticsKeys } from './keys' - -export type DeleteCollectionVariables = { - projectRef: string - collectionToken: string -} - -export async function deleteCollection({ projectRef, collectionToken }: DeleteCollectionVariables) { - const { data, error } = await del( - '/platform/projects/{ref}/analytics/warehouse/collections/{token}', - { - params: { path: { ref: projectRef, token: collectionToken } } as any, - } - ) - - if (error) handleError(error) - return data -} - -type DeleteCollectionData = Awaited> - -export const useDeleteCollectionMutation = ({ - onSuccess, - onError, - ...options -}: Omit< - UseMutationOptions, - 'mutationFn' -> = {}) => { - const queryClient = useQueryClient() - - return useMutation( - (vars) => deleteCollection(vars), - { - async onSuccess(data, variables, context) { - const { projectRef } = variables - - await queryClient.invalidateQueries(analyticsKeys.warehouseCollections(projectRef)) - - await onSuccess?.(data, variables, context) - }, - async onError(data, variables, context) { - if (onError === undefined) { - toast.error(`Failed to delete collection: ${data.message}`) - } else { - onError(data, variables, context) - } - }, - ...options, - } - ) -} diff --git a/apps/studio/data/analytics/warehouse-collections-query.ts b/apps/studio/data/analytics/warehouse-collections-query.ts deleted file mode 100644 index 4fde686a646..00000000000 --- a/apps/studio/data/analytics/warehouse-collections-query.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import { get, handleError } from 'data/fetchers' -import { IS_PLATFORM } from 'lib/constants' -import { analyticsKeys } from './keys' - -type WarehouseCollectionsVariables = { - projectRef: string -} - -export async function getWarehouseCollections( - { projectRef }: WarehouseCollectionsVariables, - signal?: AbortSignal -) { - if (!projectRef) { - throw new Error('projectRef is required') - } - - // TODO: Remove type cast when codegen types are fixed - const { data, error } = await get(`/platform/projects/{ref}/analytics/warehouse/collections`, { - params: { path: { ref: projectRef } }, - signal, - }) - - if (error) handleError(error) - - return data -} - -export type WarehouseCollectionsData = Awaited> - -export const useWarehouseCollectionsQuery = ( - { projectRef }: WarehouseCollectionsVariables, - { enabled }: { enabled: boolean } = { enabled: true } -) => - useQuery( - analyticsKeys.warehouseCollections(projectRef), - ({ signal }) => getWarehouseCollections({ projectRef }, signal), - { - enabled: enabled && !!projectRef && IS_PLATFORM, - } - ) diff --git a/apps/studio/data/analytics/warehouse-collections-update-mutation.ts b/apps/studio/data/analytics/warehouse-collections-update-mutation.ts deleted file mode 100644 index 6f96ef05575..00000000000 --- a/apps/studio/data/analytics/warehouse-collections-update-mutation.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query' -import { toast } from 'sonner' - -import { handleError, patch } from 'data/fetchers' -import type { ResponseError } from 'types' -import { analyticsKeys } from './keys' - -export type UpdateCollectionArgs = { - projectRef: string - collectionToken: string -} - -export type UpdateCollectionPayload = { - name?: string - // description?: string - retention_days?: number -} - -export async function updateCollection( - { projectRef, collectionToken }: UpdateCollectionArgs, - payload: UpdateCollectionPayload -) { - const { data, error } = await patch( - '/platform/projects/{ref}/analytics/warehouse/collections/{token}', - { - params: { path: { ref: projectRef, token: collectionToken } }, - body: payload, - } as any - ) - - if (error) handleError(error) - return data -} - -type UpdateCollectionData = Awaited> - -export const useUpdateCollection = ({ - onSuccess, - onError, - ...options -}: Omit< - UseMutationOptions, - 'mutationFn' -> = {}) => { - const queryClient = useQueryClient() - - return useMutation< - UpdateCollectionData, - ResponseError, - UpdateCollectionPayload & UpdateCollectionArgs - >( - (payload) => - updateCollection( - { projectRef: payload.projectRef, collectionToken: payload.collectionToken }, - { name: payload.name, retention_days: payload.retention_days } - ), - { - async onSuccess(data, variables, context) { - const { projectRef } = variables - - await queryClient.invalidateQueries(analyticsKeys.warehouseCollections(projectRef)) - - await onSuccess?.(data, variables, context) - }, - async onError(data, variables, context) { - if (onError === undefined) { - toast.error(`Failed to update collection: ${data.message}`) - } else { - onError(data, variables, context) - } - }, - ...options, - } - ) -} diff --git a/apps/studio/data/analytics/warehouse-query.ts b/apps/studio/data/analytics/warehouse-query.ts deleted file mode 100644 index 54c34a18414..00000000000 --- a/apps/studio/data/analytics/warehouse-query.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { useQuery, UseQueryOptions } from '@tanstack/react-query' -import { get, handleError } from 'data/fetchers' -import { analyticsKeys } from './keys' -import { ResponseError } from 'types' - -export type WarehouseQueryVariables = { - ref: string - sql: string -} - -export async function getWarehouseQuery( - { ref, sql }: WarehouseQueryVariables, - signal?: AbortSignal -) { - if (!sql) { - throw new Error('SQL must be provided') - } - - const { data, error } = await get(`/platform/projects/{ref}/analytics/warehouse/query`, { - params: { path: { ref: ref }, query: { bq_sql: sql } }, - signal, - } as any) - - if (error) { - handleError(error) - } - - // TODO!: Remove type assertion when we have a proper type for the response - return data as { result: any[] } -} - -export type WarehouseQueryData = Awaited> -export type WarehouseQueryError = ResponseError - -export const useWarehouseQueryQuery = ( - { ref, sql }: WarehouseQueryVariables, - { enabled, ...options }: UseQueryOptions = {} -) => - useQuery( - analyticsKeys.warehouseQuery(ref, sql), - ({ signal }) => getWarehouseQuery({ ref, sql }, signal), - { - enabled, - staleTime: Infinity, - cacheTime: 15 * 60 * 1000, // 15 mins cache time - refetchOnMount: false, - refetchInterval: false, - ...options, - } - ) diff --git a/apps/studio/data/analytics/warehouse-tenant-query.ts b/apps/studio/data/analytics/warehouse-tenant-query.ts deleted file mode 100644 index 624a6719641..00000000000 --- a/apps/studio/data/analytics/warehouse-tenant-query.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { useQuery, UseQueryOptions } from '@tanstack/react-query' -import { get, handleError } from 'data/fetchers' -import { IS_PLATFORM } from 'lib/constants' -import { ResponseError } from 'types' -import { analyticsKeys } from './keys' - -export type WarehouseTenantVariables = { - projectRef: string -} - -export type WarehouseTenantResponse = any - -export async function getWarehouseTenant( - { projectRef }: WarehouseTenantVariables, - signal?: AbortSignal -) { - if (!projectRef) { - throw new Error('projectRef is required') - } - const { data, error } = await get(`/platform/projects/{ref}/analytics/warehouse/tenant`, { - params: { path: { ref: projectRef } }, - signal, - }) - if (error) { - handleError(error) - } - - return data -} - -export type WarehouseTenantData = Awaited> -export type WarehouseTenantError = ResponseError - -export const useWarehouseTenantQuery = ( - { projectRef }: WarehouseTenantVariables, - { - enabled = true, - ...options - }: UseQueryOptions = {} -) => - useQuery( - analyticsKeys.warehouseTenant(projectRef), - ({ signal }) => getWarehouseTenant({ projectRef }, signal), - { - enabled: enabled && !!projectRef && IS_PLATFORM, - staleTime: Infinity, - // 2H mins cache time - cacheTime: 120 * 60 * 1000, - refetchOnMount: false, - refetchInterval: false, - ...options, - } - ) diff --git a/apps/studio/hooks/analytics/useSelectedLog.ts b/apps/studio/hooks/analytics/useSelectedLog.ts index 5b52ce62d38..f156222e89b 100644 --- a/apps/studio/hooks/analytics/useSelectedLog.ts +++ b/apps/studio/hooks/analytics/useSelectedLog.ts @@ -1,6 +1,5 @@ import { useQueryState } from 'nuqs' -// Used in logs and warehouse. export function useSelectedLog() { return useQueryState('log') } diff --git a/apps/studio/hooks/analytics/useSingleLog.tsx b/apps/studio/hooks/analytics/useSingleLog.tsx index 6ca288c2122..b773b45e673 100644 --- a/apps/studio/hooks/analytics/useSingleLog.tsx +++ b/apps/studio/hooks/analytics/useSingleLog.tsx @@ -33,9 +33,7 @@ function useSingleLog({ const params: LogsEndpointParams = { ...paramsToMerge, sql } - const isWarehouseQuery = queryType === 'warehouse' - // Warehouse queries are handled differently - const enabled = Boolean(id && table && !isWarehouseQuery) + const enabled = Boolean(id && table) const { data, diff --git a/apps/studio/pages/project/[ref]/logs/collections/[collectionToken].tsx b/apps/studio/pages/project/[ref]/logs/collections/[collectionToken].tsx deleted file mode 100644 index f10f5f2a3da..00000000000 --- a/apps/studio/pages/project/[ref]/logs/collections/[collectionToken].tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { WarehouseCollectionDetail } from 'components/interfaces/DataWarehouse/WarehouseCollectionDetail' -import DefaultLayout from 'components/layouts/DefaultLayout' -import LogsLayout from 'components/layouts/LogsLayout/LogsLayout' -import { NextPageWithLayout } from 'types' - -const PageLayout: NextPageWithLayout = () => - -PageLayout.getLayout = (page) => ( - - {page} - -) - -export default PageLayout diff --git a/apps/studio/pages/project/[ref]/logs/explorer/index.tsx b/apps/studio/pages/project/[ref]/logs/explorer/index.tsx index 85dca740843..32a15ef2413 100644 --- a/apps/studio/pages/project/[ref]/logs/explorer/index.tsx +++ b/apps/studio/pages/project/[ref]/logs/explorer/index.tsx @@ -22,17 +22,14 @@ import { maybeShowUpgradePrompt, useEditorHints, } from 'components/interfaces/Settings/Logs/Logs.utils' -import LogsQueryPanel, { SourceType } from 'components/interfaces/Settings/Logs/LogsQueryPanel' +import LogsQueryPanel from 'components/interfaces/Settings/Logs/LogsQueryPanel' import LogTable from 'components/interfaces/Settings/Logs/LogTable' import UpgradePrompt from 'components/interfaces/Settings/Logs/UpgradePrompt' -import { createWarehouseQueryTemplates } from 'components/interfaces/Settings/Logs/Warehouse.utils' import DefaultLayout from 'components/layouts/DefaultLayout' import LogsLayout from 'components/layouts/LogsLayout/LogsLayout' import CodeEditor from 'components/ui/CodeEditor/CodeEditor' import LoadingOpacity from 'components/ui/LoadingOpacity' import ShimmerLine from 'components/ui/ShimmerLine' -import { useWarehouseCollectionsQuery } from 'data/analytics/warehouse-collections-query' -import { useWarehouseQueryQuery } from 'data/analytics/warehouse-query' import { useContentQuery } from 'data/content/content-query' import { UpsertContentPayload, @@ -55,8 +52,6 @@ import { ResizablePanelGroup, } from 'ui' -const PLACEHOLDER_WAREHOUSE_QUERY = - '-- Fetch the last 10 logs in the last 7 days \nselect id, timestamp, event_message from `COLLECTION_NAME` \nwhere timestamp > timestamp_sub(current_timestamp(), interval 7 day) \norder by timestamp desc limit 10' const LOCAL_PLACEHOLDER_QUERY = 'select\n timestamp, event_message, metadata\n from edge_logs limit 5' @@ -79,13 +74,8 @@ export const LogsExplorerPage: NextPageWithLayout = () => { const { timestampStart, timestampEnd, setTimeRange } = useLogsUrlState() const [editorValue, setEditorValue] = useState(PLACEHOLDER_QUERY) - const [warehouseEditorId, setWarehouseEditorId] = useState(uuidv4()) - const [warehouseEditorValue, setWarehouseEditorValue] = useState( - PLACEHOLDER_WAREHOUSE_QUERY - ) const [saveModalOpen, setSaveModalOpen] = useState(false) const [warnings, setWarnings] = useState([]) - const [sourceType, setSourceType] = useState((routerSource as SourceType) || 'logs') const [selectedLog, setSelectedLog] = useState(null) const [recentLogs, setRecentLogs] = useLocalStorage( @@ -98,7 +88,6 @@ export const LogsExplorerPage: NextPageWithLayout = () => { type: 'log_sql', }) const query = content?.content.find((x) => x.id === queryId) - const { params, logData, @@ -112,24 +101,11 @@ export const LogsExplorerPage: NextPageWithLayout = () => { iso_timestamp_start: timestampStart, iso_timestamp_end: timestampEnd, }, - sourceType === 'logs' + true ) - const { - refetch: runWarehouseQuery, - data: warehouseResults, - isFetching: warehouseFetching, - error: warehouseError, - } = useWarehouseQueryQuery( - { ref: projectRef, sql: warehouseEditorValue }, - { - enabled: false, - } - ) - const results = sourceType === 'warehouse' ? warehouseResults?.result : logData - const isLoading = logsLoading || warehouseFetching - - const { data: warehouseCollections } = useWarehouseCollectionsQuery({ projectRef }) + const results = logData + const isLoading = logsLoading const { mutate: upsertContent, isLoading: isUpsertingContent } = useContentUpsertMutation({ onError: (e) => { @@ -187,40 +163,6 @@ export const LogsExplorerPage: NextPageWithLayout = () => { const handleRun = (value?: string | React.MouseEvent) => { const query = typeof value === 'string' ? value || editorValue : editorValue - if (sourceType === 'warehouse') { - const whQuery = warehouseEditorValue - - if (!warehouseCollections?.length) { - toast.error('You do not have any collections yet.') - return - } - - // Check that a collection name is included in the query - const collectionNames = warehouseCollections?.map((collection) => collection.name) - const collectionExists = collectionNames?.find((collectionName) => - whQuery.includes(collectionName) - ) - - if (!collectionExists) { - toast.error('Please specify a collection name in the query') - return - } - - // Check that the user is not trying to query logs tables and warehouse collections at the same time - const logsSources = Object.values(LOGS_TABLES) - const logsSourceExists = logsSources.find((source) => whQuery.includes(source)) - - if (logsSourceExists) { - return toast.error('Cannot query logs tables from current query.') - } - - runWarehouseQuery() - return router.push({ - pathname: router.pathname, - query: { ...router.query, q: query }, - }) - } - changeQuery(query) runQuery() router.push({ @@ -231,30 +173,25 @@ export const LogsExplorerPage: NextPageWithLayout = () => { } const handleInsertSource = (source: string) => { - if (sourceType === 'warehouse') { - //TODO: Only one collection can be queried at a time, we need to replace the current collection from the query for the new one - return setWarehouseEditorId(uuidv4()) - } else { - if (editorRef.current && monaco) { - const editorModel = editorRef.current?.getModel() - const currentValue = editorRef.current.getValue() - const index = currentValue.indexOf('from') + if (editorRef.current && monaco) { + const editorModel = editorRef.current?.getModel() + const currentValue = editorRef.current.getValue() + const index = currentValue.indexOf('from') - const updatedValue = - index < 0 - ? `${currentValue}${source}` - : `${currentValue.substring(0, index + 4)} ${source} ${currentValue.substring(index + 5)}` + const updatedValue = + index < 0 + ? `${currentValue}${source}` + : `${currentValue.substring(0, index + 4)} ${source} ${currentValue.substring(index + 5)}` - editorRef.current.pushUndoStop() - editorRef.current.executeEdits(`insert-identifier`, [ - { - text: updatedValue, - range: editorModel?.getFullModelRange() ?? new monaco.Range(1, 1, 1, 1), - }, - ]) - editorRef.current.pushUndoStop() - editorRef.current.focus() - } + editorRef.current.pushUndoStop() + editorRef.current.executeEdits(`insert-identifier`, [ + { + text: updatedValue, + range: editorModel?.getFullModelRange() ?? new monaco.Range(1, 1, 1, 1), + }, + ]) + editorRef.current.pushUndoStop() + editorRef.current.focus() } } @@ -316,14 +253,8 @@ export const LogsExplorerPage: NextPageWithLayout = () => { } useEffect(() => { - if (warehouseError) toast.error(warehouseError.message) - }, [warehouseError]) - - useEffect(() => { - // on mount, set initial values if (q) { setEditorValue(q) - setWarehouseEditorValue(q) } }, [q]) @@ -368,43 +299,18 @@ export const LogsExplorerPage: NextPageWithLayout = () => { onDateChange={handleDateChange} onSelectSource={handleInsertSource} templates={TEMPLATES.filter((template) => template.mode === 'custom')} - warehouseCollections={warehouseCollections || []} onSelectTemplate={onSelectTemplate} - warehouseTemplates={createWarehouseQueryTemplates(warehouseCollections || [])} - onSelectWarehouseTemplate={(template) => { - setWarehouseEditorValue(template.query) - setWarehouseEditorId(uuidv4()) - }} warnings={warnings} - dataSource={sourceType} - onDataSourceChange={(srcType) => { - setSourceType(srcType) - router.push({ - pathname: router.pathname, - query: { ...router.query, source: srcType }, - }) - }} /> - - {sourceType === 'warehouse' ? ( - setWarehouseEditorValue(v || '')} - actions={{ runQuery: { enabled: true, callback: handleRun } }} - /> - ) : ( - setEditorValue(v || '')} - actions={{ runQuery: { enabled: true, callback: handleRun } }} - /> - )} + setEditorValue(v || '')} + actions={{ runQuery: { enabled: true, callback: handleRun } }} + /> diff --git a/apps/studio/tests/features/logs/LogsQueryPanel.test.tsx b/apps/studio/tests/features/logs/LogsQueryPanel.test.tsx index df741c9a2e6..7e860a924e9 100644 --- a/apps/studio/tests/features/logs/LogsQueryPanel.test.tsx +++ b/apps/studio/tests/features/logs/LogsQueryPanel.test.tsx @@ -13,12 +13,7 @@ test('run and clear', async () => { onSelectSource={() => {}} onSelectTemplate={() => {}} warnings={[]} - warehouseCollections={[]} - dataSource="logs" - onDataSourceChange={() => {}} templates={[]} - warehouseTemplates={[]} - onSelectWarehouseTemplate={() => {}} /> ) await expect(screen.findByPlaceholderText(/Search/)).rejects.toThrow()