useProtectedSchemas.ts 4.0 KB

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