useProtectedSchemas.ts146 lines · main
1import { QUEUES_SCHEMA } from '@supabase/pg-meta'
2import { uniq, uniqBy } from 'lodash'
3import { useMemo } from 'react'
4
5import { useSelectedProjectQuery } from './misc/useSelectedProject'
6import {
7 BRIVEN_TARGET_SCHEMA_OPTION,
8 WRAPPERS,
9} from '@/components/interfaces/Integrations/Wrappers/Wrappers.constants'
10import {
11 convertKVStringArrayToJson,
12 wrapperMetaComparator,
13} from '@/components/interfaces/Integrations/Wrappers/Wrappers.utils'
14import { useFDWsQuery } from '@/data/fdw/fdws-query'
15
16/**
17 * A list of system schemas that users should not interact with
18 */
19export const INTERNAL_SCHEMAS = [
20 'auth',
21 'cron',
22 'etl',
23 'extensions',
24 'information_schema',
25 'net',
26 'pgsodium',
27 'pgsodium_masks',
28 'pgbouncer',
29 'pgtle',
30 'pgmq',
31 'realtime',
32 'storage',
33 'briven_functions',
34 'briven_migrations',
35 'vault',
36 'graphql',
37 'graphql_public',
38 QUEUES_SCHEMA,
39]
40
41/**
42 * Get the list of schemas used by FDWs like Iceberg, S3 Vectors, etc.
43 */
44const useFdwSchemasQuery = () => {
45 const { data: project } = useSelectedProjectQuery()
46 const result = useFDWsQuery({
47 projectRef: project?.ref,
48 connectionString: project?.connectionString,
49 })
50
51 // Find all wrappers that create a schema for their data.
52 const FDWsWithSchemas = useMemo(
53 () =>
54 WRAPPERS.filter((wrapper) =>
55 wrapper.server.options.some((option) => option.name === BRIVEN_TARGET_SCHEMA_OPTION.name)
56 ),
57 []
58 )
59
60 const schemas = useMemo(() => {
61 const icebergFDWs =
62 result.data?.filter((wrapper) =>
63 FDWsWithSchemas.some((w) => wrapperMetaComparator(w, wrapper))
64 ) ?? []
65
66 const fdwSchemas = icebergFDWs.map((fdw) => {
67 const schemaOption =
68 convertKVStringArrayToJson(fdw.server_options ?? [])['briven_target_schema'] ?? ''
69
70 const schemas = uniq(schemaOption.split(',').filter(Boolean))
71
72 return {
73 serverName: fdw.server_name,
74 type: fdw.handler.replace('_fdw_handler', ''),
75 schemas,
76 }
77 })
78
79 return fdwSchemas
80 }, [result.data, FDWsWithSchemas])
81
82 return { ...result, data: schemas }
83}
84
85type ProtectedSchema = {
86 name: string
87 type: 'fdw' | 'internal'
88 fdwType?: string
89 serverName?: string
90}
91
92/**
93 * Returns a list of schemas that are protected by Briven (internal schemas or schemas used by Iceberg FDWs).
94 */
95export const useProtectedSchemas = ({
96 excludeSchemas = [],
97}: { excludeSchemas?: string[] } = {}) => {
98 // Stabilize the excludeSchemas array to prevent unnecessary re-computations
99 // eslint-disable-next-line react-hooks/exhaustive-deps
100 const stableExcludeSchemas = useMemo(() => excludeSchemas, [JSON.stringify(excludeSchemas)])
101
102 const result = useFdwSchemasQuery()
103
104 const schemas = useMemo<ProtectedSchema[]>(() => {
105 const internalSchemas = INTERNAL_SCHEMAS.map((s) => ({ name: s, type: 'internal' as const }))
106 const fdwSchemas = result.data?.flatMap((s) =>
107 s.schemas.map((schema) => ({
108 name: schema,
109 type: 'fdw' as const,
110 fdwType: s.type,
111 serverName: s.serverName,
112 }))
113 )
114
115 const schemas = uniqBy([...internalSchemas, ...fdwSchemas], (s) => s.name)
116 return schemas.filter((schema) => !stableExcludeSchemas.includes(schema.name))
117 }, [result.data, stableExcludeSchemas])
118
119 return { ...result, data: schemas }
120}
121
122/**
123 * Returns whether a given schema is protected by Briven (internal schema or schema used by Iceberg FDWs).
124 */
125export const useIsProtectedSchema = ({
126 schema,
127 excludedSchemas = [],
128}: {
129 schema: string
130 excludedSchemas?: string[]
131}):
132 | { isSchemaLocked: false; reason: undefined; fdwType: undefined }
133 | { isSchemaLocked: true; reason: 'internal' | 'fdw'; fdwType: string | undefined } => {
134 const { data: schemas } = useProtectedSchemas({ excludeSchemas: excludedSchemas })
135
136 const foundSchema = schemas.find((s) => s.name === schema)
137
138 if (foundSchema) {
139 return {
140 isSchemaLocked: true,
141 reason: foundSchema.type,
142 fdwType: foundSchema.fdwType,
143 }
144 }
145 return { isSchemaLocked: false, reason: undefined, fdwType: undefined }
146}