+
+
- ) : defaultSchema ? (
-
+ ) : !!defaultSchema ? (
+
+
- ) : (
-
- }
- >
- Create a new schema "{extension.name}"
-
-
- {schemas
- ?.filter(
- (schema) =>
- !protectedSchemas.some(
- (protectedSchema) => protectedSchema.name === schema.name
- )
- )
- .map((schema) => {
- return (
- }
+
+
+
+ Extension must be installed in the “{defaultSchema}” schema.
+
+
+ ) : (
+
)}
-
-
-
-
-
- >
- )
- }}
-
-
+
+
+
+
+
+
+
+
+
+
)
}
-
-export default EnableExtensionModal
diff --git a/apps/studio/components/interfaces/Database/Extensions/ExtensionRow.tsx b/apps/studio/components/interfaces/Database/Extensions/ExtensionRow.tsx
index 883c350167e7a..56a7e86390223 100644
--- a/apps/studio/components/interfaces/Database/Extensions/ExtensionRow.tsx
+++ b/apps/studio/components/interfaces/Database/Extensions/ExtensionRow.tsx
@@ -1,20 +1,20 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
-import { AlertTriangle, Book, Github, Loader2 } from 'lucide-react'
-import Link from 'next/link'
-import { useState } from 'react'
-import { toast } from 'sonner'
-
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { useDatabaseExtensionDisableMutation } from 'data/database-extensions/database-extension-disable-mutation'
import { DatabaseExtension } from 'data/database-extensions/database-extensions-query'
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useIsOrioleDb, useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { DOCS_URL } from 'lib/constants'
+import { AlertTriangle, Book, Github, Loader2 } from 'lucide-react'
+import Link from 'next/link'
+import { useState } from 'react'
import { extensions } from 'shared-data'
+import { toast } from 'sonner'
import { Button, Switch, TableCell, TableRow, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
import { Admonition } from 'ui-patterns'
-import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
-import EnableExtensionModal from './EnableExtensionModal'
+import { ConfirmationModal } from 'ui-patterns/Dialogs/ConfirmationModal'
+
+import { EnableExtensionModal } from './EnableExtensionModal'
import { EXTENSION_DISABLE_WARNINGS } from './Extensions.constants'
interface ExtensionRowProps {
diff --git a/apps/studio/components/interfaces/Database/Extensions/Extensions.constants.ts b/apps/studio/components/interfaces/Database/Extensions/Extensions.constants.ts
index d22f90d7b8621..065adf1b7dc84 100644
--- a/apps/studio/components/interfaces/Database/Extensions/Extensions.constants.ts
+++ b/apps/studio/components/interfaces/Database/Extensions/Extensions.constants.ts
@@ -24,3 +24,8 @@ export const SEARCH_TERMS: Record
= {
export const EXTENSION_DISABLE_WARNINGS: Record = {
pg_cron: 'Disabling this extension will delete all scheduled jobs. This cannot be undone.',
}
+
+// Extensions that have recommended schemas (rather than required schemas)
+export const extensionsWithRecommendedSchemas: Record = {
+ wrappers: 'extensions',
+}
diff --git a/apps/studio/components/interfaces/Integrations/CronJobs/CreateCronJobSheet/CreateCronJobSheet.tsx b/apps/studio/components/interfaces/Integrations/CronJobs/CreateCronJobSheet/CreateCronJobSheet.tsx
index 53e4232800d16..a836fbc2d25a3 100644
--- a/apps/studio/components/interfaces/Integrations/CronJobs/CreateCronJobSheet/CreateCronJobSheet.tsx
+++ b/apps/studio/components/interfaces/Integrations/CronJobs/CreateCronJobSheet/CreateCronJobSheet.tsx
@@ -1,13 +1,8 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { PermissionAction } from '@supabase/shared-types/out/constants'
-import { parseAsString, useQueryState } from 'nuqs'
-import { useEffect, useState } from 'react'
-import { SubmitHandler, useForm } from 'react-hook-form'
-import { toast } from 'sonner'
-
import { useWatch } from '@ui/components/shadcn/ui/form'
import { useParams } from 'common'
-import EnableExtensionModal from 'components/interfaces/Database/Extensions/EnableExtensionModal'
+import { EnableExtensionModal } from 'components/interfaces/Database/Extensions/EnableExtensionModal'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { getDatabaseCronJob } from 'data/database-cron-jobs/database-cron-job-query'
import { useDatabaseCronJobCreateMutation } from 'data/database-cron-jobs/database-cron-jobs-create-mutation'
@@ -17,11 +12,15 @@ import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
+import { parseAsString, useQueryState } from 'nuqs'
+import { useEffect, useState } from 'react'
+import { SubmitHandler, useForm } from 'react-hook-form'
+import { toast } from 'sonner'
import {
Button,
- Form_Shadcn_,
FormControl_Shadcn_,
FormField_Shadcn_,
+ Form_Shadcn_,
Input_Shadcn_,
RadioGroupStacked,
RadioGroupStackedItem,
@@ -34,6 +33,7 @@ import {
} from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
+
import { CRONJOB_DEFINITIONS } from '../CronJobs.constants'
import { buildCronQuery, buildHttpRequestCommand, parseCronJobCommand } from '../CronJobs.utils'
import { EdgeFunctionSection } from '../EdgeFunctionSection'
@@ -43,9 +43,9 @@ import { HttpRequestSection } from '../HttpRequestSection'
import { SqlFunctionSection } from '../SqlFunctionSection'
import { SqlSnippetSection } from '../SqlSnippetSection'
import {
- FormSchema,
type CreateCronJobForm,
type CronJobType,
+ FormSchema,
} from './CreateCronJobSheet.constants'
import { CronJobScheduleSection } from './CronJobScheduleSection'
diff --git a/apps/studio/components/interfaces/Integrations/Integration/MissingExtensionAlert.tsx b/apps/studio/components/interfaces/Integrations/Integration/MissingExtensionAlert.tsx
index 54526a943fde1..d8dbc1312a15f 100644
--- a/apps/studio/components/interfaces/Integrations/Integration/MissingExtensionAlert.tsx
+++ b/apps/studio/components/interfaces/Integrations/Integration/MissingExtensionAlert.tsx
@@ -1,7 +1,6 @@
-import { useState } from 'react'
-
-import EnableExtensionModal from 'components/interfaces/Database/Extensions/EnableExtensionModal'
+import { EnableExtensionModal } from 'components/interfaces/Database/Extensions/EnableExtensionModal'
import { DatabaseExtension } from 'data/database-extensions/database-extensions-query'
+import { useState } from 'react'
import { Button } from 'ui'
export const MissingExtensionAlert = ({ extension }: { extension: DatabaseExtension }) => {
diff --git a/apps/studio/components/interfaces/LogDrains/LogDrainDestinationSheetForm.tsx b/apps/studio/components/interfaces/LogDrains/LogDrainDestinationSheetForm.tsx
index f612b62df4f2f..e3fa362be1845 100644
--- a/apps/studio/components/interfaces/LogDrains/LogDrainDestinationSheetForm.tsx
+++ b/apps/studio/components/interfaces/LogDrains/LogDrainDestinationSheetForm.tsx
@@ -91,6 +91,14 @@ const formUnion = z.discriminatedUnion('type', [
}),
z.object({
type: z.literal('s3'),
+ s3_bucket: z.string().min(1, { message: 'Bucket name is required' }),
+ storage_region: z.string().min(1, { message: 'Region is required' }),
+ access_key_id: z.string().min(1, { message: 'Access Key ID is required' }),
+ secret_access_key: z.string().min(1, { message: 'Secret Access Key is required' }),
+ batch_timeout: z.coerce
+ .number()
+ .int({ message: 'Batch timeout must be an integer' })
+ .min(1, { message: 'Batch timeout must be a positive integer' }),
}),
z.object({
type: z.literal('axiom'),
@@ -120,7 +128,6 @@ function LogDrainFormItem({
formControl,
placeholder,
type,
- defaultValue,
}: {
value: string
label: string
@@ -128,7 +135,6 @@ function LogDrainFormItem({
placeholder?: string
description?: ReactNode
type?: string
- defaultValue?: string
}) {
return (
(
-
+
)}
@@ -178,6 +179,7 @@ export function LogDrainDestinationSheetForm({
const DEFAULT_HEADERS = mode === 'create' ? CREATE_DEFAULT_HEADERS : defaultConfig?.headers || {}
const sentryEnabled = useFlag('SentryLogDrain')
+ const s3Enabled = useFlag('S3logdrain')
const { ref } = useParams()
const { data: logDrains } = useLogDrainsQuery({
@@ -203,6 +205,11 @@ export function LogDrainDestinationSheetForm({
username: defaultConfig?.username || '',
password: defaultConfig?.password || '',
dsn: defaultConfig?.dsn || '',
+ s3_bucket: defaultConfig?.s3_bucket || '',
+ storage_region: defaultConfig?.storage_region || '',
+ access_key_id: defaultConfig?.access_key_id || '',
+ secret_access_key: defaultConfig?.secret_access_key || '',
+ batch_timeout: defaultConfig?.batch_timeout ?? 3000,
},
})
@@ -290,10 +297,7 @@ export function LogDrainDestinationSheetForm({
form.handleSubmit(onSubmit)(e)
track('log_drain_save_button_clicked', {
- destination: form.getValues('type') as Exclude<
- LogDrainType,
- 'elastic' | 'postgres' | 'bigquery' | 'clickhouse' | 's3' | 'axiom'
- >,
+ destination: form.getValues('type'),
})
}}
>
@@ -327,18 +331,20 @@ export function LogDrainDestinationSheetForm({
{LOG_DRAIN_TYPES.find((t) => t.value === type)?.name}
- {LOG_DRAIN_TYPES.filter((t) => t.value !== 'sentry' || sentryEnabled).map(
- (type) => (
-
- {type.name}
-
- )
- )}
+ {LOG_DRAIN_TYPES.filter(
+ (t) =>
+ (t.value !== 'sentry' || sentryEnabled) &&
+ (t.value !== 's3' || s3Enabled)
+ ).map((type) => (
+
+ {type.name}
+
+ ))}
@@ -517,6 +523,49 @@ export function LogDrainDestinationSheetForm({
/>
)}
+ {type === 's3' && (
+