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