UpgradeWarnings.tsx 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. import { useParams } from 'common'
  2. import Link from 'next/link'
  3. import { Button } from 'ui'
  4. import { Admonition } from 'ui-patterns/admonition'
  5. import { InlineLink } from '@/components/ui/InlineLink'
  6. import {
  7. ProjectUpgradeEligibilityValidationError,
  8. ProjectUpgradeEligibilityWarning,
  9. } from '@/data/config/project-upgrade-eligibility-query'
  10. import { DOCS_URL } from '@/lib/constants'
  11. export const ReadReplicasWarning = ({ latestPgVersion }: { latestPgVersion: string }) => {
  12. const { ref } = useParams()
  13. return (
  14. <Admonition
  15. type="note"
  16. showIcon={false}
  17. title="A newer version of Postgres is available"
  18. description={`You will need to remove all read replicas prior to upgrading your Postgres version to the latest available (${latestPgVersion}).`}
  19. actions={
  20. <Button asChild type="default">
  21. <Link href={`/project/${ref}/database/replication`}>Manage read replicas</Link>
  22. </Button>
  23. }
  24. />
  25. )
  26. }
  27. const getValidationErrorTitle = (error: ProjectUpgradeEligibilityValidationError): string => {
  28. switch (error.type) {
  29. case 'objects_depending_on_pg_cron':
  30. return (error.dependents ?? []).join(', ') || 'Objects depending on pg_cron'
  31. case 'indexes_referencing_ll_to_earth':
  32. return `${error.schema_name}.${error.index_name}`
  33. case 'function_using_obsolete_lang':
  34. return `${error.schema_name}.${error.function_name}`
  35. case 'unsupported_extension':
  36. return error.extension_name
  37. case 'unsupported_fdw_handler':
  38. return error.fdw_name
  39. case 'unlogged_table_with_persistent_sequence':
  40. return `${error.schema_name}.${error.table_name}`
  41. case 'user_defined_objects_in_internal_schemas':
  42. return `${error.schema_name}.${error.obj_name}`
  43. case 'active_replication_slot':
  44. return error.slot_name
  45. }
  46. }
  47. const getValidationErrorDescription = (error: ProjectUpgradeEligibilityValidationError): string => {
  48. switch (error.type) {
  49. case 'objects_depending_on_pg_cron':
  50. return 'Remove objects that depend on pg_cron'
  51. case 'indexes_referencing_ll_to_earth':
  52. return `Drop or recreate the index on table ${error.schema_name}.${error.table_name} that references ll_to_earth()`
  53. case 'function_using_obsolete_lang':
  54. return `Update the function to use a supported language instead of ${error.lang_name}`
  55. case 'unsupported_extension':
  56. return 'Remove the unsupported extension'
  57. case 'unsupported_fdw_handler':
  58. return `Update or remove the FDW using the obsolete handler ${error.fdw_handler_name}`
  59. case 'unlogged_table_with_persistent_sequence':
  60. return `Convert the sequence ${error.sequence_name} to unlogged or convert the table to logged`
  61. case 'user_defined_objects_in_internal_schemas':
  62. return `Move the ${error.obj_type} to your own schema`
  63. case 'active_replication_slot':
  64. return 'Drop the active replication slot'
  65. }
  66. }
  67. const ValidationErrorItem = ({ error }: { error: ProjectUpgradeEligibilityValidationError }) => {
  68. const { ref: projectRef } = useParams()
  69. const title = getValidationErrorTitle(error)
  70. const description = getValidationErrorDescription(error)
  71. const getManageLink = (): string | null => {
  72. const encode = encodeURIComponent
  73. switch (error.type) {
  74. case 'function_using_obsolete_lang':
  75. return `/project/${projectRef}/database/functions?schema=${encode(error.schema_name)}&search=${encode(error.function_name)}`
  76. case 'unsupported_extension':
  77. return `/project/${projectRef}/database/extensions?filter=${encode(error.extension_name)}`
  78. case 'indexes_referencing_ll_to_earth':
  79. return `/project/${projectRef}/database/indexes?schema=${encode(error.schema_name)}&search=${encode(error.index_name)}`
  80. case 'unlogged_table_with_persistent_sequence':
  81. return `/project/${projectRef}/database/tables?schema=${encode(error.schema_name)}&search=${encode(error.table_name)}`
  82. case 'user_defined_objects_in_internal_schemas':
  83. if (error.obj_type === 'function') {
  84. return `/project/${projectRef}/database/functions?schema=${encode(error.schema_name)}&search=${encode(error.obj_name)}`
  85. }
  86. if (error.obj_type === 'table') {
  87. return `/project/${projectRef}/database/tables?schema=${encode(error.schema_name)}&search=${encode(error.obj_name)}`
  88. }
  89. return null
  90. case 'active_replication_slot':
  91. return null
  92. case 'unsupported_fdw_handler':
  93. return `/project/${projectRef}/integrations`
  94. case 'objects_depending_on_pg_cron':
  95. return null
  96. default:
  97. return null
  98. }
  99. }
  100. const manageLink = getManageLink()
  101. return (
  102. <li className="py-3 last:pb-0 flex flex-row gap-x-3 justify-between items-center">
  103. <div className="flex flex-col gap-y-0.5 flex-1 min-w-0">
  104. <h6 className="overflow-hidden text-ellipsis whitespace-nowrap min-w-0 text-sm font-normal text-foreground">
  105. {title}
  106. </h6>
  107. <p className="text-foreground-lighter text-xs">{description}</p>
  108. </div>
  109. {manageLink && (
  110. <Button size="tiny" type="default" asChild>
  111. <Link href={manageLink}>Manage</Link>
  112. </Button>
  113. )}
  114. </li>
  115. )
  116. }
  117. export const ValidationErrorsWarning = ({
  118. validationErrors,
  119. }: {
  120. validationErrors: ProjectUpgradeEligibilityValidationError[]
  121. }) => {
  122. if (validationErrors.length === 0) return null
  123. return (
  124. <Admonition type="note" showIcon={false} title="A newer version of Postgres is available">
  125. <div className="flex flex-col gap-3">
  126. <p>
  127. The following issues must be resolved before upgrading.{' '}
  128. <InlineLink href={`${DOCS_URL}/guides/platform/upgrading`}>Learn more</InlineLink>
  129. </p>
  130. <ul className="border-t border-border-muted flex flex-col divide-y divide-border-muted">
  131. {validationErrors.map((error, idx) => (
  132. <ValidationErrorItem key={`${error.type}-${idx}`} error={error} />
  133. ))}
  134. </ul>
  135. </div>
  136. </Admonition>
  137. )
  138. }
  139. const getWarningTitle = (warning: ProjectUpgradeEligibilityWarning): string => {
  140. switch (warning.type) {
  141. case 'pg_graphql_introspection_change':
  142. return 'GraphQL introspection will be disabled by default after upgrade'
  143. }
  144. }
  145. const getWarningDescription = (warning: ProjectUpgradeEligibilityWarning): string => {
  146. switch (warning.type) {
  147. case 'pg_graphql_introspection_change':
  148. return 'After upgrading, queries to `__schema` and `__type` will return an error unless introspection is explicitly re-enabled on the schema. Regular data queries are not affected.'
  149. }
  150. }
  151. const getWarningLink = (warning: ProjectUpgradeEligibilityWarning): string => {
  152. switch (warning.type) {
  153. case 'pg_graphql_introspection_change':
  154. return `${DOCS_URL}/guides/platform/upgrading#upgrading-to-pg_graphql-160`
  155. }
  156. }
  157. export const ValidationWarningsAdmonition = ({
  158. warnings,
  159. }: {
  160. warnings: ProjectUpgradeEligibilityWarning[]
  161. }) => {
  162. if (warnings.length === 0) return null
  163. return warnings.map((warning, idx) => (
  164. <Admonition
  165. key={`${warning.type}-${idx}`}
  166. type="default"
  167. title={getWarningTitle(warning)}
  168. description={getWarningDescription(warning)}
  169. >
  170. <Button asChild type="default" className="mt-2">
  171. <Link href={getWarningLink(warning)} target="_blank" rel="noreferrer">
  172. Read upgrade notes
  173. </Link>
  174. </Button>
  175. </Admonition>
  176. ))
  177. }