useProtectedSchemas.ts146 lines · main
| 1 | import { QUEUES_SCHEMA } from '@supabase/pg-meta' |
| 2 | import { uniq, uniqBy } from 'lodash' |
| 3 | import { useMemo } from 'react' |
| 4 | |
| 5 | import { useSelectedProjectQuery } from './misc/useSelectedProject' |
| 6 | import { |
| 7 | BRIVEN_TARGET_SCHEMA_OPTION, |
| 8 | WRAPPERS, |
| 9 | } from '@/components/interfaces/Integrations/Wrappers/Wrappers.constants' |
| 10 | import { |
| 11 | convertKVStringArrayToJson, |
| 12 | wrapperMetaComparator, |
| 13 | } from '@/components/interfaces/Integrations/Wrappers/Wrappers.utils' |
| 14 | import { useFDWsQuery } from '@/data/fdw/fdws-query' |
| 15 | |
| 16 | /** |
| 17 | * A list of system schemas that users should not interact with |
| 18 | */ |
| 19 | export 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 | */ |
| 44 | const 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 | |
| 85 | type 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 | */ |
| 95 | export 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 | */ |
| 125 | export 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 | } |