Merge branch 'master' into fix/schema-enums

This commit is contained in:
Ivan Vasilov committed 2024-04-17 13:57:41 +02:00
commit 1dc020dd2d
13 files changed
+131 -98

No files matched your search

@@ -3,11 +3,11 @@ import { Lock } from 'lucide-react'
import { useRouter } from 'next/router'
import { useParams } from 'common'
import { useTableEditorStateSnapshot } from 'state/table-editor'
import { Button } from 'ui'
import { generateAlterPolicyQuery, generateCreatePolicyQuery } from './AIPolicyEditorPanel.utils'
interface LockedCreateQuerySection {
schema: string
selectedPolicy?: PostgresPolicy
formFields: { name: string; table: string; behavior: string; command: string; roles: string }
editorOneRef: any
@@ -15,6 +15,7 @@ interface LockedCreateQuerySection {
}
export const LockedCreateQuerySection = ({
schema,
selectedPolicy,
formFields,
editorOneRef,
@@ -22,7 +23,6 @@ export const LockedCreateQuerySection = ({
}: LockedCreateQuerySection) => {
const router = useRouter()
const { ref } = useParams()
const state = useTableEditorStateSnapshot()
const isEditing = selectedPolicy !== undefined
const { name, table, behavior, command, roles } = formFields
@@ -44,7 +44,7 @@ export const LockedCreateQuerySection = ({
const query = isEditing
? generateCreatePolicyQuery({
name,
schema: state.selectedSchemaName,
schema,
table,
behavior,
command,
@@ -58,7 +58,7 @@ export const LockedCreateQuerySection = ({
: generateAlterPolicyQuery({
name: '',
newName: name,
schema: state.selectedSchemaName,
schema,
table,
command,
roles: roles.length === 0 ? 'public' : roles,
@@ -84,7 +84,7 @@ export const LockedCreateQuerySection = ({
<div className="flex items-start" style={{ fontSize: '14px' }}>
<p className="px-6 font-mono text-sm text-foreground-light select-none">2</p>
<p className="font-mono tracking-tighter">
<span className="text-[#569cd6]">on</span> "{state.selectedSchemaName}"."
<span className="text-[#569cd6]">on</span> "{schema}"."
{table}"
</p>
</div>
@@ -124,16 +124,16 @@ export const LockedCreateQuerySection = ({
export const LockedRenameQuerySection = ({
oldName,
newName,
schema,
table,
lineNumber,
}: {
oldName: string
newName: string
schema: string
table: string
lineNumber: number
}) => {
const state = useTableEditorStateSnapshot()
return (
<div className="bg-surface-300 py-1">
<div className="flex items-center" style={{ fontSize: '14px' }}>
@@ -153,7 +153,7 @@ export const LockedRenameQuerySection = ({
</p>
</div>
<p className="font-mono tracking-tighter">
<span className="text-[#569cd6]">on</span> "{state.selectedSchemaName}"."{table}"
<span className="text-[#569cd6]">on</span> "{schema}"."{table}"
</p>
</div>
<div className="flex items-center" style={{ fontSize: '14px' }}>
@@ -1,10 +1,8 @@
import { useEffect } from 'react'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { useDatabaseRolesQuery } from 'data/database-roles/database-roles-query'
import { useTablesQuery } from 'data/tables/tables-query'
import { useTableEditorStateSnapshot } from 'state/table-editor'
import {
FormControl_Shadcn_,
FormField_Shadcn_,
@@ -24,6 +22,7 @@ import {
import { MultiSelectV2 } from 'ui-patterns/MultiSelect/MultiSelectV2'
interface PolicyDetailsV2Props {
schema: string
searchString?: string
isEditing: boolean
form: any
@@ -31,18 +30,18 @@ interface PolicyDetailsV2Props {
}
export const PolicyDetailsV2 = ({
schema,
searchString,
isEditing,
form,
onUpdateCommand,
}: PolicyDetailsV2Props) => {
const { project } = useProjectContext()
const snap = useTableEditorStateSnapshot()
const { data: tables, isSuccess: isSuccessTables } = useTablesQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
schema: snap.selectedSchemaName,
schema: schema,
sortByProperty: 'name',
includeColumns: true,
})
@@ -66,7 +65,7 @@ export const PolicyDetailsV2 = ({
if (!isEditing) {
const table = tables?.find(
(table) =>
table.schema === snap.selectedSchemaName &&
table.schema === schema &&
(table.id.toString() === searchString || table.name === searchString)
)
if (table) {
@@ -117,7 +116,7 @@ export const PolicyDetailsV2 = ({
onValueChange={(value) => form.setValue('table', value)}
>
<SelectTrigger_Shadcn_ className="text-sm h-10">
{snap.selectedSchemaName}.{field.value}
{schema}.{field.value}
</SelectTrigger_Shadcn_>
<SelectContent_Shadcn_>
<SelectGroup_Shadcn_>
@@ -9,9 +9,9 @@ import CardButton from 'components/ui/CardButton'
import CopyButton from 'components/ui/CopyButton'
import NoSearchResults from 'components/ui/NoSearchResults'
import { getGeneralPolicyTemplates } from '../PolicyEditorModal/PolicyEditorModal.constants'
import { useTableEditorStateSnapshot } from 'state/table-editor'
interface PolicyTemplatesProps {
schema: string
table: string
selectedPolicy?: PostgresPolicy
selectedTemplate?: string
@@ -19,17 +19,14 @@ interface PolicyTemplatesProps {
}
export const PolicyTemplates = ({
schema,
table,
selectedPolicy,
selectedTemplate,
onSelectTemplate,
}: PolicyTemplatesProps) => {
const [search, setSearch] = useState('')
const state = useTableEditorStateSnapshot()
const templates = getGeneralPolicyTemplates(
state.selectedSchemaName,
table.length > 0 ? table : 'table_name'
)
const templates = getGeneralPolicyTemplates(schema, table.length > 0 ? table : 'table_name')
const baseTemplates =
selectedPolicy !== undefined
@@ -15,7 +15,6 @@ import {
Checkbox_Shadcn_,
Form_Shadcn_,
Label_Shadcn_,
Modal,
ScrollArea,
Sheet,
SheetContent,
@@ -45,7 +44,6 @@ import { useSelectedOrganization, useSelectedProject } from 'hooks'
import { BASE_PATH, OPT_IN_TAGS } from 'lib/constants'
import { uuidv4 } from 'lib/helpers'
import Telemetry from 'lib/telemetry'
import { useTableEditorStateSnapshot } from 'state/table-editor'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { AIPolicyChat } from './AIPolicyChat'
import {
@@ -70,6 +68,7 @@ const DiffEditor = dynamic(
interface AIPolicyEditorPanelProps {
visible: boolean
schema: string
searchString?: string
selectedPolicy?: PostgresPolicy
onSelectCancel: () => void
@@ -80,6 +79,7 @@ interface AIPolicyEditorPanelProps {
*/
export const AIPolicyEditorPanel = memo(function ({
visible,
schema,
searchString,
selectedPolicy,
onSelectCancel,
@@ -91,7 +91,6 @@ export const AIPolicyEditorPanel = memo(function ({
const selectedOrganization = useSelectedOrganization()
const telemetryProps = useTelemetryProps()
const state = useTableEditorStateSnapshot()
const isAiAssistantEnabled = useIsRLSAIAssistantEnabled()
// [Joshen] Hyrid form fields, just spit balling to get a decent POC out
@@ -330,7 +329,7 @@ export const AIPolicyEditorPanel = memo(function ({
if (selectedPolicy === undefined) {
const sql = generateCreatePolicyQuery({
name: name,
schema: state.selectedSchemaName,
schema,
table,
behavior,
command,
@@ -534,6 +533,7 @@ export const AIPolicyEditorPanel = memo(function ({
) : (
<>
<PolicyDetailsV2
schema={schema}
searchString={searchString}
isEditing={selectedPolicy !== undefined}
form={form}
@@ -544,6 +544,7 @@ export const AIPolicyEditorPanel = memo(function ({
/>
<div className="h-full">
<LockedCreateQuerySection
schema={schema}
selectedPolicy={selectedPolicy}
editorOneRef={editorOneRef}
editorTwoRef={editorTwoRef}
@@ -670,6 +671,7 @@ export const AIPolicyEditorPanel = memo(function ({
<LockedRenameQuerySection
oldName={selectedPolicy.name}
newName={name}
schema={schema}
table={table}
lineNumber={
8 + expOneLineCount + (showCheckBlock ? expTwoLineCount : 0)
@@ -789,6 +791,7 @@ export const AIPolicyEditorPanel = memo(function ({
>
<ScrollArea className="h-full w-full">
<PolicyTemplates
schema={schema}
table={table}
selectedPolicy={selectedPolicy}
selectedTemplate={selectedDiff}
@@ -14,10 +14,10 @@ import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState'
import InformationBox from 'components/ui/InformationBox'
import { useDatabasePolicyDeleteMutation } from 'data/database-policies/database-policy-delete-mutation'
import { useTableUpdateMutation } from 'data/tables/table-update-mutation'
import { useTableEditorStateSnapshot } from 'state/table-editor'
import ConfirmModal from 'ui-patterns/Dialogs/ConfirmDialog'
interface PoliciesProps {
schema: string
tables: PostgresTable[]
hasTables: boolean
isLocked: boolean
@@ -25,6 +25,7 @@ interface PoliciesProps {
}
const Policies = ({
schema,
tables,
hasTables,
isLocked,
@@ -33,7 +34,6 @@ const Policies = ({
const router = useRouter()
const { ref } = useParams()
const { project } = useProjectContext()
const snap = useTableEditorStateSnapshot()
const [selectedTableToToggleRLS, setSelectedTableToToggleRLS] = useState<any>({})
const [selectedPolicyToDelete, setSelectedPolicyToDelete] = useState<any>({})
@@ -137,7 +137,7 @@ const Policies = ({
return (
<>
<div className="flex flex-col gap-y-4 pb-4">
{isLocked && <ProtectedSchemaWarning schema={snap.selectedSchemaName} entity="policies" />}
{isLocked && <ProtectedSchemaWarning schema={schema} entity="policies" />}
{tables.length > 0 ? (
tables.map((table: any) => (
<section key={table.id}>
@@ -162,7 +162,7 @@ const GridHeaderActions = ({ table, isViewSelected, isTableSelected }: GridHeade
>
<Link
passHref
href={`/project/${projectRef}/auth/policies?search=${table.id}`}
href={`/project/${projectRef}/auth/policies?search=${table.id}&schema=${table.schema}`}
>
Add RLS policy
</Link>
@@ -202,7 +202,10 @@ const GridHeaderActions = ({ table, isViewSelected, isTableSelected }: GridHeade
)
}
>
<Link passHref href={`/project/${projectRef}/auth/policies?search=${table.id}`}>
<Link
passHref
href={`/project/${projectRef}/auth/policies?search=${table.id}&schema=${table.schema}`}
>
Auth {policies.length > 1 ? 'policies' : 'policy'}
</Link>
</Button>
+18 -14
View File
@@ -1,11 +1,11 @@
import { useCallback } from 'react'
import { useRouter } from 'next/router'
import { Dispatch, SetStateAction, useCallback, useMemo } from 'react'
export type UrlStateParams = {
[k: string]: string | string[] | undefined
}
export function useUrlState({
export function useUrlState<ValueParams extends UrlStateParams>({
replace = true,
arrayKeys = [],
}: {
@@ -14,22 +14,26 @@ export function useUrlState({
*/
replace?: boolean
arrayKeys?: string[]
} = {}) {
const arrayKeysSet = new Set(arrayKeys)
} = {}): [ValueParams, Dispatch<SetStateAction<ValueParams>>] {
const stringifiedArrayKeys = JSON.stringify(arrayKeys)
// eslint-disable-next-line react-hooks/exhaustive-deps
const arrayKeysSet = useMemo(() => new Set(arrayKeys), [stringifiedArrayKeys])
const router = useRouter()
const params: UrlStateParams = Object.fromEntries(
Object.entries(router.query).map(([key, value]) => {
if (arrayKeysSet.has(key)) {
return Array.isArray(value) ? [key, value] : [key, [value]]
}
const params: ValueParams = useMemo(() => {
return Object.fromEntries(
Object.entries(router.query).map(([key, value]) => {
if (arrayKeysSet.has(key)) {
return Array.isArray(value) ? [key, value] : [key, [value]]
}
return [key, value]
})
)
return [key, value]
})
)
}, [arrayKeysSet, router.query])
const setParams = useCallback(
(newParams: UrlStateParams | ((previousParams: UrlStateParams) => UrlStateParams)) => {
const setParams: Dispatch<SetStateAction<ValueParams>> = useCallback(
(newParams) => {
const nextParams = typeof newParams === 'function' ? newParams(params) : newParams
let newQuery = Object.fromEntries(
Object.entries({ ...params, ...nextParams }).filter(([, value]) => Boolean(value))
@@ -1,10 +1,7 @@
import type { PostgresPolicy, PostgresTable } from '@supabase/postgres-meta'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { partition } from 'lodash'
import { useEffect, useState } from 'react'
import { useState } from 'react'
import { useParams } from 'common'
import { useIsRLSAIAssistantEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext'
import { Policies } from 'components/interfaces/Auth/Policies'
import { AIPolicyEditorPanel } from 'components/interfaces/Auth/Policies/AIPolicyEditorPanel'
import { AuthLayout } from 'components/layouts'
@@ -16,9 +13,9 @@ import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
import { useDatabasePoliciesQuery } from 'data/database-policies/database-policies-query'
import { useSchemasQuery } from 'data/database/schemas-query'
import { useTablesQuery } from 'data/tables/tables-query'
import { useCheckPermissions, usePermissionsLoaded } from 'hooks'
import { useCheckPermissions, usePermissionsLoaded, useUrlState } from 'hooks'
import { EXCLUDED_SCHEMAS } from 'lib/constants/schemas'
import { useTableEditorStateSnapshot } from 'state/table-editor'
import { partition } from 'lodash'
import type { NextPageWithLayout } from 'types'
import {
Button,
@@ -66,22 +63,16 @@ const onFilterTables = (
}
const AuthPoliciesPage: NextPageWithLayout = () => {
const { search, schema } = useParams()
const [params, setParams] = useUrlState<{
schema?: string
search?: string
}>()
const { schema = 'public', search: searchString = '' } = params
const { project } = useProjectContext()
const snap = useTableEditorStateSnapshot()
const [searchString, setSearchString] = useState<string>('')
const [showPolicyAiEditor, setShowPolicyAiEditor] = useState(false)
const [selectedPolicyToEdit, setSelectedPolicyToEdit] = useState<PostgresPolicy>()
useEffect(() => {
if (search) setSearchString(search)
}, [search])
useEffect(() => {
if (schema) snap.setSelectedSchemaName(schema)
}, [schema])
const { data: schemas } = useSchemasQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
@@ -89,7 +80,7 @@ const AuthPoliciesPage: NextPageWithLayout = () => {
const [protectedSchemas] = partition(schemas, (schema) =>
EXCLUDED_SCHEMAS.includes(schema?.name ?? '')
)
const selectedSchema = schemas?.find((schema) => schema.name === snap.selectedSchemaName)
const selectedSchema = schemas?.find((s) => s.name === schema)
const isLocked = protectedSchemas.some((s) => s.id === selectedSchema?.id)
const { data: policies } = useDatabasePoliciesQuery({
@@ -106,7 +97,7 @@ const AuthPoliciesPage: NextPageWithLayout = () => {
} = useTablesQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
schema: snap.selectedSchemaName,
schema: schema,
})
const filteredTables = onFilterTables(tables ?? [], policies ?? [], searchString)
@@ -128,18 +119,20 @@ const AuthPoliciesPage: NextPageWithLayout = () => {
className="w-[260px]"
size="small"
showError={false}
selectedSchemaName={snap.selectedSchemaName}
onSelectSchema={(schema: string) => {
snap.setSelectedSchemaName(schema)
setSearchString('')
selectedSchemaName={schema}
onSelectSchema={(schema) => {
setParams({ ...params, search: undefined, schema })
}}
/>
<Input
size="small"
placeholder="Filter tables and policies"
className="block w-64 text-sm placeholder-border-muted"
value={searchString}
onChange={(e) => setSearchString(e.target.value)}
value={searchString || ''}
onChange={(e) => {
const str = e.target.value
setParams({ ...params, search: str === '' ? undefined : str })
}}
icon={<IconSearch size="tiny" />}
/>
</div>
@@ -169,7 +162,7 @@ const AuthPoliciesPage: NextPageWithLayout = () => {
{!canCreatePolicies
? 'You need additional permissions to create RLS policies'
: schemaHasNoTables
? `No table in schema ${snap.selectedSchemaName} to create policies on`
? `No table in schema ${schema} to create policies on`
: null}
</TooltipContent_Shadcn_>
)}
@@ -184,6 +177,7 @@ const AuthPoliciesPage: NextPageWithLayout = () => {
{isSuccess && (
<Policies
schema={schema}
tables={filteredTables}
hasTables={tables.length > 0}
isLocked={isLocked}
@@ -196,6 +190,7 @@ const AuthPoliciesPage: NextPageWithLayout = () => {
<AIPolicyEditorPanel
visible={showPolicyAiEditor}
schema={schema}
searchString={searchString}
selectedPolicy={selectedPolicyToEdit}
onSelectCancel={() => {
+3
View File
@@ -1,6 +1,7 @@
import Nav from 'components/Nav/index'
import Footer from 'components/Footer/index'
import { cn } from 'ui'
import { useForceDeepDark } from '~/lib/theme.utils'
type Props = {
hideHeader?: boolean
@@ -19,6 +20,8 @@ const DefaultLayout = (props: Props) => {
children,
} = props
useForceDeepDark()
return (
<>
<Nav hideNavbar={hideHeader} />
+43
View File
@@ -0,0 +1,43 @@
import { useEffect } from 'react'
import { useTheme } from 'next-themes'
import { useRouter } from 'next/router'
export function useForceDeepDark() {
const router = useRouter()
const { resolvedTheme, theme } = useTheme()
const isDarkTheme = resolvedTheme?.includes('dark')
const isGaSection = router.pathname.includes('/ga-week') || router.pathname === '/ga'
useEffect(() => {
const handleDocumentLoad = () => {
// Update the HTML element attributes
const theme = isDarkTheme ? (isGaSection ? 'deep-dark' : 'dark') : 'light'
document.documentElement.setAttribute('data-theme', theme)
document.documentElement.style.colorScheme = theme
// wait before setting the theme
setTimeout(() => {
document.documentElement.setAttribute('data-theme', theme)
document.documentElement.style.colorScheme = theme
}, 200)
// Clean up the event listener
window.removeEventListener('load', handleDocumentLoad)
}
// Check if document is already loaded
if (document.readyState === 'complete') {
handleDocumentLoad()
} else {
// Add a global load event listener
window.addEventListener('load', handleDocumentLoad)
}
// Clean up the event listener on component unmount
return () => {
window.removeEventListener('load', handleDocumentLoad)
}
}, [resolvedTheme, theme, isDarkTheme, router])
}
-15
View File
@@ -14,7 +14,6 @@ import { Meetup } from '~/components/LaunchWeek/11/LW11Meetups'
import LW11StickyNav from '~/components/LaunchWeek/11/Releases/LW11StickyNav'
import LW11Header from '~/components/LaunchWeek/11/Releases/LW11Header'
import MainStage from '~/components/LaunchWeek/11/Releases/MainStage'
import { useTheme } from 'next-themes'
const BuildStage = dynamic(() => import('~/components/LaunchWeek/11/Releases/BuildStage'))
const LW11Meetups = dynamic(() => import('~/components/LaunchWeek/11/LW11Meetups'))
@@ -29,7 +28,6 @@ interface Props {
export default function GAWeekIndex({ meetups }: Props) {
const { query } = useRouter()
const { resolvedTheme, setTheme } = useTheme()
const TITLE = 'Supabase GA Week | 15-19 April 2024'
const DESCRIPTION = 'Join us for a week of announcing new features, every day at 7 AM PT.'
@@ -65,19 +63,6 @@ export default function GAWeekIndex({ meetups }: Props) {
}
}, [supabase])
const isDark = resolvedTheme?.includes('dark')
const isDarkTheme = resolvedTheme === 'dark'
useEffect(() => {
isDarkTheme && setTheme('deep-dark')
}, [isDarkTheme])
useEffect(() => {
return () => {
isDark && setTheme('dark')
}
}, [])
useEffect(() => {
if (session?.user) {
if (userData?.id) {
+2 -14
View File
@@ -1,4 +1,4 @@
import React, { useEffect } from 'react'
import React from 'react'
import dynamic from 'next/dynamic'
import Link from 'next/link'
import { useRouter } from 'next/router'
@@ -19,7 +19,7 @@ const CTABanner = dynamic(() => import('~/components/CTABanner'))
export default function IndexPage() {
const router = useRouter()
const { resolvedTheme, setTheme } = useTheme()
const { resolvedTheme } = useTheme()
const isDark = resolvedTheme?.includes('dark')
const data = pageData(isDark!)
@@ -27,18 +27,6 @@ export default function IndexPage() {
const meta_description = 'Supabase is officially launching into General Availability.'
const meta_image = '/images/ga/ga-og-alt.png'
const isDarkTheme = resolvedTheme === 'dark'
useEffect(() => {
isDarkTheme && setTheme('deep-dark')
}, [isDarkTheme])
useEffect(() => {
return () => {
isDark && setTheme('dark')
}
}, [])
return (
<DefaultLayout>
<NextSeo
+13
View File
@@ -152,6 +152,19 @@ export const pricing: Pricing = {
},
usage_based: true,
},
{
title: 'Anonymous Sign-ins',
tooltips: {
main: 'Anonymous user requests count towards MAU, just like a permanent user.',
},
plans: {
free: true,
pro: true,
team: true,
enterprise: true,
},
usage_based: false,
},
{
title: 'Social OAuth providers',
plans: {