useInstalledIntegrations.tsx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import { useMemo } from 'react'
  2. import {
  3. hasMatchingWrapper,
  4. hasRequiredExtensions,
  5. isOAuthInstalled,
  6. isStripeSyncEngineInstalled,
  7. } from './Landing.utils'
  8. import { useAvailableIntegrations } from './useAvailableIntegrations'
  9. import { useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
  10. import { useDatabaseExtensionsQuery } from '@/data/database-extensions/database-extensions-query'
  11. import { useSchemasQuery } from '@/data/database/schemas-query'
  12. import { useFDWsQuery } from '@/data/fdw/fdws-query'
  13. import { useSecretsQuery } from '@/data/secrets/secrets-query'
  14. import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
  15. import { EMPTY_ARR } from '@/lib/void'
  16. export const useInstalledIntegrations = () => {
  17. const { data: project } = useSelectedProjectQuery()
  18. const {
  19. data: allIntegrations = EMPTY_ARR,
  20. error: availableIntegrationsError,
  21. isPending: isAvailableIntegrationsLoading,
  22. isSuccess: isSuccessAvailableIntegrations,
  23. isError: isErrorAvailableIntegrations,
  24. } = useAvailableIntegrations()
  25. const hasSecretKeyPrefixIntegration = useMemo(() => {
  26. return allIntegrations.some(
  27. (integration) =>
  28. integration.type === 'oauth' &&
  29. integration.installIdentificationMethod === 'secret_key_prefix' &&
  30. !!integration.secretKeyPrefix
  31. )
  32. }, [allIntegrations])
  33. const hasEdgeFunctionSecretNameIntegration = useMemo(() => {
  34. return allIntegrations.some(
  35. (integration) =>
  36. integration.type === 'oauth' &&
  37. integration.installIdentificationMethod === 'edge_function_secret_name' &&
  38. !!integration.edgeFunctionSecretName
  39. )
  40. }, [allIntegrations])
  41. const {
  42. data: apiKeys = EMPTY_ARR,
  43. error: apiKeysError,
  44. isError: isErrorApiKeys,
  45. isLoading: isApiKeysLoading,
  46. isSuccess: isSuccessApiKeys,
  47. } = useAPIKeysQuery(
  48. { projectRef: project?.ref, reveal: false },
  49. { enabled: hasSecretKeyPrefixIntegration }
  50. )
  51. const {
  52. data: edgeFunctionSecrets = EMPTY_ARR,
  53. error: edgeFunctionSecretsError,
  54. isError: isErrorEdgeFunctionSecrets,
  55. isLoading: isEdgeFunctionSecretsLoading,
  56. isSuccess: isSuccessEdgeFunctionSecrets,
  57. } = useSecretsQuery(
  58. { projectRef: project?.ref },
  59. { enabled: hasEdgeFunctionSecretNameIntegration }
  60. )
  61. const {
  62. data: wrappers = EMPTY_ARR,
  63. error: fdwError,
  64. isError: isErrorFDWs,
  65. isPending: isFDWLoading,
  66. isSuccess: isSuccessFDWs,
  67. } = useFDWsQuery({
  68. projectRef: project?.ref,
  69. connectionString: project?.connectionString,
  70. })
  71. const {
  72. data: extensions = EMPTY_ARR,
  73. error: extensionsError,
  74. isError: isErrorExtensions,
  75. isPending: isExtensionsLoading,
  76. isSuccess: isSuccessExtensions,
  77. } = useDatabaseExtensionsQuery({
  78. projectRef: project?.ref,
  79. connectionString: project?.connectionString,
  80. })
  81. const {
  82. data: schemas = EMPTY_ARR,
  83. error: schemasError,
  84. isError: isErrorSchemas,
  85. isPending: isSchemasLoading,
  86. isSuccess: isSuccessSchemas,
  87. } = useSchemasQuery({
  88. projectRef: project?.ref,
  89. connectionString: project?.connectionString,
  90. })
  91. const isHooksEnabled = schemas?.some((schema) => schema.name === 'briven_functions')
  92. const installedIntegrations = useMemo(() => {
  93. return allIntegrations
  94. .filter((integration) => {
  95. if (integration.id === 'webhooks') return isHooksEnabled
  96. if (integration.id === 'data_api') return true
  97. if (integration.id === 'stripe_sync_engine') {
  98. return isStripeSyncEngineInstalled(schemas)
  99. }
  100. if (integration.type === 'wrapper') {
  101. return hasMatchingWrapper({ meta: integration.meta, wrappers })
  102. }
  103. if (integration.type === 'postgres_extension') {
  104. return hasRequiredExtensions({ integration, extensions })
  105. }
  106. if (integration.type === 'oauth') {
  107. return isOAuthInstalled({ integration, apiKeys, secrets: edgeFunctionSecrets })
  108. }
  109. return false
  110. })
  111. .sort((a, b) => a.name.localeCompare(b.name))
  112. }, [allIntegrations, wrappers, extensions, schemas, isHooksEnabled, apiKeys, edgeFunctionSecrets])
  113. const error =
  114. fdwError ||
  115. extensionsError ||
  116. schemasError ||
  117. availableIntegrationsError ||
  118. (hasSecretKeyPrefixIntegration ? apiKeysError : null) ||
  119. (hasEdgeFunctionSecretNameIntegration ? edgeFunctionSecretsError : null)
  120. const isLoading =
  121. isSchemasLoading ||
  122. isFDWLoading ||
  123. isExtensionsLoading ||
  124. isAvailableIntegrationsLoading ||
  125. (hasSecretKeyPrefixIntegration && isApiKeysLoading) ||
  126. (hasEdgeFunctionSecretNameIntegration && isEdgeFunctionSecretsLoading)
  127. const isError =
  128. isErrorFDWs ||
  129. isErrorExtensions ||
  130. isErrorSchemas ||
  131. isErrorAvailableIntegrations ||
  132. (hasSecretKeyPrefixIntegration && isErrorApiKeys) ||
  133. (hasEdgeFunctionSecretNameIntegration && isErrorEdgeFunctionSecrets)
  134. const isSuccess =
  135. isSuccessFDWs &&
  136. isSuccessExtensions &&
  137. isSuccessSchemas &&
  138. isSuccessAvailableIntegrations &&
  139. (!hasSecretKeyPrefixIntegration || isSuccessApiKeys) &&
  140. (!hasEdgeFunctionSecretNameIntegration || isSuccessEdgeFunctionSecrets)
  141. return {
  142. // show all integrations at once instead of showing partial results
  143. installedIntegrations: isLoading ? EMPTY_ARR : installedIntegrations,
  144. error,
  145. isError,
  146. isLoading,
  147. isSuccess,
  148. }
  149. }