mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
chore: remove pg meta schema store
This commit is contained in:
1 parent
64ba7be134
commit
04575b65b1
35 files changed
+378
-364
No files matched your search
@@ -4,19 +4,25 @@ import type { PostgresExtension, PostgresSchema } from '@supabase/postgres-meta'
|
||||
|
||||
import { useStore } from 'hooks'
|
||||
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
|
||||
import { useSchemasQuery } from 'data/database/schemas-query'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
|
||||
interface Props {
|
||||
export interface EnableExtensionModalProps {
|
||||
visible: boolean
|
||||
extension: PostgresExtension
|
||||
onCancel: () => void
|
||||
}
|
||||
|
||||
const EnableExtensionModal: FC<Props> = ({ visible, extension, onCancel }) => {
|
||||
const EnableExtensionModal = ({ visible, extension, onCancel }: EnableExtensionModalProps) => {
|
||||
const { project } = useProjectContext()
|
||||
const { ui, meta } = useStore()
|
||||
const [defaultSchema, setDefaultSchema] = useState()
|
||||
const [fetchingSchemaInfo, setFetchingSchemaInfo] = useState(false)
|
||||
|
||||
const schemas = meta.schemas.list()
|
||||
const { data: schemas, isLoading: isSchemasLoading } = useSchemasQuery({
|
||||
projectRef: project?.ref,
|
||||
connectionString: project?.connectionString,
|
||||
})
|
||||
|
||||
// [Joshen] Worth checking in with users - whether having this schema selection
|
||||
// might be confusing, and if we should have a tooltip to explain that schemas
|
||||
@@ -44,7 +50,7 @@ const EnableExtensionModal: FC<Props> = ({ visible, extension, onCancel }) => {
|
||||
return () => {
|
||||
cancel = true
|
||||
}
|
||||
}, [visible])
|
||||
}, [visible, extension.name])
|
||||
|
||||
const validate = (values: any) => {
|
||||
const errors: any = {}
|
||||
@@ -124,7 +130,7 @@ const EnableExtensionModal: FC<Props> = ({ visible, extension, onCancel }) => {
|
||||
return (
|
||||
<div className="space-y-4 py-4">
|
||||
<Modal.Content>
|
||||
{fetchingSchemaInfo ? (
|
||||
{fetchingSchemaInfo || isSchemasLoading ? (
|
||||
<div className="space-y-2">
|
||||
<ShimmeringLoader />
|
||||
<div className="w-3/4">
|
||||
@@ -156,8 +162,7 @@ const EnableExtensionModal: FC<Props> = ({ visible, extension, onCancel }) => {
|
||||
Create a new schema "{extension.name}"
|
||||
</Listbox.Option>
|
||||
<Modal.Separator />
|
||||
{/* @ts-ignore */}
|
||||
{schemas.map((schema: PostgresSchema) => {
|
||||
{schemas?.map((schema: PostgresSchema) => {
|
||||
return (
|
||||
<Listbox.Option
|
||||
key={schema.id}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { FC } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { includes } from 'lodash'
|
||||
import { Button, Dropdown, IconEdit3, IconFileText, IconMoreVertical, IconTrash } from 'ui'
|
||||
@@ -7,24 +6,25 @@ import * as Tooltip from '@radix-ui/react-tooltip'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
|
||||
import { checkPermissions, useStore } from 'hooks'
|
||||
import { EXCLUDED_SCHEMAS } from 'lib/constants/schemas'
|
||||
import Table from 'components/to-be-cleaned/Table'
|
||||
|
||||
interface Props {
|
||||
export interface FunctionListProps {
|
||||
schema: string
|
||||
filterString: string
|
||||
editFunction: (fn: any) => void
|
||||
deleteFunction: (fn: any) => void
|
||||
}
|
||||
|
||||
const FunctionList: FC<Props> = ({
|
||||
const FunctionList = ({
|
||||
schema,
|
||||
filterString,
|
||||
editFunction = () => {},
|
||||
deleteFunction = () => {},
|
||||
}) => {
|
||||
}: FunctionListProps) => {
|
||||
const router = useRouter()
|
||||
const { ui, meta } = useStore()
|
||||
const functions = meta.functions.list((fn: any) => !meta.excludedSchemas.includes(fn.schema))
|
||||
const functions = meta.functions.list((fn: any) => !EXCLUDED_SCHEMAS.includes(fn.schema))
|
||||
const filteredFunctions = functions.filter((x: any) =>
|
||||
includes(x.name.toLowerCase(), filterString.toLowerCase())
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { FC, useState } from 'react'
|
||||
import { uniqBy, map as lodashMap, includes } from 'lodash'
|
||||
import { useState } from 'react'
|
||||
import { uniqBy, map as lodashMap, includes, noop } from 'lodash'
|
||||
import { Button, IconSearch, IconLoader, Input } from 'ui'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import * as Tooltip from '@radix-ui/react-tooltip'
|
||||
@@ -7,26 +7,26 @@ import { PostgresFunction } from '@supabase/postgres-meta'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
|
||||
import { checkPermissions, useStore } from 'hooks'
|
||||
import AlphaPreview from 'components/to-be-cleaned/AlphaPreview'
|
||||
import { EXCLUDED_SCHEMAS } from 'lib/constants/schemas'
|
||||
import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState'
|
||||
import SchemaTable from './SchemaTable'
|
||||
|
||||
interface Props {
|
||||
export interface FunctionsListProps {
|
||||
createFunction: () => void
|
||||
editFunction: (fn: PostgresFunction) => void
|
||||
deleteFunction: (fn: PostgresFunction) => void
|
||||
}
|
||||
|
||||
const FunctionsList: FC<Props> = ({
|
||||
createFunction = () => {},
|
||||
editFunction = () => {},
|
||||
deleteFunction = () => {},
|
||||
}) => {
|
||||
const FunctionsList = ({
|
||||
createFunction = noop,
|
||||
editFunction = noop,
|
||||
deleteFunction = noop,
|
||||
}: FunctionsListProps) => {
|
||||
const { meta } = useStore()
|
||||
const [filterString, setFilterString] = useState<string>('')
|
||||
|
||||
const functions = meta.functions.list(
|
||||
(fn: PostgresFunction) => !meta.excludedSchemas.includes(fn.schema)
|
||||
(fn: PostgresFunction) => !EXCLUDED_SCHEMAS.includes(fn.schema)
|
||||
)
|
||||
const filteredFunctions = functions.filter((x: PostgresFunction) =>
|
||||
includes(x.name?.toLowerCase(), filterString.toLowerCase())
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import { FC, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { Button, Input, IconChevronLeft, IconSearch, IconAlertCircle } from 'ui'
|
||||
import type { PostgresPublication } from '@supabase/postgres-meta'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
|
||||
import { checkPermissions, useStore } from 'hooks'
|
||||
import PublicationsTableItem from './PublicationsTableItem'
|
||||
import { EXCLUDED_SCHEMAS } from 'lib/constants/schemas'
|
||||
import Table from 'components/to-be-cleaned/Table'
|
||||
import NoSearchResults from 'components/to-be-cleaned/NoSearchResults'
|
||||
import InformationBox from 'components/ui/InformationBox'
|
||||
import PublicationsTableItem from './PublicationsTableItem'
|
||||
|
||||
interface Props {
|
||||
export interface PublicationsTablesProps {
|
||||
selectedPublication: PostgresPublication
|
||||
onSelectBack: () => void
|
||||
}
|
||||
|
||||
const PublicationsTables: FC<Props> = ({ selectedPublication, onSelectBack }) => {
|
||||
const PublicationsTables = ({ selectedPublication, onSelectBack }: PublicationsTablesProps) => {
|
||||
const { meta } = useStore()
|
||||
const [filterString, setFilterString] = useState<string>('')
|
||||
|
||||
@@ -26,10 +27,10 @@ const PublicationsTables: FC<Props> = ({ selectedPublication, onSelectBack }) =>
|
||||
|
||||
const tables =
|
||||
filterString.length === 0
|
||||
? meta.tables.list((table: any) => !meta.excludedSchemas.includes(table.schema))
|
||||
? meta.tables.list((table: any) => !EXCLUDED_SCHEMAS.includes(table.schema))
|
||||
: meta.tables.list(
|
||||
(table: any) =>
|
||||
!meta.excludedSchemas.includes(table.schema) && table.name.includes(filterString)
|
||||
!EXCLUDED_SCHEMAS.includes(table.schema) && table.name.includes(filterString)
|
||||
)
|
||||
|
||||
// const publication = selectedPublication
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { FC, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import * as Tooltip from '@radix-ui/react-tooltip'
|
||||
import { noop } from 'lodash'
|
||||
import { Input, Button, IconSearch, IconPlus, IconChevronLeft, IconEdit3, IconTrash } from 'ui'
|
||||
import type { PostgresTable } from '@supabase/postgres-meta'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
|
||||
import { useStore, checkPermissions } from 'hooks'
|
||||
import { EXCLUDED_SCHEMAS } from 'lib/constants/schemas'
|
||||
import Table from 'components/to-be-cleaned/Table'
|
||||
import NoSearchResults from 'components/to-be-cleaned/NoSearchResults'
|
||||
|
||||
interface Props {
|
||||
export interface ColumnListProps {
|
||||
selectedTable: PostgresTable
|
||||
onSelectBack: () => void
|
||||
onAddColumn: () => void
|
||||
@@ -17,13 +19,13 @@ interface Props {
|
||||
onDeleteColumn: (column: any) => void
|
||||
}
|
||||
|
||||
const ColumnList: FC<Props> = ({
|
||||
const ColumnList = ({
|
||||
selectedTable,
|
||||
onSelectBack = () => {},
|
||||
onAddColumn = () => {},
|
||||
onEditColumn = () => {},
|
||||
onDeleteColumn = () => {},
|
||||
}) => {
|
||||
onSelectBack = noop,
|
||||
onAddColumn = noop,
|
||||
onEditColumn = noop,
|
||||
onDeleteColumn = noop,
|
||||
}: ColumnListProps) => {
|
||||
const { meta } = useStore()
|
||||
const [filterString, setFilterString] = useState<string>('')
|
||||
const columns =
|
||||
@@ -31,7 +33,7 @@ const ColumnList: FC<Props> = ({
|
||||
? selectedTable.columns
|
||||
: selectedTable.columns?.filter((column: any) => column.name.includes(filterString))) ?? []
|
||||
|
||||
const isLocked = meta.excludedSchemas.includes(selectedTable.schema ?? '')
|
||||
const isLocked = EXCLUDED_SCHEMAS.includes(selectedTable.schema ?? '')
|
||||
const canUpdateColumns = checkPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'columns')
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FC, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import {
|
||||
Button,
|
||||
@@ -12,45 +12,50 @@ import {
|
||||
IconLock,
|
||||
IconCheck,
|
||||
} from 'ui'
|
||||
import { partition } from 'lodash'
|
||||
import { noop, partition } from 'lodash'
|
||||
import * as Tooltip from '@radix-ui/react-tooltip'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import type { PostgresTable, PostgresSchema } from '@supabase/postgres-meta'
|
||||
|
||||
import { useStore, checkPermissions } from 'hooks'
|
||||
import { EXCLUDED_SCHEMAS } from 'lib/constants/schemas'
|
||||
import { useTableEditorStateSnapshot } from 'state/table-editor'
|
||||
import Table from 'components/to-be-cleaned/Table'
|
||||
import NoSearchResults from 'components/to-be-cleaned/NoSearchResults'
|
||||
import type { PostgresTable, PostgresSchema } from '@supabase/postgres-meta'
|
||||
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
|
||||
import { useSchemasQuery } from 'data/database/schemas-query'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
|
||||
interface Props {
|
||||
selectedSchema: string
|
||||
onSelectSchema: (schema: string) => void
|
||||
export interface TableListProps {
|
||||
onAddTable: () => void
|
||||
onEditTable: (table: any) => void
|
||||
onDeleteTable: (table: any) => void
|
||||
onOpenTable: (table: any) => void
|
||||
}
|
||||
|
||||
const TableList: FC<Props> = ({
|
||||
selectedSchema,
|
||||
onSelectSchema = () => {},
|
||||
onAddTable = () => {},
|
||||
onEditTable = () => {},
|
||||
onDeleteTable = () => {},
|
||||
onOpenTable = () => {},
|
||||
}) => {
|
||||
const TableList = ({
|
||||
onAddTable = noop,
|
||||
onEditTable = noop,
|
||||
onDeleteTable = noop,
|
||||
onOpenTable = noop,
|
||||
}: TableListProps) => {
|
||||
const { project } = useProjectContext()
|
||||
const snap = useTableEditorStateSnapshot()
|
||||
const { meta } = useStore()
|
||||
const { isLoading } = meta.tables
|
||||
const [filterString, setFilterString] = useState<string>('')
|
||||
const canUpdateTables = checkPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'tables')
|
||||
|
||||
const schemas: PostgresSchema[] = meta.schemas.list()
|
||||
const { data: schemas } = useSchemasQuery({
|
||||
projectRef: project?.ref,
|
||||
connectionString: project?.connectionString,
|
||||
})
|
||||
const [protectedSchemas, openSchemas] = partition(schemas, (schema) =>
|
||||
meta.excludedSchemas.includes(schema?.name ?? '')
|
||||
EXCLUDED_SCHEMAS.includes(schema?.name ?? '')
|
||||
)
|
||||
|
||||
const allTables: PostgresTable[] = meta.tables.list(
|
||||
(table: PostgresTable) => table.schema === selectedSchema
|
||||
(table: PostgresTable) => table.schema === snap.selectedSchemaName
|
||||
)
|
||||
const tables =
|
||||
filterString.length === 0
|
||||
@@ -62,8 +67,8 @@ const TableList: FC<Props> = ({
|
||||
const realtimePublication = publications.find(
|
||||
(publication) => publication.name === 'supabase_realtime'
|
||||
)
|
||||
// @ts-ignore
|
||||
const schema = schemas.find((schema) => schema.name === selectedSchema)
|
||||
|
||||
const schema = schemas?.find((schema) => schema.name === snap.selectedSchemaName)
|
||||
const isLocked = protectedSchemas.some((s) => s.id === schema?.id)
|
||||
|
||||
return (
|
||||
@@ -73,8 +78,8 @@ const TableList: FC<Props> = ({
|
||||
<div className="w-[230px]">
|
||||
<Listbox
|
||||
size="small"
|
||||
value={selectedSchema}
|
||||
onChange={onSelectSchema}
|
||||
value={snap.selectedSchemaName}
|
||||
onChange={snap.setSelectedSchemaName}
|
||||
icon={isLocked && <IconLock size={14} strokeWidth={2} />}
|
||||
>
|
||||
<Listbox.Option disabled key="normal-schemas" value="normal-schemas" label="Schemas">
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Form, IconDatabase, Input, Listbox, SidePanel, Modal, IconPlus } from 'ui'
|
||||
import { useStore } from 'hooks'
|
||||
import { useSchemasQuery } from 'data/database/schemas-query'
|
||||
import ActionBar from 'components/interfaces/TableGridEditor/SidePanelEditor/ActionBar'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import { Table, TableOption } from './Wrappers.types'
|
||||
import { makeValidateRequired } from './Wrappers.utils'
|
||||
import ActionBar from 'components/interfaces/TableGridEditor/SidePanelEditor/ActionBar'
|
||||
|
||||
export type WrapperTableEditorProps = {
|
||||
visible: boolean
|
||||
@@ -126,8 +127,11 @@ const TableForm = ({
|
||||
onSubmit: OnSubmitFn
|
||||
initialData: any
|
||||
}) => {
|
||||
const { meta } = useStore()
|
||||
const schemas = meta.schemas.list()
|
||||
const { project } = useProjectContext()
|
||||
const { data: schemas } = useSchemasQuery({
|
||||
projectRef: project?.ref,
|
||||
connectionString: project?.connectionString,
|
||||
})
|
||||
|
||||
const requiredOptions =
|
||||
table.options.filter((option) => option.editable && option.required && !option.defaultValue) ??
|
||||
@@ -173,8 +177,8 @@ const TableForm = ({
|
||||
Create a new schema
|
||||
</Listbox.Option>
|
||||
<Modal.Separator />
|
||||
{/* @ts-ignore */}
|
||||
{schemas.map((schema: PostgresSchema) => {
|
||||
|
||||
{schemas?.map((schema) => {
|
||||
return (
|
||||
<Listbox.Option
|
||||
key={schema.id}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useContext, FC, useEffect } from 'react'
|
||||
import { useEffect } from 'react'
|
||||
import { indexOf } from 'lodash'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { Input, Form, IconAlertCircle, InputNumber } from 'ui'
|
||||
@@ -8,8 +8,8 @@ import { checkPermissions, useStore } from 'hooks'
|
||||
import { useParams } from 'common/hooks'
|
||||
import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query'
|
||||
import { useProjectPostgrestConfigUpdateMutation } from 'data/config/project-postgrest-config-update-mutation'
|
||||
import { useSchemasQuery } from 'data/database/schemas-query'
|
||||
import MultiSelect from 'components/ui/MultiSelect'
|
||||
import { PageContext } from 'pages/project/[ref]/settings/api'
|
||||
|
||||
import {
|
||||
FormActions,
|
||||
@@ -18,19 +18,22 @@ import {
|
||||
FormSectionContent,
|
||||
FormSectionLabel,
|
||||
} from 'components/ui/Forms'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
|
||||
interface Props {}
|
||||
|
||||
const PostgrestConfig: FC<Props> = ({}) => {
|
||||
const PageState: any = useContext(PageContext)
|
||||
const PostgrestConfig = () => {
|
||||
const { project } = useProjectContext()
|
||||
const { ui } = useStore()
|
||||
const { meta } = PageState
|
||||
|
||||
const { ref: projectRef } = useParams()
|
||||
|
||||
const formId = 'project-postgres-config'
|
||||
const { data: config, isError } = useProjectPostgrestConfigQuery({ projectRef })
|
||||
|
||||
const { data: schemas } = useSchemasQuery({
|
||||
projectRef: project?.ref,
|
||||
connectionString: project?.connectionString,
|
||||
})
|
||||
|
||||
const initialValues = {
|
||||
db_schema: '',
|
||||
max_rows: '',
|
||||
@@ -68,15 +71,12 @@ const PostgrestConfig: FC<Props> = ({}) => {
|
||||
const permanentSchema = ['public', 'storage']
|
||||
const hiddenSchema = ['auth', 'pgbouncer', 'hooks', 'extensions']
|
||||
const schema =
|
||||
meta.schemas
|
||||
.list(
|
||||
(x: any) => {
|
||||
const find = indexOf(hiddenSchema, x.name)
|
||||
if (find < 0) return x
|
||||
},
|
||||
{ allSchemas: true }
|
||||
)
|
||||
.map((x: any) => {
|
||||
schemas
|
||||
?.filter((x) => {
|
||||
const find = indexOf(hiddenSchema, x.name)
|
||||
if (find < 0) return x
|
||||
})
|
||||
.map((x) => {
|
||||
return {
|
||||
id: x.id,
|
||||
value: x.name,
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import { FC } from 'react'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { checkPermissions, useLocalStorage, useStore } from 'hooks'
|
||||
import { checkPermissions, useLocalStorage } from 'hooks'
|
||||
import { EXCLUDED_SCHEMAS } from 'lib/constants/schemas'
|
||||
import { useTableEditorStateSnapshot } from 'state/table-editor'
|
||||
import { useEntityTypesQuery } from 'data/entity-types/entity-types-infinite-query'
|
||||
import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
|
||||
interface Props {
|
||||
selectedSchema: string
|
||||
export interface EmptyStateProps {
|
||||
onAddTable: () => void
|
||||
}
|
||||
|
||||
const EmptyState: FC<Props> = ({ selectedSchema, onAddTable }) => {
|
||||
const { meta } = useStore()
|
||||
const isProtectedSchema = meta.excludedSchemas.includes(selectedSchema)
|
||||
const EmptyState = ({ onAddTable }: EmptyStateProps) => {
|
||||
const snap = useTableEditorStateSnapshot()
|
||||
const isProtectedSchema = EXCLUDED_SCHEMAS.includes(snap.selectedSchemaName)
|
||||
const canCreateTables =
|
||||
!isProtectedSchema && checkPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'tables')
|
||||
|
||||
@@ -27,7 +26,7 @@ const EmptyState: FC<Props> = ({ selectedSchema, onAddTable }) => {
|
||||
{
|
||||
projectRef: project?.ref,
|
||||
connectionString: project?.connectionString,
|
||||
schema: selectedSchema,
|
||||
schema: snap.selectedSchemaName,
|
||||
sort,
|
||||
},
|
||||
{
|
||||
@@ -65,4 +64,4 @@ const EmptyState: FC<Props> = ({ selectedSchema, onAddTable }) => {
|
||||
)
|
||||
}
|
||||
|
||||
export default observer(EmptyState)
|
||||
export default EmptyState
|
||||
+2
-4
@@ -12,6 +12,7 @@ import type {
|
||||
|
||||
import { useFlag, useStore } from 'hooks'
|
||||
import { useParams } from 'common/hooks'
|
||||
import { EXCLUDED_SCHEMAS } from 'lib/constants/schemas'
|
||||
import ActionBar from '../ActionBar'
|
||||
import HeaderTitle from './HeaderTitle'
|
||||
import ColumnType from './ColumnType'
|
||||
@@ -35,7 +36,6 @@ import {
|
||||
} from '../SidePanelEditor.types'
|
||||
import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui/Forms'
|
||||
import { EncryptionKeySelector } from 'components/interfaces/Settings/Vault'
|
||||
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import { useForeignKeyConstraintsQuery } from 'data/database/foreign-key-constraints-query'
|
||||
|
||||
@@ -84,9 +84,7 @@ const ColumnEditor = ({
|
||||
const foreignKeyMeta = data || []
|
||||
|
||||
const keys = vault.listKeys()
|
||||
const enumTypes = meta.types.list(
|
||||
(type: PostgresType) => !meta.excludedSchemas.includes(type.schema)
|
||||
)
|
||||
const enumTypes = meta.types.list((type: PostgresType) => !EXCLUDED_SCHEMAS.includes(type.schema))
|
||||
|
||||
const [pgsodiumExtension] = meta.extensions.list(
|
||||
(ext: PostgresExtension) => ext.name.toLowerCase() === 'pgsodium'
|
||||
|
||||
+19
-8
@@ -1,19 +1,21 @@
|
||||
import React, { FC, useEffect, useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { get, find, isEmpty, sortBy } from 'lodash'
|
||||
import { Dictionary } from 'components/grid'
|
||||
import { SidePanel, Input, Listbox, IconHelpCircle, IconDatabase, Toggle } from 'ui'
|
||||
import { SidePanel, Input, Listbox, IconHelpCircle, IconDatabase } from 'ui'
|
||||
import type { PostgresTable, PostgresColumn, PostgresSchema } from '@supabase/postgres-meta'
|
||||
|
||||
import { useStore } from 'hooks'
|
||||
import { FOREIGN_KEY_DELETION_ACTION } from 'data/database/database-query-constants'
|
||||
import { useSchemasQuery } from 'data/database/schemas-query'
|
||||
import InformationBox from 'components/ui/InformationBox'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import ActionBar from '../ActionBar'
|
||||
import { ForeignKey } from './ForeignKeySelector.types'
|
||||
import { ColumnField } from '../SidePanelEditor.types'
|
||||
import InformationBox from 'components/ui/InformationBox'
|
||||
import { FOREIGN_KEY_DELETION_ACTION } from 'data/database/database-query-constants'
|
||||
import { FOREIGN_KEY_DELETION_OPTIONS } from './ForeignKeySelector.constants'
|
||||
import { generateDeletionActionDescription } from './ForeignKeySelector.utils'
|
||||
|
||||
interface Props {
|
||||
export interface ForeignKeySelectorProps {
|
||||
column: ColumnField
|
||||
metadata?: any
|
||||
visible: boolean
|
||||
@@ -23,7 +25,13 @@ interface Props {
|
||||
) => void
|
||||
}
|
||||
|
||||
const ForeignKeySelector: FC<Props> = ({ column, visible = false, closePanel, saveChanges }) => {
|
||||
const ForeignKeySelector = ({
|
||||
column,
|
||||
visible = false,
|
||||
closePanel,
|
||||
saveChanges,
|
||||
}: ForeignKeySelectorProps) => {
|
||||
const { project } = useProjectContext()
|
||||
const { meta } = useStore()
|
||||
const [errors, setErrors] = useState<any>({})
|
||||
const [selectedForeignKey, setSelectedForeignKey] = useState<ForeignKey>({
|
||||
@@ -33,7 +41,10 @@ const ForeignKeySelector: FC<Props> = ({ column, visible = false, closePanel, sa
|
||||
deletionAction: FOREIGN_KEY_DELETION_ACTION.NO_ACTION,
|
||||
})
|
||||
|
||||
const schemas = meta.schemas.list()
|
||||
const { data: schemas } = useSchemasQuery({
|
||||
projectRef: project?.ref,
|
||||
connectionString: project?.connectionString,
|
||||
})
|
||||
const tables = meta.tables.list(
|
||||
(table: PostgresTable) => table.schema === selectedForeignKey.schema
|
||||
)
|
||||
@@ -190,7 +201,7 @@ const ForeignKeySelector: FC<Props> = ({ column, visible = false, closePanel, sa
|
||||
error={errors.schema}
|
||||
onChange={(value: string) => updateSelectedSchema(value)}
|
||||
>
|
||||
{schemas.map((schema: PostgresSchema) => {
|
||||
{schemas?.map((schema: PostgresSchema) => {
|
||||
return (
|
||||
<Listbox.Option
|
||||
key={schema.id}
|
||||
|
||||
@@ -27,7 +27,6 @@ import ForeignRowSelector, {
|
||||
} from './RowEditor/ForeignRowSelector/ForeignRowSelector'
|
||||
|
||||
export interface SidePanelEditorProps {
|
||||
selectedSchema: string
|
||||
selectedTable?: PostgresTable
|
||||
selectedRowToEdit?: Dictionary<any>
|
||||
selectedColumnToEdit?: PostgresColumn
|
||||
@@ -51,7 +50,6 @@ export interface SidePanelEditorProps {
|
||||
}
|
||||
|
||||
const SidePanelEditor = ({
|
||||
selectedSchema,
|
||||
selectedTable,
|
||||
selectedRowToEdit,
|
||||
selectedColumnToEdit,
|
||||
@@ -551,7 +549,6 @@ const SidePanelEditor = ({
|
||||
)}
|
||||
<TableEditor
|
||||
table={selectedTableToEdit}
|
||||
selectedSchema={selectedSchema}
|
||||
isDuplicating={isDuplicating}
|
||||
visible={sidePanelKey === 'table'}
|
||||
closePanel={onClosePanel}
|
||||
|
||||
+24
-20
@@ -1,10 +1,15 @@
|
||||
import Link from 'next/link'
|
||||
import { FC, useEffect, useState } from 'react'
|
||||
import { isUndefined, isEmpty } from 'lodash'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { isUndefined, isEmpty, noop } from 'lodash'
|
||||
import { Badge, Checkbox, SidePanel, Input, Alert, IconBookOpen, Button, Modal } from 'ui'
|
||||
import type { PostgresTable, PostgresType } from '@supabase/postgres-meta'
|
||||
|
||||
import { useStore } from 'hooks'
|
||||
import { EXCLUDED_SCHEMAS } from 'lib/constants/schemas'
|
||||
import { useForeignKeyConstraintsQuery } from 'data/database/foreign-key-constraints-query'
|
||||
import { useTableEditorStateSnapshot } from 'state/table-editor'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import ConfirmationModal from 'components/ui/ConfirmationModal'
|
||||
import ActionBar from '../ActionBar'
|
||||
import HeaderTitle from './HeaderTitle'
|
||||
import ColumnManagement from './ColumnManagement'
|
||||
@@ -18,14 +23,10 @@ import {
|
||||
generateTableFieldFromPostgresTable,
|
||||
formatImportedContentToColumnFields,
|
||||
} from './TableEditor.utils'
|
||||
import { useForeignKeyConstraintsQuery } from 'data/database/foreign-key-constraints-query'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import ConfirmationModal from 'components/ui/ConfirmationModal'
|
||||
import RLSDisableModalContent from './RLSDisableModal'
|
||||
|
||||
interface Props {
|
||||
export interface TableEditorProps {
|
||||
table?: PostgresTable
|
||||
selectedSchema: string
|
||||
isDuplicating: boolean
|
||||
visible: boolean
|
||||
closePanel: () => void
|
||||
@@ -45,22 +46,20 @@ interface Props {
|
||||
updateEditorDirty: () => void
|
||||
}
|
||||
|
||||
const TableEditor: FC<Props> = ({
|
||||
const TableEditor = ({
|
||||
table,
|
||||
selectedSchema,
|
||||
isDuplicating,
|
||||
visible = false,
|
||||
closePanel = () => {},
|
||||
saveChanges = () => {},
|
||||
updateEditorDirty = () => {},
|
||||
}) => {
|
||||
closePanel = noop,
|
||||
saveChanges = noop,
|
||||
updateEditorDirty = noop,
|
||||
}: TableEditorProps) => {
|
||||
const snap = useTableEditorStateSnapshot()
|
||||
const { ui, meta } = useStore()
|
||||
const { project } = useProjectContext()
|
||||
const isNewRecord = isUndefined(table)
|
||||
|
||||
const enumTypes = meta.types.list(
|
||||
(type: PostgresType) => !meta.excludedSchemas.includes(type.schema)
|
||||
)
|
||||
const enumTypes = meta.types.list((type: PostgresType) => !EXCLUDED_SCHEMAS.includes(type.schema))
|
||||
|
||||
const publications = meta.publications.list()
|
||||
const realtimePublication = publications.find(
|
||||
@@ -135,7 +134,7 @@ const TableEditor: FC<Props> = ({
|
||||
if (isEmpty(errors)) {
|
||||
const payload: CreateTablePayload | UpdateTablePayload = {
|
||||
name: tableFields.name,
|
||||
schema: selectedSchema,
|
||||
schema: snap.selectedSchemaName,
|
||||
comment: tableFields.comment,
|
||||
...(!isNewRecord && { rls_enabled: tableFields.isRLSEnabled }),
|
||||
}
|
||||
@@ -170,8 +169,13 @@ const TableEditor: FC<Props> = ({
|
||||
size="large"
|
||||
key="TableEditor"
|
||||
visible={visible}
|
||||
// @ts-ignore
|
||||
header={<HeaderTitle schema={selectedSchema} table={table} isDuplicating={isDuplicating} />}
|
||||
header={
|
||||
<HeaderTitle
|
||||
schema={snap.selectedSchemaName}
|
||||
table={table as any}
|
||||
isDuplicating={isDuplicating}
|
||||
/>
|
||||
}
|
||||
className={`transition-all duration-100 ease-in ${isImportingSpreadsheet ? ' mr-32' : ''}`}
|
||||
onCancel={closePanel}
|
||||
onConfirm={() => (resolve: () => void) => onSaveChanges(resolve)}
|
||||
@@ -297,7 +301,7 @@ const TableEditor: FC<Props> = ({
|
||||
<div className="space-y-10 py-6">
|
||||
{!isDuplicating && (
|
||||
<ColumnManagement
|
||||
table={{ name: tableFields.name, schema: selectedSchema }}
|
||||
table={{ name: tableFields.name, schema: snap.selectedSchemaName }}
|
||||
columns={tableFields?.columns}
|
||||
enumTypes={enumTypes}
|
||||
isNewRecord={isNewRecord}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { QueryKey, useQueryClient } from '@tanstack/react-query'
|
||||
import { SchemaView } from 'types'
|
||||
import { checkPermissions, useFlag, useStore, useUrlState } from 'hooks'
|
||||
import useEntityType from 'hooks/misc/useEntityType'
|
||||
import { EXCLUDED_SCHEMAS } from 'lib/constants/schemas'
|
||||
import { useParams } from 'common/hooks'
|
||||
import GridHeaderActions from './GridHeaderActions'
|
||||
import NotFoundState from './NotFoundState'
|
||||
@@ -23,25 +24,25 @@ import {
|
||||
import { sqlKeys } from 'data/sql/keys'
|
||||
import { useProjectJsonSchemaQuery } from 'data/docs/project-json-schema-query'
|
||||
import { useTableRowUpdateMutation } from 'data/table-rows/table-row-update-mutation'
|
||||
import { JsonEditValue } from './SidePanelEditor/RowEditor/RowEditor.types'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import { ENTITY_TYPE } from 'data/entity-types/entity-type-constants'
|
||||
import {
|
||||
ForeignKeyConstraint,
|
||||
useForeignKeyConstraintsQuery,
|
||||
} from 'data/database/foreign-key-constraints-query'
|
||||
import { FOREIGN_KEY_DELETION_ACTION } from 'data/database/database-query-constants'
|
||||
import { ForeignRowSelectorProps } from './SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector'
|
||||
import { useTableEditorStateSnapshot } from 'state/table-editor'
|
||||
import TwoOptionToggle from 'components/ui/TwoOptionToggle'
|
||||
import { ERROR_PRIMARY_KEY_NOTFOUND } from 'components/grid/constants'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import { JsonEditValue } from './SidePanelEditor/RowEditor/RowEditor.types'
|
||||
import { ForeignRowSelectorProps } from './SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector'
|
||||
import TableDefinition from './TableDefinition'
|
||||
import APIDocumentationPanel from './APIDocumentationPanel'
|
||||
import { ERROR_PRIMARY_KEY_NOTFOUND } from 'components/grid/constants'
|
||||
|
||||
export interface TableGridEditorProps {
|
||||
/** Theme for the editor */
|
||||
theme?: 'dark' | 'light'
|
||||
|
||||
selectedSchema?: string
|
||||
selectedTable: any // PostgresTable | SchemaView
|
||||
|
||||
/** Determines what side panel editor to show */
|
||||
@@ -76,8 +77,6 @@ export interface TableGridEditorProps {
|
||||
|
||||
const TableGridEditor = ({
|
||||
theme = 'dark',
|
||||
|
||||
selectedSchema,
|
||||
selectedTable,
|
||||
sidePanelKey,
|
||||
isDuplicating,
|
||||
@@ -97,12 +96,13 @@ const TableGridEditor = ({
|
||||
onClosePanel = noop,
|
||||
onImportData = noop,
|
||||
}: TableGridEditorProps) => {
|
||||
const { project } = useProjectContext()
|
||||
const snap = useTableEditorStateSnapshot()
|
||||
const { meta, ui, vault } = useStore()
|
||||
const router = useRouter()
|
||||
const { ref: projectRef, id } = useParams()
|
||||
const gridRef = useRef<SupabaseGridRef>(null)
|
||||
|
||||
const { project } = useProjectContext()
|
||||
const isVaultEnabled = useFlag('vaultExtension')
|
||||
const [encryptedColumns, setEncryptedColumns] = useState([])
|
||||
const [apiPreviewPanelOpen, setApiPreviewPanelOpen] = useState(false)
|
||||
@@ -213,7 +213,7 @@ const TableGridEditor = ({
|
||||
entityType?.type === ENTITY_TYPE.VIEW || entityType?.type === ENTITY_TYPE.MATERIALIZED_VIEW
|
||||
const isTableSelected = entityType?.type === ENTITY_TYPE.TABLE
|
||||
const isForeignTableSelected = entityType?.type === ENTITY_TYPE.FOREIGN_TABLE
|
||||
const isLocked = meta.excludedSchemas.includes(entityType?.schema ?? '')
|
||||
const isLocked = EXCLUDED_SCHEMAS.includes(entityType?.schema ?? '')
|
||||
const canUpdateTables = checkPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'tables')
|
||||
const canEditViaTableEditor = isTableSelected && !isLocked
|
||||
|
||||
@@ -411,9 +411,8 @@ const TableGridEditor = ({
|
||||
{(isViewSelected || isTableSelected) && <TableDefinition id={selectedTable?.id} />}
|
||||
</SupabaseGrid>
|
||||
|
||||
{!isUndefined(selectedSchema) && (
|
||||
{!isUndefined(snap.selectedSchemaName) && (
|
||||
<SidePanelEditor
|
||||
selectedSchema={selectedSchema}
|
||||
isDuplicating={isDuplicating}
|
||||
selectedTable={selectedTable as PostgresTable}
|
||||
selectedRowToEdit={selectedRowToEdit}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FC, ReactNode, useState, useEffect } from 'react'
|
||||
import { useState, useEffect, PropsWithChildren } from 'react'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
@@ -8,12 +8,11 @@ import Error from 'components/ui/Error'
|
||||
import ProductMenu from 'components/ui/ProductMenu'
|
||||
import { generateAuthMenu } from './AuthLayout.utils'
|
||||
|
||||
interface Props {
|
||||
export interface AuthLayoutProps {
|
||||
title?: string
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
const AuthLayout: FC<Props> = ({ title, children }) => {
|
||||
const AuthLayout = ({ title, children }: PropsWithChildren<AuthLayoutProps>) => {
|
||||
const { ui, meta } = useStore()
|
||||
const { isInitialized, isLoading, error } = meta.tables
|
||||
const projectRef = ui.selectedProject?.ref ?? 'default'
|
||||
@@ -28,7 +27,6 @@ const AuthLayout: FC<Props> = ({ title, children }) => {
|
||||
meta.policies.load()
|
||||
meta.tables.load()
|
||||
meta.roles.load()
|
||||
meta.schemas.load()
|
||||
}
|
||||
}, [ui.selectedProject?.ref])
|
||||
|
||||
|
||||
@@ -16,8 +16,7 @@ interface Props {
|
||||
|
||||
const DatabaseLayout: FC<Props> = ({ title, children }) => {
|
||||
const { meta, ui, vault, backups } = useStore()
|
||||
const { isLoading } = meta.schemas
|
||||
const { isInitialized, error } = meta.tables
|
||||
const { error } = meta.tables
|
||||
const project = ui.selectedProject
|
||||
|
||||
const router = useRouter()
|
||||
@@ -27,12 +26,8 @@ const DatabaseLayout: FC<Props> = ({ title, children }) => {
|
||||
const isVaultEnabled = vaultExtension !== undefined && vaultExtension.installed_version !== null
|
||||
const foreignDataWrappersEnabled = useFlag('foreignDataWrappers')
|
||||
|
||||
const [loaded, setLoaded] = useState<boolean>(isInitialized)
|
||||
|
||||
useEffect(() => {
|
||||
if (ui.selectedProject?.ref) {
|
||||
// Eventually should only load the required stores based on the pages
|
||||
meta.schemas.load()
|
||||
meta.tables.load()
|
||||
|
||||
meta.roles.load()
|
||||
@@ -52,14 +47,6 @@ const DatabaseLayout: FC<Props> = ({ title, children }) => {
|
||||
}
|
||||
}, [ui.selectedProject?.ref, isVaultEnabled])
|
||||
|
||||
// Optimization required: load logic should be at the page level
|
||||
// e.g backups page is waiting for meta.tables to load finish when it doesnt even need that data
|
||||
useEffect(() => {
|
||||
if (!isLoading && !loaded) {
|
||||
setLoaded(true)
|
||||
}
|
||||
}, [isLoading])
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<ProjectLayout>
|
||||
@@ -70,7 +57,7 @@ const DatabaseLayout: FC<Props> = ({ title, children }) => {
|
||||
|
||||
return (
|
||||
<ProjectLayout
|
||||
isLoading={!loaded}
|
||||
// isLoading={!loaded}
|
||||
product="Database"
|
||||
productMenu={
|
||||
<ProductMenu page={page} menu={generateDatabaseMenu(project, foreignDataWrappersEnabled)} />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useParams } from 'common/hooks'
|
||||
import { useStore } from 'hooks'
|
||||
import { PROJECT_STATUS } from 'lib/constants'
|
||||
import { TableEditorStateContextProvider } from 'state/table-editor'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { createContext, PropsWithChildren, useContext, useMemo } from 'react'
|
||||
import { Project } from 'types'
|
||||
@@ -43,7 +44,13 @@ export const ProjectContextProvider = observer(
|
||||
}
|
||||
}, [ui.selectedProject?.ref, projectRef])
|
||||
|
||||
return <ProjectContext.Provider value={value}>{children}</ProjectContext.Provider>
|
||||
return (
|
||||
<ProjectContext.Provider value={value}>
|
||||
<TableEditorStateContextProvider key={`table-editor-state-${projectRef}`}>
|
||||
{children}
|
||||
</TableEditorStateContextProvider>
|
||||
</ProjectContext.Provider>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { checkPermissions, useStore } from 'hooks'
|
||||
import { useParams } from 'common/hooks'
|
||||
import { Entity } from 'data/entity-types/entity-type-query'
|
||||
import { ENTITY_TYPE } from 'data/entity-types/entity-type-constants'
|
||||
import { useIsTableLoaded, useTableEditorStateSnapshot } from 'state/table-editor'
|
||||
import { useIsTableLoaded, useTableEditorGlobalStateSnapshot } from 'state/table-editor-global'
|
||||
import ProjectLayout from '../'
|
||||
import TableEditorMenu from './TableEditorMenu'
|
||||
import NoPermission from 'components/ui/NoPermission'
|
||||
@@ -18,8 +18,6 @@ import useLatest from 'hooks/misc/useLatest'
|
||||
import useTableRowsPrefetchWrapper from './TableEditorLayout.utils'
|
||||
|
||||
export interface TableEditorLayoutProps {
|
||||
selectedSchema?: string
|
||||
onSelectSchema: (schema: string) => void
|
||||
onAddTable: () => void
|
||||
onEditTable: (table: Entity) => void
|
||||
onDeleteTable: (table: Entity) => void
|
||||
@@ -27,8 +25,6 @@ export interface TableEditorLayoutProps {
|
||||
}
|
||||
|
||||
const TableEditorLayout = ({
|
||||
selectedSchema,
|
||||
onSelectSchema = noop,
|
||||
onAddTable = noop,
|
||||
onEditTable = noop,
|
||||
onDeleteTable = noop,
|
||||
@@ -40,7 +36,7 @@ const TableEditorLayout = ({
|
||||
const { ref, id: _id } = useParams()
|
||||
const id = _id ? Number(_id) : undefined
|
||||
|
||||
const snap = useTableEditorStateSnapshot()
|
||||
const snap = useTableEditorGlobalStateSnapshot()
|
||||
const canReadTables = checkPermissions(PermissionAction.TENANT_SQL_ADMIN_READ, 'tables')
|
||||
|
||||
const vaultExtension = meta.extensions.byId('supabase_vault')
|
||||
@@ -48,7 +44,6 @@ const TableEditorLayout = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (ui.selectedProject?.ref) {
|
||||
meta.schemas.load()
|
||||
meta.types.load()
|
||||
meta.policies.load()
|
||||
meta.publications.load()
|
||||
@@ -125,8 +120,6 @@ const TableEditorLayout = ({
|
||||
product="Table editor"
|
||||
productMenu={
|
||||
<TableEditorMenu
|
||||
selectedSchema={selectedSchema}
|
||||
onSelectSchema={onSelectSchema}
|
||||
onAddTable={onAddTable}
|
||||
onEditTable={onEditTable}
|
||||
onDeleteTable={onDeleteTable}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useMemo, useState } from 'react'
|
||||
import { noop, partition } from 'lodash'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import {
|
||||
Button,
|
||||
Dropdown,
|
||||
@@ -17,19 +16,19 @@ import {
|
||||
} from 'ui'
|
||||
import * as Tooltip from '@radix-ui/react-tooltip'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import type { PostgresSchema } from '@supabase/postgres-meta'
|
||||
|
||||
import { useParams } from 'common/hooks'
|
||||
import { checkPermissions, useStore, useLocalStorage } from 'hooks'
|
||||
import { useTableEditorStateSnapshot } from 'state/table-editor'
|
||||
import { checkPermissions, useLocalStorage } from 'hooks'
|
||||
import InfiniteList from 'components/ui/InfiniteList'
|
||||
import { useEntityTypesQuery } from 'data/entity-types/entity-types-infinite-query'
|
||||
import { Entity } from 'data/entity-types/entity-type-query'
|
||||
import { useSchemasQuery } from 'data/database/schemas-query'
|
||||
import { EXCLUDED_SCHEMAS } from 'lib/constants/schemas'
|
||||
import { useProjectContext } from '../ProjectLayout/ProjectContext'
|
||||
import InfiniteList from 'components/ui/InfiniteList'
|
||||
import EntityListItem from './EntityListItem'
|
||||
|
||||
export interface TableEditorMenuProps {
|
||||
selectedSchema?: string
|
||||
onSelectSchema: (schema: string) => void
|
||||
onAddTable: () => void
|
||||
onEditTable: (table: Entity) => void
|
||||
onDeleteTable: (table: Entity) => void
|
||||
@@ -37,15 +36,13 @@ export interface TableEditorMenuProps {
|
||||
}
|
||||
|
||||
const TableEditorMenu = ({
|
||||
selectedSchema,
|
||||
onSelectSchema = noop,
|
||||
onAddTable = noop,
|
||||
onEditTable = noop,
|
||||
onDeleteTable = noop,
|
||||
onDuplicateTable = noop,
|
||||
}: TableEditorMenuProps) => {
|
||||
const { meta } = useStore()
|
||||
const { id } = useParams()
|
||||
const snap = useTableEditorStateSnapshot()
|
||||
|
||||
const [searchText, setSearchText] = useState<string>('')
|
||||
const [sort, setSort] = useLocalStorage<'alphabetical' | 'grouped-alphabetical'>(
|
||||
@@ -67,7 +64,7 @@ const TableEditorMenu = ({
|
||||
{
|
||||
projectRef: project?.ref,
|
||||
connectionString: project?.connectionString,
|
||||
schema: selectedSchema,
|
||||
schema: snap.selectedSchemaName,
|
||||
search: searchText || undefined,
|
||||
sort,
|
||||
},
|
||||
@@ -82,19 +79,24 @@ const TableEditorMenu = ({
|
||||
[data?.pages]
|
||||
)
|
||||
|
||||
const schemas: PostgresSchema[] = meta.schemas.list()
|
||||
const {
|
||||
data: schemas,
|
||||
isLoading: isSchemasLoading,
|
||||
isSuccess: isSchemasSuccess,
|
||||
} = useSchemasQuery({
|
||||
projectRef: project?.ref,
|
||||
connectionString: project?.connectionString,
|
||||
})
|
||||
|
||||
const schema = schemas.find((schema) => schema.name === selectedSchema)
|
||||
const schema = schemas?.find((schema) => schema.name === snap.selectedSchemaName)
|
||||
const canCreateTables = checkPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'tables')
|
||||
|
||||
const isLoadingTableMetadata = id ? !meta.tables.byId(id) : true
|
||||
|
||||
const refreshTables = async () => {
|
||||
await refetch()
|
||||
}
|
||||
|
||||
const [protectedSchemas, openSchemas] = partition(schemas, (schema) =>
|
||||
meta.excludedSchemas.includes(schema?.name ?? '')
|
||||
EXCLUDED_SCHEMAS.includes(schema?.name ?? '')
|
||||
)
|
||||
const isLocked = protectedSchemas.some((s) => s.id === schema?.id)
|
||||
|
||||
@@ -105,18 +107,20 @@ const TableEditorMenu = ({
|
||||
>
|
||||
{/* Schema selection dropdown */}
|
||||
<div className="px-3 mx-4">
|
||||
{!meta.schemas.isInitialized ? (
|
||||
{isSchemasLoading && (
|
||||
<div className="flex h-[26px] items-center space-x-3 rounded border border-gray-500 px-3">
|
||||
<IconLoader className="animate-spin" size={12} />
|
||||
<span className="text-xs text-scale-900">Loading schemas...</span>
|
||||
</div>
|
||||
) : (
|
||||
)}
|
||||
|
||||
{isSchemasSuccess && (
|
||||
<Listbox
|
||||
size="tiny"
|
||||
value={selectedSchema}
|
||||
value={snap.selectedSchemaName}
|
||||
onChange={(name: string) => {
|
||||
setSearchText('')
|
||||
onSelectSchema(name)
|
||||
snap.setSelectedSchemaName(name)
|
||||
}}
|
||||
>
|
||||
<Listbox.Option
|
||||
@@ -348,7 +352,7 @@ const TableEditorMenu = ({
|
||||
onEditTable,
|
||||
onDeleteTable,
|
||||
onDuplicateTable,
|
||||
isLoadingTableMetadata,
|
||||
isLoadingTableMetadata: false, // TODO(alaister)
|
||||
}}
|
||||
getItemSize={() => 28}
|
||||
hasNextPage={hasNextPage}
|
||||
@@ -362,4 +366,4 @@ const TableEditorMenu = ({
|
||||
)
|
||||
}
|
||||
|
||||
export default observer(TableEditorMenu)
|
||||
export default TableEditorMenu
|
||||
@@ -0,0 +1,5 @@
|
||||
export const databaseKeys = {
|
||||
schemaList: (projectRef: string | undefined) => ['projects', projectRef, 'schemas'] as const,
|
||||
schema: (projectRef: string | undefined, id: string | undefined) =>
|
||||
['projects', projectRef, 'schemas', id] as const,
|
||||
}
|
||||
@@ -1,60 +1,50 @@
|
||||
import { useCallback } from 'react'
|
||||
import { UseQueryOptions } from '@tanstack/react-query'
|
||||
import { ExecuteSqlData, useExecuteSqlPrefetch, useExecuteSqlQuery } from '../sql/execute-sql-query'
|
||||
|
||||
export type Schema = {
|
||||
name: string
|
||||
}
|
||||
|
||||
export const getSchemasQuery = () => {
|
||||
const sql = /* SQL */ `
|
||||
SELECT nspname as name
|
||||
FROM pg_namespace
|
||||
WHERE
|
||||
nspname not in ('information_schema', 'pg_catalog', 'pg_toast')
|
||||
AND nspname not like 'pg_temp_%'
|
||||
AND nspname not like 'pg_toast_temp_%'
|
||||
AND has_schema_privilege(oid, 'CREATE, USAGE')
|
||||
ORDER BY nspname;
|
||||
`.trim()
|
||||
|
||||
return sql
|
||||
}
|
||||
import { PostgresSchema } from '@supabase/postgres-meta'
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||
import { get } from 'lib/common/fetch'
|
||||
import { API_URL } from 'lib/constants'
|
||||
import { databaseKeys } from './keys'
|
||||
|
||||
export type SchemasVariables = {
|
||||
projectRef?: string
|
||||
connectionString?: string
|
||||
}
|
||||
|
||||
export type SchemasData = { result: Schema[] }
|
||||
export type Schema = PostgresSchema
|
||||
|
||||
export type SchemasResponse = Schema[] | { error?: any }
|
||||
|
||||
export async function getSchemas(
|
||||
{ projectRef, connectionString }: SchemasVariables,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
if (!projectRef) {
|
||||
throw new Error('projectRef is required')
|
||||
}
|
||||
|
||||
let headers = new Headers()
|
||||
if (connectionString) headers.set('x-connection-encrypted', connectionString)
|
||||
|
||||
const response = (await get(`${API_URL}/pg-meta/${projectRef}/schemas`, {
|
||||
headers: Object.fromEntries(headers),
|
||||
signal,
|
||||
})) as SchemasResponse
|
||||
|
||||
if (!Array.isArray(response) && response.error) {
|
||||
throw response.error
|
||||
}
|
||||
|
||||
return response as PostgresSchema[]
|
||||
}
|
||||
|
||||
export type SchemasData = Awaited<ReturnType<typeof getSchemas>>
|
||||
export type SchemasError = unknown
|
||||
|
||||
export const useSchemasQuery = <TData extends SchemasData = SchemasData>(
|
||||
export const useSchemasQuery = <TData = SchemasData>(
|
||||
{ projectRef, connectionString }: SchemasVariables,
|
||||
options: UseQueryOptions<ExecuteSqlData, SchemasError, TData> = {}
|
||||
) => {
|
||||
return useExecuteSqlQuery(
|
||||
{
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql: getSchemasQuery(),
|
||||
queryKey: ['schemas'],
|
||||
},
|
||||
options
|
||||
{ enabled = true, ...options }: UseQueryOptions<SchemasData, SchemasError, TData> = {}
|
||||
) =>
|
||||
useQuery<SchemasData, SchemasError, TData>(
|
||||
databaseKeys.schemaList(projectRef),
|
||||
({ signal }) => getSchemas({ projectRef, connectionString }, signal),
|
||||
{ enabled: enabled && typeof projectRef !== 'undefined', ...options }
|
||||
)
|
||||
}
|
||||
|
||||
export const useSchemasPrefetch = () => {
|
||||
const prefetch = useExecuteSqlPrefetch()
|
||||
|
||||
return useCallback(
|
||||
({ projectRef, connectionString }: SchemasVariables) =>
|
||||
prefetch({
|
||||
projectRef,
|
||||
connectionString,
|
||||
sql: getSchemasQuery(),
|
||||
queryKey: ['schemas'],
|
||||
}),
|
||||
[prefetch]
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* A list of system schemas that users should not interact with
|
||||
*/
|
||||
export const EXCLUDED_SCHEMAS = [
|
||||
'auth',
|
||||
'extensions',
|
||||
'information_schema',
|
||||
'net',
|
||||
'pgsodium',
|
||||
'pgsodium_masks',
|
||||
'pgbouncer',
|
||||
'realtime',
|
||||
'storage',
|
||||
'supabase_functions',
|
||||
'vault',
|
||||
'graphql',
|
||||
'graphql_public',
|
||||
'pgtle',
|
||||
'supabase_migrations',
|
||||
]
|
||||
@@ -33,8 +33,6 @@ const GraphiQLPage: NextPageWithLayout = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (ui.selectedProject?.ref) {
|
||||
// Schemas may be needed when enabling the GraphQL extension
|
||||
meta.schemas.load()
|
||||
meta.extensions.load()
|
||||
}
|
||||
}, [ui.selectedProject?.ref])
|
||||
|
||||
@@ -11,6 +11,10 @@ import { useParams } from 'common/hooks'
|
||||
import { AuthLayout } from 'components/layouts'
|
||||
import { Policies } from 'components/interfaces/Auth/Policies'
|
||||
import NoPermission from 'components/ui/NoPermission'
|
||||
import { useSchemasQuery } from 'data/database/schemas-query'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import { useTableEditorStateSnapshot } from 'state/table-editor'
|
||||
import { EXCLUDED_SCHEMAS } from 'lib/constants/schemas'
|
||||
|
||||
/**
|
||||
* Filter tables by table name and policy name
|
||||
@@ -48,26 +52,31 @@ const onFilterTables = (
|
||||
}
|
||||
|
||||
const AuthPoliciesPage: NextPageWithLayout = () => {
|
||||
const { project } = useProjectContext()
|
||||
const snap = useTableEditorStateSnapshot()
|
||||
const { meta } = useStore()
|
||||
const { search } = useParams()
|
||||
const [selectedSchema, setSelectedSchema] = useState<string>('public')
|
||||
const [searchString, setSearchString] = useState<string>('')
|
||||
|
||||
useEffect(() => {
|
||||
if (search) setSearchString(search)
|
||||
}, [search])
|
||||
|
||||
const schemas = meta.schemas.list()
|
||||
const { data: schemas } = useSchemasQuery({
|
||||
projectRef: project?.ref,
|
||||
connectionString: project?.connectionString,
|
||||
})
|
||||
const [protectedSchemas, openSchemas] = partition(schemas, (schema) =>
|
||||
meta.excludedSchemas.includes(schema?.name ?? '')
|
||||
EXCLUDED_SCHEMAS.includes(schema?.name ?? '')
|
||||
)
|
||||
// @ts-ignore
|
||||
const schema = schemas.find((schema) => schema.name === selectedSchema)
|
||||
const schema = schemas?.find((schema) => schema.name === snap.selectedSchemaName)
|
||||
const isLocked = protectedSchemas.some((s) => s.id === schema?.id)
|
||||
|
||||
const policies = meta.policies.list()
|
||||
|
||||
const tables = meta.tables.list((table: { schema: string }) => table.schema === selectedSchema)
|
||||
const tables = meta.tables.list(
|
||||
(table: { schema: string }) => table.schema === snap.selectedSchemaName
|
||||
)
|
||||
const filteredTables = onFilterTables(tables, policies, searchString)
|
||||
|
||||
const canReadPolicies = checkPermissions(PermissionAction.TENANT_SQL_ADMIN_READ, 'policies')
|
||||
@@ -84,9 +93,9 @@ const AuthPoliciesPage: NextPageWithLayout = () => {
|
||||
<div className="w-[230px]">
|
||||
<Listbox
|
||||
size="small"
|
||||
value={selectedSchema}
|
||||
value={snap.selectedSchemaName}
|
||||
onChange={(schema: string) => {
|
||||
setSelectedSchema(schema)
|
||||
snap.setSelectedSchemaName(schema)
|
||||
setSearchString('')
|
||||
}}
|
||||
icon={isLocked && <IconLock size={14} strokeWidth={2} />}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import clsx from 'clsx'
|
||||
import { IconLoader } from 'ui'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
|
||||
import { NextPageWithLayout } from 'types'
|
||||
import { checkPermissions, useStore } from 'hooks'
|
||||
import { useParams } from 'common'
|
||||
import { API_URL } from 'lib/constants'
|
||||
import { post } from 'lib/common/fetch'
|
||||
import { DatabaseLayout } from 'components/layouts'
|
||||
@@ -15,20 +15,30 @@ import HooksList from 'components/interfaces/Database/Hooks/HooksList/HooksList'
|
||||
import DeleteHookModal from 'components/interfaces/Database/Hooks/DeleteHookModal'
|
||||
import EditHookPanel from 'components/interfaces/Database/Hooks/EditHookPanel'
|
||||
import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState'
|
||||
import { useSchemasQuery } from 'data/database/schemas-query'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
|
||||
const HooksPage: NextPageWithLayout = () => {
|
||||
const { project } = useProjectContext()
|
||||
const { meta, ui } = useStore()
|
||||
|
||||
const router = useRouter()
|
||||
const { ref } = router.query
|
||||
const schemas = meta.schemas.list()
|
||||
const { isLoading: isLoadingSchemas } = meta.schemas
|
||||
const { ref: projectRef } = useParams()
|
||||
|
||||
const {
|
||||
data: schemas,
|
||||
isLoading: isSchemasLoading,
|
||||
refetch,
|
||||
} = useSchemasQuery({
|
||||
projectRef: project?.ref,
|
||||
connectionString: project?.connectionString,
|
||||
})
|
||||
|
||||
const [selectedHook, setSelectedHook] = useState<any>()
|
||||
const [showCreateHookForm, setShowCreateHookForm] = useState<boolean>(false)
|
||||
const [showDeleteHookForm, setShowDeleteHookForm] = useState<boolean>(false)
|
||||
|
||||
const isHooksEnabled = schemas.some((schema: any) => schema.name === 'supabase_functions')
|
||||
const isHooksEnabled =
|
||||
schemas?.some((schema: any) => schema.name === 'supabase_functions') ?? false
|
||||
const canReadWebhooks = checkPermissions(PermissionAction.TENANT_SQL_ADMIN_READ, 'triggers')
|
||||
const canCreateWebhooks = checkPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'triggers')
|
||||
|
||||
@@ -37,14 +47,14 @@ const HooksPage: NextPageWithLayout = () => {
|
||||
}, [ui.selectedProject?.ref])
|
||||
|
||||
const enableHooksForProject = async () => {
|
||||
const res = await post(`${API_URL}/database/${ref}/hook-enable`, {})
|
||||
const res = await post(`${API_URL}/database/${projectRef}/hook-enable`, {})
|
||||
if (res.error) {
|
||||
ui.setNotification({
|
||||
category: 'error',
|
||||
message: `Failed to enable webhooks: ${res.error.message}`,
|
||||
})
|
||||
} else {
|
||||
meta.schemas.load()
|
||||
refetch()
|
||||
ui.setNotification({
|
||||
category: 'success',
|
||||
message: `Successfully enabled webhooks`,
|
||||
@@ -71,7 +81,7 @@ const HooksPage: NextPageWithLayout = () => {
|
||||
return <NoPermission isFullPage resourceText="view database webhooks" />
|
||||
}
|
||||
|
||||
if (isLoadingSchemas) {
|
||||
if (isSchemasLoading) {
|
||||
return (
|
||||
<div className="w-full h-full flex items-center justify-center space-x-2">
|
||||
<IconLoader className="animate-spin" size="tiny" strokeWidth={1.5} />
|
||||
|
||||
@@ -23,7 +23,6 @@ const DatabaseTables: NextPageWithLayout = () => {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const [isDeleting, setIsDeleting] = useState<boolean>(false)
|
||||
const [selectedSchema, setSelectedSchema] = useState('public')
|
||||
const [selectedTable, setSelectedTable] = useState<any>()
|
||||
const [sidePanelKey, setSidePanelKey] = useState<'column' | 'table'>()
|
||||
|
||||
@@ -147,8 +146,6 @@ const DatabaseTables: NextPageWithLayout = () => {
|
||||
<div className="p-4">
|
||||
{isUndefined(selectedTable) ? (
|
||||
<TableList
|
||||
selectedSchema={selectedSchema}
|
||||
onSelectSchema={setSelectedSchema}
|
||||
onAddTable={onAddTable}
|
||||
onEditTable={onEditTable}
|
||||
onDeleteTable={onDeleteTable}
|
||||
@@ -198,7 +195,6 @@ const DatabaseTables: NextPageWithLayout = () => {
|
||||
</ConfirmationModal>
|
||||
<SidePanelEditor
|
||||
sidePanelKey={sidePanelKey}
|
||||
selectedSchema={selectedSchema}
|
||||
selectedTable={selectedTable}
|
||||
onColumnSaved={onColumnUpdated}
|
||||
closePanel={onClosePanel}
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
} from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import { ForeignRowSelectorProps } from 'components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector'
|
||||
import { useTheme } from 'common'
|
||||
import { useTableEditorStateSnapshot } from 'state/table-editor'
|
||||
|
||||
const TableEditorPage: NextPageWithLayout = () => {
|
||||
const router = useRouter()
|
||||
@@ -32,11 +33,11 @@ const TableEditorPage: NextPageWithLayout = () => {
|
||||
const [_, setParams] = useUrlState({ arrayKeys: ['filter', 'sort'] })
|
||||
|
||||
const { project } = useProjectContext()
|
||||
const snap = useTableEditorStateSnapshot()
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const { meta, ui } = useStore()
|
||||
const [selectedSchema, setSelectedSchema] = useState<string>()
|
||||
|
||||
const [isDeleting, setIsDeleting] = useState<boolean>(false)
|
||||
const [isDuplicating, setIsDuplicating] = useState<boolean>(false)
|
||||
@@ -75,11 +76,11 @@ const TableEditorPage: NextPageWithLayout = () => {
|
||||
.find((table) => table.id === Number(id))
|
||||
: undefined
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedTable && 'schema' in selectedTable) {
|
||||
setSelectedSchema(selectedTable.schema)
|
||||
}
|
||||
}, [selectedTable?.name])
|
||||
// useEffect(() => {
|
||||
// if (selectedTable && 'schema' in selectedTable) {
|
||||
// setSelectedSchema(selectedTable.schema)
|
||||
// }
|
||||
// }, [selectedTable?.name])
|
||||
|
||||
const onAddRow = () => {
|
||||
setSidePanelKey('row')
|
||||
@@ -221,7 +222,7 @@ const TableEditorPage: NextPageWithLayout = () => {
|
||||
),
|
||||
])
|
||||
|
||||
if (selectedSchema) await meta.views.loadBySchema(selectedSchema)
|
||||
if (snap.selectedSchemaName) await meta.views.loadBySchema(snap.selectedSchemaName)
|
||||
} catch (error: any) {
|
||||
ui.setNotification({
|
||||
category: 'error',
|
||||
@@ -240,7 +241,9 @@ const TableEditorPage: NextPageWithLayout = () => {
|
||||
const response: any = await meta.tables.del(selectedTableToDelete.id, isDeleteWithCascade)
|
||||
if (response.error) throw response.error
|
||||
|
||||
const tables = meta.tables.list((table: PostgresTable) => table.schema === selectedSchema)
|
||||
const tables = meta.tables.list(
|
||||
(table: PostgresTable) => table.schema === snap.selectedSchemaName
|
||||
)
|
||||
|
||||
await queryClient.invalidateQueries(entityTypeKeys.list(projectRef))
|
||||
|
||||
@@ -254,7 +257,7 @@ const TableEditorPage: NextPageWithLayout = () => {
|
||||
category: 'success',
|
||||
message: `Successfully deleted table "${selectedTableToDelete.name}"`,
|
||||
})
|
||||
if (selectedSchema) await meta.views.loadBySchema(selectedSchema)
|
||||
if (snap.selectedSchemaName) await meta.views.loadBySchema(snap.selectedSchemaName)
|
||||
} catch (error: any) {
|
||||
ui.setNotification({
|
||||
error,
|
||||
@@ -269,15 +272,12 @@ const TableEditorPage: NextPageWithLayout = () => {
|
||||
|
||||
return (
|
||||
<TableEditorLayout
|
||||
selectedSchema={selectedSchema}
|
||||
onSelectSchema={setSelectedSchema}
|
||||
onAddTable={onAddTable}
|
||||
onEditTable={onEditTable}
|
||||
onDeleteTable={onDeleteTable}
|
||||
onDuplicateTable={onDuplicateTable}
|
||||
>
|
||||
<TableGridEditor
|
||||
selectedSchema={selectedSchema}
|
||||
selectedTable={selectedTable}
|
||||
sidePanelKey={sidePanelKey}
|
||||
isDuplicating={isDuplicating}
|
||||
|
||||
@@ -11,14 +11,15 @@ import { TableEditorLayout } from 'components/layouts'
|
||||
import { EmptyState, SidePanelEditor } from 'components/interfaces/TableGridEditor'
|
||||
import ConfirmationModal from 'components/ui/ConfirmationModal'
|
||||
import { ProjectContextFromParamsProvider } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import { useTableEditorStateSnapshot } from 'state/table-editor'
|
||||
|
||||
const TableEditorPage: NextPageWithLayout = () => {
|
||||
const snap = useTableEditorStateSnapshot()
|
||||
const { meta, ui } = useStore()
|
||||
const projectRef = ui.selectedProject?.ref
|
||||
const [sidePanelKey, setSidePanelKey] = useState<'row' | 'column' | 'table'>()
|
||||
const [isDeleting, setIsDeleting] = useState<boolean>(false)
|
||||
const [isDuplicating, setIsDuplicating] = useState<boolean>(false)
|
||||
const [selectedSchema, setSelectedSchema] = useState<string>('public')
|
||||
const [selectedTableToEdit, setSelectedTableToEdit] = useState<PostgresTable>()
|
||||
const [selectedTableToDelete, setSelectedTableToDelete] = useState<PostgresTable>()
|
||||
|
||||
@@ -63,7 +64,7 @@ const TableEditorPage: NextPageWithLayout = () => {
|
||||
category: 'success',
|
||||
message: `Successfully deleted ${selectedTableToDelete!.name}`,
|
||||
})
|
||||
await meta.views.loadBySchema(selectedSchema)
|
||||
await meta.views.loadBySchema(snap.selectedSchemaName)
|
||||
} catch (error: any) {
|
||||
ui.setNotification({
|
||||
category: 'error',
|
||||
@@ -74,14 +75,12 @@ const TableEditorPage: NextPageWithLayout = () => {
|
||||
|
||||
return (
|
||||
<TableEditorLayout
|
||||
selectedSchema={selectedSchema}
|
||||
onSelectSchema={setSelectedSchema}
|
||||
onAddTable={onAddTable}
|
||||
onEditTable={onEditTable}
|
||||
onDeleteTable={onDeleteTable}
|
||||
onDuplicateTable={onDuplicateTable}
|
||||
>
|
||||
<EmptyState selectedSchema={selectedSchema} onAddTable={onAddTable} />
|
||||
<EmptyState onAddTable={onAddTable} />
|
||||
{/* On this page it'll only handle tables */}
|
||||
<ConfirmationModal
|
||||
danger
|
||||
@@ -96,7 +95,6 @@ const TableEditorPage: NextPageWithLayout = () => {
|
||||
onSelectConfirm={onConfirmDeleteTable}
|
||||
/>
|
||||
<SidePanelEditor
|
||||
selectedSchema={selectedSchema}
|
||||
isDuplicating={isDuplicating}
|
||||
selectedTableToEdit={selectedTableToEdit}
|
||||
sidePanelKey={sidePanelKey}
|
||||
|
||||
@@ -28,10 +28,6 @@ const ApiSettings: NextPageWithLayout = () => {
|
||||
PageContext.project = project
|
||||
if (meta) PageState.meta = meta
|
||||
|
||||
useEffect(() => {
|
||||
if (project?.ref) meta.schemas.load()
|
||||
}, [project?.ref])
|
||||
|
||||
return (
|
||||
<PageContext.Provider value={PageState}>
|
||||
<div className="flex flex-col gap-8 px-5 py-6 mx-auto 1xl:px-28 lg:px-16 xl:px-24 2xl:px-32">
|
||||
|
||||
@@ -65,7 +65,7 @@ const SqlEditor: NextPageWithLayout = () => {
|
||||
pgInfoRef.current = {}
|
||||
}
|
||||
pgInfoRef.current.tableColumns = tableColumns?.result
|
||||
pgInfoRef.current.schemas = schemas?.result
|
||||
pgInfoRef.current.schemas = schemas
|
||||
pgInfoRef.current.keywords = keywords?.result
|
||||
pgInfoRef.current.functions = functions?.result
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { proxy, snapshot, useSnapshot } from 'valtio'
|
||||
import { proxySet } from 'valtio/utils'
|
||||
|
||||
export const tableEditorGlobalState = proxy({
|
||||
projectRef: undefined as undefined | string,
|
||||
loadedIds: proxySet<number>([]),
|
||||
addLoadedId: (ref: string, id: number) => {
|
||||
if (ref !== tableEditorGlobalState.projectRef) {
|
||||
tableEditorGlobalState.loadedIds.clear()
|
||||
tableEditorGlobalState.projectRef = ref
|
||||
}
|
||||
|
||||
tableEditorGlobalState.loadedIds.add(id)
|
||||
},
|
||||
})
|
||||
|
||||
export const getTableEditorGlobalStateSnapshot = () => snapshot(tableEditorGlobalState)
|
||||
|
||||
export const useTableEditorGlobalStateSnapshot = (options?: Parameters<typeof useSnapshot>[1]) =>
|
||||
useSnapshot(tableEditorGlobalState, options)
|
||||
|
||||
export const useIsTableLoaded = (ref?: string, id?: number) => {
|
||||
const snap = useTableEditorGlobalStateSnapshot()
|
||||
|
||||
if (!ref || !id) return false
|
||||
if (ref !== snap.projectRef) return false
|
||||
return snap.loadedIds.has(id)
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { proxy, snapshot, useSnapshot } from 'valtio'
|
||||
import { proxySet } from 'valtio/utils'
|
||||
|
||||
export const tableEditorState = proxy({
|
||||
projectRef: undefined as undefined | string,
|
||||
loadedIds: proxySet<number>([]),
|
||||
addLoadedId: (ref: string, id: number) => {
|
||||
if (ref !== tableEditorState.projectRef) {
|
||||
tableEditorState.loadedIds.clear()
|
||||
tableEditorState.projectRef = ref
|
||||
}
|
||||
|
||||
tableEditorState.loadedIds.add(id)
|
||||
},
|
||||
})
|
||||
|
||||
export const getTableEditorStateSnapshot = () => snapshot(tableEditorState)
|
||||
|
||||
export const useTableEditorStateSnapshot = (options?: Parameters<typeof useSnapshot>[1]) =>
|
||||
useSnapshot(tableEditorState, options)
|
||||
|
||||
export const useIsTableLoaded = (ref?: string, id?: number) => {
|
||||
const snap = useTableEditorStateSnapshot()
|
||||
|
||||
if (!ref || !id) return false
|
||||
if (ref !== snap.projectRef) return false
|
||||
return snap.loadedIds.has(id)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { PropsWithChildren, createContext, useContext, useRef } from 'react'
|
||||
import { proxy, useSnapshot } from 'valtio'
|
||||
|
||||
export const createTableEditorState = () => {
|
||||
const state = proxy({
|
||||
selectedSchemaName: 'public',
|
||||
setSelectedSchemaName: (schemaName: string) => {
|
||||
state.selectedSchemaName = schemaName
|
||||
},
|
||||
})
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
export type TableEditorState = ReturnType<typeof createTableEditorState>
|
||||
|
||||
export const TableEditorStateContext = createContext<TableEditorState>(createTableEditorState())
|
||||
|
||||
export const TableEditorStateContextProvider = ({ children }: PropsWithChildren<{}>) => {
|
||||
const state = useRef(createTableEditorState()).current
|
||||
|
||||
return (
|
||||
<TableEditorStateContext.Provider value={state}>{children}</TableEditorStateContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useTableEditorStateSnapshot = (options?: Parameters<typeof useSnapshot>[1]) => {
|
||||
const state = useContext(TableEditorStateContext)
|
||||
return useSnapshot(state, options)
|
||||
}
|
||||
@@ -18,7 +18,6 @@ import { ResponseError } from 'types'
|
||||
|
||||
import { IRootStore } from '../RootStore'
|
||||
import ColumnStore from './ColumnStore'
|
||||
import SchemaStore from './SchemaStore'
|
||||
import TableStore, { ITableStore } from './TableStore'
|
||||
import OpenApiStore, { IOpenApiStore } from './OpenApiStore'
|
||||
import { IPostgresMetaInterface } from '../common/PostgresMetaInterface'
|
||||
@@ -51,12 +50,9 @@ const BATCH_SIZE = 1000
|
||||
const CHUNK_SIZE = 1024 * 1024 * 0.1 // 0.1MB
|
||||
|
||||
export interface IMetaStore {
|
||||
excludedSchemas: string[]
|
||||
|
||||
openApi: IOpenApiStore
|
||||
tables: ITableStore
|
||||
columns: IPostgresMetaInterface<PostgresColumn>
|
||||
schemas: IPostgresMetaInterface<PostgresSchema>
|
||||
views: IViewStore
|
||||
materializedViews: IMaterializedViewStore
|
||||
foreignTables: IForeignTableStore
|
||||
@@ -150,7 +146,6 @@ export default class MetaStore implements IMetaStore {
|
||||
openApi: OpenApiStore
|
||||
tables: TableStore
|
||||
columns: ColumnStore
|
||||
schemas: SchemaStore
|
||||
views: ViewStore
|
||||
materializedViews: MaterializedViewStore
|
||||
foreignTables: ForeignTableStore
|
||||
@@ -169,23 +164,6 @@ export default class MetaStore implements IMetaStore {
|
||||
baseUrl: string
|
||||
headers: { [prop: string]: any }
|
||||
|
||||
// [Joshen] I'm going to treat this as a list of system schemas
|
||||
excludedSchemas = [
|
||||
'auth',
|
||||
'extensions',
|
||||
'information_schema',
|
||||
'net',
|
||||
'pgsodium',
|
||||
'pgsodium_masks',
|
||||
'pgbouncer',
|
||||
'realtime',
|
||||
'storage',
|
||||
'supabase_functions',
|
||||
'vault',
|
||||
'graphql',
|
||||
'graphql_public',
|
||||
]
|
||||
|
||||
constructor(rootStore: IRootStore, options: { projectRef: string; connectionString?: string }) {
|
||||
const { projectRef, connectionString } = options
|
||||
this.rootStore = rootStore
|
||||
@@ -204,7 +182,6 @@ export default class MetaStore implements IMetaStore {
|
||||
)
|
||||
this.tables = new TableStore(this.rootStore, `${this.baseUrl}/tables`, this.headers)
|
||||
this.columns = new ColumnStore(this.rootStore, `${this.baseUrl}/columns`, this.headers)
|
||||
this.schemas = new SchemaStore(this.rootStore, `${this.baseUrl}/schemas`, this.headers)
|
||||
this.views = new ViewStore(this.rootStore, `${this.baseUrl}/views`, this.headers)
|
||||
this.materializedViews = new MaterializedViewStore(
|
||||
this.rootStore,
|
||||
@@ -237,9 +214,7 @@ export default class MetaStore implements IMetaStore {
|
||||
)
|
||||
this.types = new TypesStore(this.rootStore, `${this.baseUrl}/types`, this.headers)
|
||||
|
||||
makeObservable(this, {
|
||||
excludedSchemas: observable,
|
||||
})
|
||||
makeObservable(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -989,9 +964,6 @@ export default class MetaStore implements IMetaStore {
|
||||
this.columns.setUrl(`${this.baseUrl}/columns`)
|
||||
this.columns.setHeaders(this.headers)
|
||||
|
||||
this.schemas.setUrl(`${this.baseUrl}/schemas`)
|
||||
this.schemas.setHeaders(this.headers)
|
||||
|
||||
this.views.setUrl(`${this.baseUrl}/views`)
|
||||
this.views.setHeaders(this.headers)
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import type { PostgresSchema } from '@supabase/postgres-meta'
|
||||
import PostgresMetaInterface from '../common/PostgresMetaInterface'
|
||||
import { IRootStore } from '../RootStore'
|
||||
|
||||
export default class SchemaStore extends PostgresMetaInterface<PostgresSchema> {
|
||||
constructor(
|
||||
rootStore: IRootStore,
|
||||
dataUrl: string,
|
||||
headers?: {
|
||||
[prop: string]: any
|
||||
},
|
||||
options?: { identifier: string }
|
||||
) {
|
||||
super(rootStore, dataUrl, headers, options)
|
||||
}
|
||||
|
||||
// Dashboard to hide schemas with pg_ prefix
|
||||
list(filter: any) {
|
||||
const schemasFilter = (schema: PostgresSchema) =>
|
||||
!schema.name.startsWith('pg_') && (typeof filter === 'function' ? filter(schema) : true)
|
||||
|
||||
return super.list(schemasFilter)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user