Swap gateway to outer layer

This commit is contained in:
x032205
2025-07-17 20:25:10 -04:00
parent e6bfb6ce2b
commit a8b448be0f
24 changed files with 241 additions and 168 deletions

View File

@@ -0,0 +1,19 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasColumn(TableName.AppConnection, "gatewayId"))) {
await knex.schema.alterTable(TableName.AppConnection, (t) => {
t.uuid("gatewayId").nullable();
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasColumn(TableName.AppConnection, "gatewayId")) {
await knex.schema.alterTable(TableName.AppConnection, (t) => {
t.dropColumn("gatewayId");
});
}
}

View File

@@ -20,7 +20,8 @@ export const AppConnectionsSchema = z.object({
orgId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date(),
isPlatformManagedCredentials: z.boolean().default(false).nullable().optional()
isPlatformManagedCredentials: z.boolean().default(false).nullable().optional(),
gatewayId: z.string().uuid().nullable().optional()
});
export type TAppConnections = z.infer<typeof AppConnectionsSchema>;

View File

@@ -24,7 +24,6 @@ export const SanitizedOracleDBConnectionSchema = z.discriminatedUnion("method",
BaseOracleDBConnectionSchema.extend({
method: z.literal(OracleDBConnectionMethod.UsernameAndPassword),
credentials: OracleDBConnectionCredentialsSchema.pick({
gatewayId: true,
host: true,
database: true,
port: true,

View File

@@ -2199,7 +2199,6 @@ export const AppConnections = {
audience: "The unique identifier of the target API you want to access."
},
SQL_CONNECTION: {
gatewayId: "The ID of the gateway to use when performing database requests.",
host: "The hostname of the database server.",
port: "The port number of the database.",
database: "The name of the database to connect to.",

View File

@@ -1707,7 +1707,8 @@ export const registerRoutes = async (
permissionService,
kmsService,
licenseService,
gatewayService
gatewayService,
gatewayDAL
});
const secretSyncService = secretSyncServiceFactory({

View File

@@ -25,12 +25,14 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
credentials: I["credentials"];
description?: string | null;
isPlatformManagedCredentials?: boolean;
gatewayId?: string | null;
}>;
updateSchema: z.ZodType<{
name?: string;
credentials?: I["credentials"];
description?: string | null;
isPlatformManagedCredentials?: boolean;
gatewayId?: string | null;
}>;
sanitizedResponseSchema: z.ZodTypeAny;
}) => {
@@ -224,10 +226,10 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { name, method, credentials, description, isPlatformManagedCredentials } = req.body;
const { name, method, credentials, description, isPlatformManagedCredentials, gatewayId } = req.body;
const appConnection = (await server.services.appConnection.createAppConnection(
{ name, method, app, credentials, description, isPlatformManagedCredentials },
{ name, method, app, credentials, description, isPlatformManagedCredentials, gatewayId },
req.permission
)) as T;
@@ -270,11 +272,14 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { name, credentials, description, isPlatformManagedCredentials } = req.body;
const { name, credentials, description, isPlatformManagedCredentials, gatewayId } = req.body;
const { connectionId } = req.params;
console.log("123");
console.log(gatewayId);
const appConnection = (await server.services.appConnection.updateAppConnection(
{ name, credentials, connectionId, description, isPlatformManagedCredentials },
{ name, credentials, connectionId, description, isPlatformManagedCredentials, gatewayId },
req.permission
)) as T;

View File

@@ -30,7 +30,8 @@ export const GenericCreateAppConnectionFieldsSchema = (
.describe(AppConnections.CREATE(app).description),
isPlatformManagedCredentials: supportsPlatformManagedCredentials
? z.boolean().optional().default(false).describe(AppConnections.CREATE(app).isPlatformManagedCredentials)
: z.literal(false).optional().describe(`Not supported for ${APP_CONNECTION_NAME_MAP[app]} Connections.`)
: z.literal(false).optional().describe(`Not supported for ${APP_CONNECTION_NAME_MAP[app]} Connections.`),
gatewayId: z.string().uuid().nullish().describe("The Gateway ID to use for this connection.")
});
export const GenericUpdateAppConnectionFieldsSchema = (
@@ -47,5 +48,6 @@ export const GenericUpdateAppConnectionFieldsSchema = (
.describe(AppConnections.UPDATE(app).description),
isPlatformManagedCredentials: supportsPlatformManagedCredentials
? z.boolean().optional().describe(AppConnections.UPDATE(app).isPlatformManagedCredentials)
: z.literal(false).optional().describe(`Not supported for ${APP_CONNECTION_NAME_MAP[app]} Connections.`)
: z.literal(false).optional().describe(`Not supported for ${APP_CONNECTION_NAME_MAP[app]} Connections.`),
gatewayId: z.string().uuid().nullish().describe("The Gateway ID to use for this connection.")
});

View File

@@ -3,9 +3,14 @@ import { ForbiddenError, subject } from "@casl/ability";
import { ValidateOCIConnectionCredentialsSchema } from "@app/ee/services/app-connections/oci";
import { ociConnectionService } from "@app/ee/services/app-connections/oci/oci-connection-service";
import { ValidateOracleDBConnectionCredentialsSchema } from "@app/ee/services/app-connections/oracledb";
import { TGatewayDALFactory } from "@app/ee/services/gateway/gateway-dal";
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionAppConnectionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import {
OrgPermissionAppConnectionActions,
OrgPermissionGatewayActions,
OrgPermissionSubjects
} from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { crypto } from "@app/lib/crypto/cryptography";
import { DatabaseErrorCode } from "@app/lib/error-codes";
@@ -98,6 +103,7 @@ export type TAppConnectionServiceFactoryDep = {
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">;
gatewayDAL: Pick<TGatewayDALFactory, "find">;
};
export type TAppConnectionServiceFactory = ReturnType<typeof appConnectionServiceFactory>;
@@ -144,7 +150,8 @@ export const appConnectionServiceFactory = ({
permissionService,
kmsService,
licenseService,
gatewayService
gatewayService,
gatewayDAL
}: TAppConnectionServiceFactoryDep) => {
const listAppConnectionsByOrg = async (actor: OrgServiceActor, app?: AppConnection) => {
const { permission } = await permissionService.getOrgPermission(
@@ -225,7 +232,7 @@ export const appConnectionServiceFactory = ({
};
const createAppConnection = async (
{ method, app, credentials, ...params }: TCreateAppConnectionDTO,
{ method, app, credentials, gatewayId, ...params }: TCreateAppConnectionDTO,
actor: OrgServiceActor
) => {
const { permission } = await permissionService.getOrgPermission(
@@ -241,6 +248,20 @@ export const appConnectionServiceFactory = ({
OrgPermissionSubjects.AppConnections
);
if (gatewayId) {
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionGatewayActions.AttachGateways,
OrgPermissionSubjects.Gateway
);
const [gateway] = await gatewayDAL.find({ id: gatewayId, orgId: actor.orgId });
if (!gateway) {
throw new NotFoundError({
message: `Gateway with ID ${gatewayId} not found for org`
});
}
}
await enterpriseAppCheck(
licenseService,
app,
@@ -253,7 +274,8 @@ export const appConnectionServiceFactory = ({
app,
credentials,
method,
orgId: actor.orgId
orgId: actor.orgId,
gatewayId
} as TAppConnectionConfig,
gatewayService
);
@@ -271,6 +293,7 @@ export const appConnectionServiceFactory = ({
encryptedCredentials,
method,
app,
gatewayId,
...params
});
};
@@ -283,7 +306,8 @@ export const appConnectionServiceFactory = ({
app,
orgId: actor.orgId,
credentials: validatedCredentials,
method
method,
gatewayId
} as TAppConnectionConfig,
(platformCredentials) => createConnection(platformCredentials),
gatewayService
@@ -307,7 +331,7 @@ export const appConnectionServiceFactory = ({
};
const updateAppConnection = async (
{ connectionId, credentials, ...params }: TUpdateAppConnectionDTO,
{ connectionId, credentials, gatewayId, ...params }: TUpdateAppConnectionDTO,
actor: OrgServiceActor
) => {
const appConnection = await appConnectionDAL.findById(connectionId);
@@ -334,6 +358,22 @@ export const appConnectionServiceFactory = ({
OrgPermissionSubjects.AppConnections
);
if (gatewayId !== appConnection.gatewayId) {
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionGatewayActions.AttachGateways,
OrgPermissionSubjects.Gateway
);
if (gatewayId) {
const [gateway] = await gatewayDAL.find({ id: gatewayId, orgId: actor.orgId });
if (!gateway) {
throw new NotFoundError({
message: `Gateway with ID ${gatewayId} not found for org`
});
}
}
}
// prevent updating credentials or management status if platform managed
if (appConnection.isPlatformManagedCredentials && (params.isPlatformManagedCredentials === false || credentials)) {
throw new BadRequestError({
@@ -363,7 +403,8 @@ export const appConnectionServiceFactory = ({
app,
orgId: actor.orgId,
credentials,
method
method,
gatewayId
} as TAppConnectionConfig,
gatewayService
);
@@ -385,6 +426,7 @@ export const appConnectionServiceFactory = ({
return appConnectionDAL.updateById(connectionId, {
orgId: actor.orgId,
encryptedCredentials,
gatewayId,
...params
});
};
@@ -401,7 +443,8 @@ export const appConnectionServiceFactory = ({
app,
orgId: actor.orgId,
credentials: updatedCredentials,
method
method,
gatewayId
} as TAppConnectionConfig,
(platformCredentials) => updateConnection(platformCredentials),
gatewayService

View File

@@ -283,7 +283,7 @@ export type TSqlConnectionInput =
export type TCreateAppConnectionDTO = Pick<
TAppConnectionInput,
"credentials" | "method" | "name" | "app" | "description" | "isPlatformManagedCredentials"
"credentials" | "method" | "name" | "app" | "description" | "isPlatformManagedCredentials" | "gatewayId"
>;
export type TUpdateAppConnectionDTO = Partial<Omit<TCreateAppConnectionDTO, "method" | "app">> & {

View File

@@ -26,7 +26,6 @@ export const SanitizedMsSqlConnectionSchema = z.discriminatedUnion("method", [
BaseMsSqlConnectionSchema.extend({
method: z.literal(MsSqlConnectionMethod.UsernameAndPassword),
credentials: MsSqlConnectionAccessTokenCredentialsSchema.pick({
gatewayId: true,
host: true,
database: true,
port: true,

View File

@@ -24,7 +24,6 @@ export const SanitizedMySqlConnectionSchema = z.discriminatedUnion("method", [
BaseMySqlConnectionSchema.extend({
method: z.literal(MySqlConnectionMethod.UsernameAndPassword),
credentials: MySqlConnectionAccessTokenCredentialsSchema.pick({
gatewayId: true,
host: true,
database: true,
port: true,

View File

@@ -24,7 +24,6 @@ export const SanitizedPostgresConnectionSchema = z.discriminatedUnion("method",
BasePostgresConnectionSchema.extend({
method: z.literal(PostgresConnectionMethod.UsernameAndPassword),
credentials: PostgresConnectionAccessTokenCredentialsSchema.pick({
gatewayId: true,
host: true,
database: true,
port: true,

View File

@@ -105,11 +105,11 @@ export const executeWithPotentialGateway = async <T>(
gatewayService: Pick<TGatewayServiceFactory, "fnGetGatewayClientTlsByGatewayId">,
operation: (client: Knex) => Promise<T>
): Promise<T> => {
const { credentials, app } = config;
const { credentials, app, gatewayId } = config;
if (credentials.gatewayId && gatewayService) {
if (gatewayId && gatewayService) {
const [targetHost] = await verifyHostInputValidity(credentials.host, true);
const relayDetails = await gatewayService.fnGetGatewayClientTlsByGatewayId(credentials.gatewayId);
const relayDetails = await gatewayService.fnGetGatewayClientTlsByGatewayId(gatewayId);
const [relayHost, relayPort] = relayDetails.relayAddress.split(":");
return withGatewayProxy(

View File

@@ -3,11 +3,6 @@ import { z } from "zod";
import { AppConnections } from "@app/lib/api-docs";
export const BaseSqlUsernameAndPasswordConnectionSchema = z.object({
gatewayId: z
.union([z.string().uuid(), z.literal("")])
.transform((value) => value || null)
.nullish()
.describe(AppConnections.CREDENTIALS.SQL_CONNECTION.gatewayId),
host: z.string().trim().min(1, "Host required").describe(AppConnections.CREDENTIALS.SQL_CONNECTION.host),
port: z.coerce.number().describe(AppConnections.CREDENTIALS.SQL_CONNECTION.port),
database: z.string().trim().min(1, "Database required").describe(AppConnections.CREDENTIALS.SQL_CONNECTION.database),

View File

@@ -1,6 +1,9 @@
import { DiscriminativePick } from "@app/lib/types";
import { TSqlConnectionInput } from "@app/services/app-connection/app-connection-types";
export type TSqlConnectionConfig = DiscriminativePick<TSqlConnectionInput, "method" | "app" | "credentials"> & {
export type TSqlConnectionConfig = DiscriminativePick<
TSqlConnectionInput,
"method" | "app" | "credentials" | "gatewayId"
> & {
orgId: string;
};

View File

@@ -113,11 +113,20 @@ export type TAvailableAppConnectionsResponse = { appConnections: TAvailableAppCo
export type TCreateAppConnectionDTO = Pick<
TAppConnection,
"name" | "credentials" | "method" | "app" | "description" | "isPlatformManagedCredentials"
| "name"
| "credentials"
| "method"
| "app"
| "description"
| "isPlatformManagedCredentials"
| "gatewayId"
>;
export type TUpdateAppConnectionDTO = Partial<
Pick<TAppConnection, "name" | "credentials" | "description" | "isPlatformManagedCredentials">
Pick<
TAppConnection,
"name" | "credentials" | "description" | "isPlatformManagedCredentials" | "gatewayId"
>
> & {
connectionId: string;
app: AppConnection;

View File

@@ -7,4 +7,5 @@ export type TRootAppConnection = {
createdAt: string;
updatedAt: string;
isPlatformManagedCredentials?: boolean;
gatewayId?: string | null;
};

View File

@@ -1,5 +1,4 @@
export type TBaseSqlConnectionCredentials = {
gatewayId?: string | null;
host: string;
port: number;
username: string;

View File

@@ -6,7 +6,8 @@ import { slugSchema } from "@app/lib/schemas";
export const genericAppConnectionFieldsSchema = z.object({
name: slugSchema({ min: 1, max: 64, field: "Name" }),
description: z.string().trim().max(256, "Description cannot exceed 256 characters").nullish()
description: z.string().trim().max(256, "Description cannot exceed 256 characters").nullish(),
gatewayId: z.string().uuid().nullish()
});
export const GenericAppConnectionsFields = () => {

View File

@@ -58,8 +58,8 @@ export const MsSqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
defaultValues: appConnection ?? {
app: AppConnection.MsSql,
method: MsSqlConnectionMethod.UsernameAndPassword,
gatewayId: null,
credentials: {
gatewayId: "",
host: "",
port: 1433,
database: "default",
@@ -99,37 +99,6 @@ export const MsSqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
}}
>
{!isUpdate && <GenericAppConnectionsFields />}
<Controller
name="method"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
tooltipText={`The method you would like to use to connect with ${
APP_CONNECTION_MAP[AppConnection.MsSql].name
}. This field cannot be changed after creation.`}
errorText={error?.message}
isError={Boolean(error?.message)}
label="Method"
>
<Select
isDisabled={isUpdate}
value={value}
onValueChange={(val) => onChange(val)}
className="w-full border border-mineshaft-500"
position="popper"
dropdownContainerClassName="max-w-none"
>
{Object.values(MsSqlConnectionMethod).map((method) => {
return (
<SelectItem value={method} key={method}>
{getAppConnectionMethodDetails(method).name}{" "}
</SelectItem>
);
})}
</Select>
</FormControl>
)}
/>
<OrgPermissionCan
I={OrgGatewayPermissionActions.AttachGateways}
a={OrgPermissionSubjects.Gateway}
@@ -137,7 +106,7 @@ export const MsSqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
{(isAllowed) => (
<Controller
control={control}
name="credentials.gatewayId"
name="gatewayId"
defaultValue=""
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
@@ -179,6 +148,37 @@ export const MsSqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
/>
)}
</OrgPermissionCan>
<Controller
name="method"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
tooltipText={`The method you would like to use to connect with ${
APP_CONNECTION_MAP[AppConnection.MsSql].name
}. This field cannot be changed after creation.`}
errorText={error?.message}
isError={Boolean(error?.message)}
label="Method"
>
<Select
isDisabled={isUpdate}
value={value}
onValueChange={(val) => onChange(val)}
className="w-full border border-mineshaft-500"
position="popper"
dropdownContainerClassName="max-w-none"
>
{Object.values(MsSqlConnectionMethod).map((method) => {
return (
<SelectItem value={method} key={method}>
{getAppConnectionMethodDetails(method).name}{" "}
</SelectItem>
);
})}
</Select>
</FormControl>
)}
/>
<SqlConnectionFields
isPlatformManagedCredentials={isPlatformManagedCredentials}
selectedTabIndex={selectedTabIndex}

View File

@@ -55,8 +55,8 @@ export const MySqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
defaultValues: appConnection ?? {
app: AppConnection.MySql,
method: MySqlConnectionMethod.UsernameAndPassword,
gatewayId: null,
credentials: {
gatewayId: "",
host: "",
port: 3306,
database: "default",
@@ -96,37 +96,6 @@ export const MySqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
}}
>
{!isUpdate && <GenericAppConnectionsFields />}
<Controller
name="method"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
tooltipText={`The method you would like to use to connect with ${
APP_CONNECTION_MAP[AppConnection.MySql].name
}. This field cannot be changed after creation.`}
errorText={error?.message}
isError={Boolean(error?.message)}
label="Method"
>
<Select
isDisabled={isUpdate}
value={value}
onValueChange={(val) => onChange(val)}
className="w-full border border-mineshaft-500"
position="popper"
dropdownContainerClassName="max-w-none"
>
{Object.values(MySqlConnectionMethod).map((method) => {
return (
<SelectItem value={method} key={method}>
{getAppConnectionMethodDetails(method).name}{" "}
</SelectItem>
);
})}
</Select>
</FormControl>
)}
/>
<OrgPermissionCan
I={OrgGatewayPermissionActions.AttachGateways}
a={OrgPermissionSubjects.Gateway}
@@ -134,7 +103,7 @@ export const MySqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
{(isAllowed) => (
<Controller
control={control}
name="credentials.gatewayId"
name="gatewayId"
defaultValue=""
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
@@ -176,6 +145,37 @@ export const MySqlConnectionForm = ({ appConnection, onSubmit }: Props) => {
/>
)}
</OrgPermissionCan>
<Controller
name="method"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
tooltipText={`The method you would like to use to connect with ${
APP_CONNECTION_MAP[AppConnection.MySql].name
}. This field cannot be changed after creation.`}
errorText={error?.message}
isError={Boolean(error?.message)}
label="Method"
>
<Select
isDisabled={isUpdate}
value={value}
onValueChange={(val) => onChange(val)}
className="w-full border border-mineshaft-500"
position="popper"
dropdownContainerClassName="max-w-none"
>
{Object.values(MySqlConnectionMethod).map((method) => {
return (
<SelectItem value={method} key={method}>
{getAppConnectionMethodDetails(method).name}{" "}
</SelectItem>
);
})}
</Select>
</FormControl>
)}
/>
<SqlConnectionFields
isPlatformManagedCredentials={isPlatformManagedCredentials}
selectedTabIndex={selectedTabIndex}

View File

@@ -55,8 +55,8 @@ export const OracleDBConnectionForm = ({ appConnection, onSubmit }: Props) => {
defaultValues: appConnection ?? {
app: AppConnection.OracleDB,
method: OracleDBConnectionMethod.UsernameAndPassword,
gatewayId: null,
credentials: {
gatewayId: "",
host: "",
port: 1521,
database: "ORCL", // Typically FREEPDB1 or ORCL
@@ -96,37 +96,6 @@ export const OracleDBConnectionForm = ({ appConnection, onSubmit }: Props) => {
}}
>
{!isUpdate && <GenericAppConnectionsFields />}
<Controller
name="method"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
tooltipText={`The method you would like to use to connect with ${
APP_CONNECTION_MAP[AppConnection.OracleDB].name
}. This field cannot be changed after creation.`}
errorText={error?.message}
isError={Boolean(error?.message)}
label="Method"
>
<Select
isDisabled={isUpdate}
value={value}
onValueChange={(val) => onChange(val)}
className="w-full border border-mineshaft-500"
position="popper"
dropdownContainerClassName="max-w-none"
>
{Object.values(OracleDBConnectionMethod).map((method) => {
return (
<SelectItem value={method} key={method}>
{getAppConnectionMethodDetails(method).name}{" "}
</SelectItem>
);
})}
</Select>
</FormControl>
)}
/>
<OrgPermissionCan
I={OrgGatewayPermissionActions.AttachGateways}
a={OrgPermissionSubjects.Gateway}
@@ -134,7 +103,7 @@ export const OracleDBConnectionForm = ({ appConnection, onSubmit }: Props) => {
{(isAllowed) => (
<Controller
control={control}
name="credentials.gatewayId"
name="gatewayId"
defaultValue=""
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
@@ -176,6 +145,37 @@ export const OracleDBConnectionForm = ({ appConnection, onSubmit }: Props) => {
/>
)}
</OrgPermissionCan>
<Controller
name="method"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
tooltipText={`The method you would like to use to connect with ${
APP_CONNECTION_MAP[AppConnection.OracleDB].name
}. This field cannot be changed after creation.`}
errorText={error?.message}
isError={Boolean(error?.message)}
label="Method"
>
<Select
isDisabled={isUpdate}
value={value}
onValueChange={(val) => onChange(val)}
className="w-full border border-mineshaft-500"
position="popper"
dropdownContainerClassName="max-w-none"
>
{Object.values(OracleDBConnectionMethod).map((method) => {
return (
<SelectItem value={method} key={method}>
{getAppConnectionMethodDetails(method).name}{" "}
</SelectItem>
);
})}
</Select>
</FormControl>
)}
/>
<SqlConnectionFields
isPlatformManagedCredentials={isPlatformManagedCredentials}
selectedTabIndex={selectedTabIndex}

View File

@@ -55,8 +55,8 @@ export const PostgresConnectionForm = ({ appConnection, onSubmit }: Props) => {
defaultValues: appConnection ?? {
app: AppConnection.Postgres,
method: PostgresConnectionMethod.UsernameAndPassword,
gatewayId: null,
credentials: {
gatewayId: "",
host: "",
port: 5432,
database: "default",
@@ -96,37 +96,6 @@ export const PostgresConnectionForm = ({ appConnection, onSubmit }: Props) => {
}}
>
{!isUpdate && <GenericAppConnectionsFields />}
<Controller
name="method"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
tooltipText={`The method you would like to use to connect with ${
APP_CONNECTION_MAP[AppConnection.Postgres].name
}. This field cannot be changed after creation.`}
errorText={error?.message}
isError={Boolean(error?.message)}
label="Method"
>
<Select
isDisabled={isUpdate}
value={value}
onValueChange={(val) => onChange(val)}
className="w-full border border-mineshaft-500"
position="popper"
dropdownContainerClassName="max-w-none"
>
{Object.values(PostgresConnectionMethod).map((method) => {
return (
<SelectItem value={method} key={method}>
{getAppConnectionMethodDetails(method).name}{" "}
</SelectItem>
);
})}
</Select>
</FormControl>
)}
/>
<OrgPermissionCan
I={OrgGatewayPermissionActions.AttachGateways}
a={OrgPermissionSubjects.Gateway}
@@ -134,7 +103,7 @@ export const PostgresConnectionForm = ({ appConnection, onSubmit }: Props) => {
{(isAllowed) => (
<Controller
control={control}
name="credentials.gatewayId"
name="gatewayId"
defaultValue=""
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
@@ -176,6 +145,37 @@ export const PostgresConnectionForm = ({ appConnection, onSubmit }: Props) => {
/>
)}
</OrgPermissionCan>
<Controller
name="method"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
tooltipText={`The method you would like to use to connect with ${
APP_CONNECTION_MAP[AppConnection.Postgres].name
}. This field cannot be changed after creation.`}
errorText={error?.message}
isError={Boolean(error?.message)}
label="Method"
>
<Select
isDisabled={isUpdate}
value={value}
onValueChange={(val) => onChange(val)}
className="w-full border border-mineshaft-500"
position="popper"
dropdownContainerClassName="max-w-none"
>
{Object.values(PostgresConnectionMethod).map((method) => {
return (
<SelectItem value={method} key={method}>
{getAppConnectionMethodDetails(method).name}{" "}
</SelectItem>
);
})}
</Select>
</FormControl>
)}
/>
<SqlConnectionFields
isPlatformManagedCredentials={isPlatformManagedCredentials}
selectedTabIndex={selectedTabIndex}

View File

@@ -1,7 +1,6 @@
import { z } from "zod";
export const BaseSqlUsernameAndPasswordConnectionSchema = z.object({
gatewayId: z.string().nullable().optional(),
host: z.string().trim().min(1, "Host required"),
port: z.coerce.number().default(5432),
database: z.string().trim().min(1, "Database required").default("default"),