feat: tidb cloud serverless tier support (#85)

* feat: tidb cloud serverless tier support

* clearify: check the engine type before call the tidb connection change function

* delete: unused type declare
This commit is contained in:
Cheese
2023-05-12 02:39:00 +00:00
committed by GitHub
parent 4323b19562
commit 700c4bf8d1
16 changed files with 240 additions and 118 deletions

View File

@@ -49,6 +49,7 @@
"react-is": "^18.2.0",
"react-loader-spinner": "^5.3.4",
"react-markdown": "^8.0.6",
"react-svg": "^16.1.11",
"react-textarea-autosize": "^8.4.0",
"react-use": "^17.4.0",
"remark-gfm": "^3.0.1",

73
pnpm-lock.yaml generated
View File

@@ -115,6 +115,9 @@ dependencies:
react-markdown:
specifier: ^8.0.6
version: 8.0.6(@types/react@18.0.28)(react@18.2.0)
react-svg:
specifier: ^16.1.11
version: 16.1.11(react-dom@18.2.0)(react@18.2.0)
react-textarea-autosize:
specifier: ^8.4.0
version: 8.4.0(@types/react@18.0.28)(react@18.2.0)
@@ -476,6 +479,13 @@ packages:
dependencies:
regenerator-runtime: 0.13.11
/@babel/runtime@7.21.5:
resolution: {integrity: sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==}
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: 0.13.11
dev: false
/@babel/template@7.20.7:
resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==}
engines: {node: '>=6.9.0'}
@@ -648,7 +658,7 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
ajv: 6.12.6
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4
espree: 9.5.0
globals: 13.20.0
ignore: 5.2.4
@@ -709,7 +719,7 @@ packages:
engines: {node: '>=10.10.0'}
dependencies:
'@humanwhocodes/object-schema': 1.2.1
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
@@ -1577,6 +1587,14 @@ packages:
tailwindcss: 3.2.7(postcss@8.4.21)(ts-node@10.9.1)
dev: true
/@tanem/svg-injector@10.1.53:
resolution: {integrity: sha512-tR2Kh0GcTk+7hFTUsCo7JcEsAxz7j28dvaeC77jDp+acgGVdWXtk1v6lY8G0v1g813sgBrBzUr3St8RPBSmjEQ==}
dependencies:
'@babel/runtime': 7.21.5
content-type: 1.0.5
tslib: 2.5.0
dev: false
/@tediousjs/connection-string@0.4.2:
resolution: {integrity: sha512-1R9UC7Qc5wief2oJL+c1+d7v1/oPBayL85u8L/jV2DzIKput1TZ8ZUjj2nxQaSfzu210zp0oFWUrYUiUs8NhBQ==}
dev: true
@@ -1745,7 +1763,7 @@ packages:
'@typescript-eslint/scope-manager': 5.55.0
'@typescript-eslint/types': 5.55.0
'@typescript-eslint/typescript-estree': 5.55.0(typescript@4.9.5)
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4
eslint: 8.20.0
typescript: 4.9.5
transitivePeerDependencies:
@@ -1776,7 +1794,7 @@ packages:
dependencies:
'@typescript-eslint/types': 5.55.0
'@typescript-eslint/visitor-keys': 5.55.0
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4
globby: 11.1.0
is-glob: 4.0.3
semver: 7.3.8
@@ -1848,7 +1866,7 @@ packages:
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
engines: {node: '>= 6.0.0'}
dependencies:
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4
transitivePeerDependencies:
- supports-color
dev: true
@@ -2249,6 +2267,11 @@ packages:
engines: {node: '>= 0.6'}
dev: false
/content-type@1.0.5:
resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
engines: {node: '>= 0.6'}
dev: false
/convert-source-map@1.9.0:
resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==}
dev: false
@@ -2343,6 +2366,17 @@ packages:
ms: 2.1.3
dev: true
/debug@4.3.4:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.1.2
/debug@4.3.4(supports-color@5.5.0):
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
@@ -2354,6 +2388,7 @@ packages:
dependencies:
ms: 2.1.2
supports-color: 5.5.0
dev: false
/decode-named-character-reference@1.0.2:
resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==}
@@ -2666,7 +2701,7 @@ packages:
eslint: '*'
eslint-plugin-import: '*'
dependencies:
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4
eslint: 8.20.0
eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.55.0)(eslint-import-resolver-typescript@2.7.1)(eslint@8.20.0)
glob: 7.2.3
@@ -2836,7 +2871,7 @@ packages:
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.3
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4
doctrine: 3.0.0
escape-string-regexp: 4.0.0
eslint-scope: 7.1.1
@@ -3173,6 +3208,7 @@ packages:
/has-flag@3.0.0:
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
engines: {node: '>=4'}
dev: false
/has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
@@ -3263,7 +3299,7 @@ packages:
dependencies:
'@tootallnate/once': 2.0.0
agent-base: 6.0.2
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4
transitivePeerDependencies:
- supports-color
dev: true
@@ -3273,7 +3309,7 @@ packages:
engines: {node: '>= 6'}
dependencies:
agent-base: 6.0.2
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4
transitivePeerDependencies:
- supports-color
dev: true
@@ -4138,7 +4174,7 @@ packages:
resolution: {integrity: sha512-6Mj0yHLdUZjHnOPgr5xfWIMqMWS12zDN6iws9SLuSz76W8jTtAv24MN4/CL7gJrl5vtxGInkkqDv/JIoRsQOvA==}
dependencies:
'@types/debug': 4.1.7
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4
decode-named-character-reference: 1.0.2
micromark-core-commonmark: 1.0.6
micromark-factory-space: 1.0.0
@@ -4205,7 +4241,7 @@ packages:
dependencies:
'@tediousjs/connection-string': 0.4.2
commander: 9.5.0
debug: 4.3.4(supports-color@5.5.0)
debug: 4.3.4
rfdc: 1.3.0
tarn: 3.0.2
tedious: 15.1.3
@@ -4965,6 +5001,20 @@ packages:
tslib: 2.5.0
dev: false
/react-svg@16.1.11(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-Utrm1NATp/1PVML4g97yrFyLvbKZ9HhzFydSsUPuCd8eEYf8llj8sBaYgLO1d5UX5sJ9j2sLgSbOLrGGRhd8NQ==}
peerDependencies:
react: ^16.0.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0
dependencies:
'@babel/runtime': 7.21.5
'@tanem/svg-injector': 10.1.53
'@types/prop-types': 15.7.5
prop-types: 15.8.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/react-syntax-highlighter@15.5.0(react@18.2.0):
resolution: {integrity: sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==}
peerDependencies:
@@ -5483,6 +5533,7 @@ packages:
engines: {node: '>=4'}
dependencies:
has-flag: 3.0.0
dev: false
/supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}

6
public/tidb-cloud.svg Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-20 -20 310 310" stroke="currentColor" fill="transparent" stroke-width="7">
<path id="out_line" d="M202.51,79.58c-23.51-40.94-75.76-55.07-116.7-31.55-22.03,12.65-37.29,34.44-41.64,59.47C13.48,114.6-5.63,145.24,1.49,175.93c5.99,25.86,29.04,44.17,55.58,44.16,1.93,0,3.86-.1,5.78-.29h116.83c1.91,.15,3.83,.29,5.78,.29,39.37,0,71.29-31.92,71.29-71.29,0-32.8-22.38-61.37-54.23-69.22h0Z"/>
<path id="t_line" d="M128.29,81.27l-44.16,25.5v25.49l22.09-12.75v51.32l22.08,12.73h0V106.75l22.08-12.75-22.08-12.74Z"/>
<path id="i_line" d="M150.5,119.64v51.17l22.17-12.8v-51.19l-22.17,12.81Z"/>
</svg>

After

Width:  |  Height:  |  Size: 723 B

View File

@@ -231,6 +231,7 @@ const CreateConnectionModal = (props: Props) => {
{ value: Engine.MySQL, label: "MySQL" },
{ value: Engine.PostgreSQL, label: "PostgreSQL" },
{ value: Engine.MSSQL, label: "MSSQL" },
{ value: Engine.TiDBServerless, label: "TiDB Serverless Tier" },
]}
onValueChange={(value) =>
setPartialConnection({ engineType: value as Engine })
@@ -300,114 +301,123 @@ const CreateConnectionModal = (props: Props) => {
onChange={(value) => setPartialConnection({ password: value })}
/>
</div>
<div className="w-full flex flex-col">
<label className="block text-sm font-medium text-gray-700 mb-1">
SSL
</label>
<div className="w-full flex flex-row justify-start items-start flex-wrap">
{SSLTypeOptions.map((option) => (
<label
key={option.value}
className="w-auto flex flex-row justify-start items-center cursor-pointer mr-3 mb-3"
>
<input
type="radio"
className="radio w-4 h-4 mr-1"
value={option.value}
checked={sslType === option.value}
onChange={(e) => setSSLType(e.target.value as SSLType)}
/>
<span className="text-sm">{option.label}</span>
</label>
))}
{connection.engineType === Engine.TiDBServerless ? (
<div className="w-full flex flex-col">
<label className="block text-sm font-medium text-gray-700 mb-1">
{t("connection.tidb-serverless-ssl-hint")}
</label>
</div>
{sslType !== "none" && (
<>
<div className="text-sm space-x-3 mb-2">
<span
className={`leading-6 pb-1 border-b-2 border-transparent cursor-pointer opacity-60 hover:opacity-80 ${
selectedSSLField === "ca" &&
"!border-indigo-600 !opacity-100"
} `}
onClick={() => setSelectedSSLField("ca")}
) : (
<div className="w-full flex flex-col">
<label className="block text-sm font-medium text-gray-700 mb-1">
SSL
</label>
<div className="w-full flex flex-row justify-start items-start flex-wrap">
{SSLTypeOptions.map((option) => (
<label
key={option.value}
className="w-auto flex flex-row justify-start items-center cursor-pointer mr-3 mb-3"
>
CA Certificate
</span>
{sslType === "full" && (
<>
<span
className={`leading-6 pb-1 border-b-2 border-transparent cursor-pointer opacity-60 hover:opacity-80 ${
selectedSSLField === "key" &&
"!border-indigo-600 !opacity-100"
}`}
onClick={() => setSelectedSSLField("key")}
>
Client Key
</span>
<span
className={`leading-6 pb-1 border-b-2 border-transparent cursor-pointer opacity-60 hover:opacity-80 ${
selectedSSLField === "cert" &&
"!border-indigo-600 !opacity-100"
}`}
onClick={() => setSelectedSSLField("cert")}
>
Client Certificate
</span>
</>
)}
</div>
<div className="w-full h-auto relative">
<TextareaAutosize
className="w-full border resize-none rounded-lg text-sm p-3"
minRows={3}
maxRows={3}
value={
(connection.ssl && connection.ssl[selectedSSLField]) ?? ""
}
onChange={handleSSLValueChange}
/>
<div
className={`${
connection.ssl &&
connection.ssl[selectedSSLField] &&
"hidden"
} absolute top-3 left-4 text-gray-400 text-sm leading-6 pointer-events-none`}
>
<span className="">Input or </span>
<label className="pointer-events-auto border border-dashed px-2 py-1 rounded-lg cursor-pointer hover:border-gray-600 hover:text-gray-600">
upload file
<input
type="radio"
className="radio w-4 h-4 mr-1"
value={option.value}
checked={sslType === option.value}
onChange={(e) => setSSLType(e.target.value as SSLType)}
/>
<span className="text-sm">{option.label}</span>
</label>
))}
</div>
{sslType !== "none" && (
<>
<div className="text-sm space-x-3 mb-2">
<span
className={`leading-6 pb-1 border-b-2 border-transparent cursor-pointer opacity-60 hover:opacity-80 ${
selectedSSLField === "ca" &&
"!border-indigo-600 !opacity-100"
} `}
onClick={() => setSelectedSSLField("ca")}
>
CA Certificate
</span>
{sslType === "full" && (
<>
<span
className={`leading-6 pb-1 border-b-2 border-transparent cursor-pointer opacity-60 hover:opacity-80 ${
selectedSSLField === "key" &&
"!border-indigo-600 !opacity-100"
}`}
onClick={() => setSelectedSSLField("key")}
>
Client Key
</span>
<span
className={`leading-6 pb-1 border-b-2 border-transparent cursor-pointer opacity-60 hover:opacity-80 ${
selectedSSLField === "cert" &&
"!border-indigo-600 !opacity-100"
}`}
onClick={() => setSelectedSSLField("cert")}
>
Client Certificate
</span>
</>
)}
</div>
<div className="w-full h-auto relative">
<TextareaAutosize
className="w-full border resize-none rounded-lg text-sm p-3"
minRows={3}
maxRows={3}
value={
(connection.ssl && connection.ssl[selectedSSLField]) ?? ""
}
onChange={handleSSLValueChange}
/>
<div
className={`${
connection.ssl &&
connection.ssl[selectedSSLField] &&
"hidden"
} absolute top-3 left-4 text-gray-400 text-sm leading-6 pointer-events-none`}
>
<span className="">Input or </span>
<label className="pointer-events-auto border border-dashed px-2 py-1 rounded-lg cursor-pointer hover:border-gray-600 hover:text-gray-600">
upload file
<input
className="hidden"
type="file"
onChange={handleSSLFileInputChange}
/>
</label>
</div>
</div>
</>
)}
{connection.engineType === Engine.MSSQL && (
<div className="w-full flex flex-col">
<label className="block text-sm font-medium text-gray-700 mb-1">
Encrypt
</label>
<div className="w-full flex flex-row justify-start items-start flex-wrap">
<label className="flex items-center">
<input
className="hidden"
type="file"
onChange={handleSSLFileInputChange}
type="checkbox"
className="form-checkbox h-4 w-4 text-indigo-600 transition duration-150 ease-in-out"
checked={connection.encrypt}
onChange={(e) =>
setPartialConnection({ encrypt: e.target.checked })
}
/>
<span className="ml-2 text-sm">Encrypt connection</span>
</label>
</div>
</div>
</>
)}
{connection.engineType === Engine.MSSQL && (
<div className="w-full flex flex-col">
<label className="block text-sm font-medium text-gray-700 mb-1">
Encrypt
</label>
<div className="w-full flex flex-row justify-start items-start flex-wrap">
<label className="flex items-center">
<input
type="checkbox"
className="form-checkbox h-4 w-4 text-indigo-600 transition duration-150 ease-in-out"
checked={connection.encrypt}
onChange={(e) =>
setPartialConnection({ encrypt: e.target.checked })
}
/>
<span className="ml-2 text-sm">Encrypt connection</span>
</label>
</div>
</div>
)}
</div>
)}
</div>
)}
</div>
<div className="modal-action w-full flex flex-row justify-between items-center space-x-2">
<div>
{isEditing && (

View File

@@ -15,6 +15,8 @@ const EngineIcon = (props: Props) => {
return <Icon.DiPostgresql className={className} />;
} else if (engine === Engine.MSSQL) {
return <Icon.DiMsqlServer className={className} />;
} else if (engine === Engine.TiDBServerless) {
return <Icon.TiDBCloudIcon className={className}/>;
} else {
return <Icon.DiDatabase className={className} />;
}

View File

@@ -6,6 +6,12 @@ import * as Fi from "react-icons/fi";
import * as Gi from "react-icons/gi";
import * as Io from "react-icons/io";
import * as Io5 from "react-icons/io5";
import {IconType, IconBaseProps} from "react-icons/lib";
import {ReactSVG} from "react-svg";
const TiDBCloudIcon: IconType = (props: IconBaseProps) => (
<ReactSVG src="tidb-cloud.svg" className={props.className} />
);
const Icon = {
...Ai,
@@ -16,6 +22,7 @@ const Icon = {
...Gi,
...Io,
...Io5,
TiDBCloudIcon
};
// Icon is a collection of all icons from react-icons.

View File

@@ -34,7 +34,8 @@
"port": "Port",
"database-name": "Database name",
"username": "Username",
"password": "Password"
"password": "Password",
"tidb-serverless-ssl-hint": "SSL is required and configured"
},
"assistant": {
"self": "Bot",

View File

@@ -32,7 +32,8 @@
"port": "Puerto",
"database-name": "Nombre de Base de Datos",
"username": "Usuario",
"password": "Contraseña"
"password": "Contraseña",
"tidb-serverless-ssl-hint": "Se requiere SSL y ya está configurado"
},
"assistant": {
"self": "Bot",

View File

@@ -34,7 +34,8 @@
"port": "端口",
"database-name": "数据库",
"username": "用户名",
"password": "密码"
"password": "密码",
"tidb-serverless-ssl-hint": "必需SSL且已配置"
},
"assistant": {
"self": "机器人",

View File

@@ -1,6 +1,8 @@
import { NextApiRequest, NextApiResponse } from "next";
import { newConnector } from "@/lib/connectors";
import { Connection } from "@/types";
import { changeTiDBConnectionToMySQL } from "@/utils";
import { Engine } from "@/types/connection";
// POST /api/connection/db
// req body: { connection: Connection }
@@ -10,7 +12,11 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
return res.status(405).json([]);
}
const connection = req.body.connection as Connection;
let connection = req.body.connection as Connection;
if (connection.engineType === Engine.TiDBServerless) {
connection = changeTiDBConnectionToMySQL(connection);
}
try {
const connector = newConnector(connection);
const databaseNameList = await connector.getDatabases();

View File

@@ -1,6 +1,8 @@
import { NextApiRequest, NextApiResponse } from "next";
import { newConnector } from "@/lib/connectors";
import { Connection, Table } from "@/types";
import { changeTiDBConnectionToMySQL } from "@/utils";
import { Engine } from "@/types/connection";
// POST /api/connection/db_schema
// req body: { connection: Connection, db: string }
@@ -10,7 +12,11 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
return res.status(405).json([]);
}
const connection = req.body.connection as Connection;
let connection = req.body.connection as Connection;
if (connection.engineType === Engine.TiDBServerless) {
connection = changeTiDBConnectionToMySQL(connection);
}
const db = req.body.db as string;
try {
const connector = newConnector(connection);

View File

@@ -1,6 +1,8 @@
import { NextApiRequest, NextApiResponse } from "next";
import { newConnector } from "@/lib/connectors";
import { Connection } from "@/types";
import { changeTiDBConnectionToMySQL } from "@/utils";
import { Engine } from "@/types/connection";
// POST /api/connection/execute
// req body: { connection: Connection, db: string, statement: string }
@@ -9,7 +11,10 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
return res.status(405).json(false);
}
const connection = req.body.connection as Connection;
let connection = req.body.connection as Connection;
if (connection.engineType === Engine.TiDBServerless) {
connection = changeTiDBConnectionToMySQL(connection);
}
const db = req.body.db as string;
const statement = req.body.statement as string;

View File

@@ -1,6 +1,8 @@
import { NextApiRequest, NextApiResponse } from "next";
import { newConnector } from "@/lib/connectors";
import { Connection } from "@/types";
import { changeTiDBConnectionToMySQL } from "@/utils";
import { Engine } from "@/types/connection";
// POST /api/connection/test
// req body: { connection: Connection }
@@ -10,7 +12,10 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
return res.status(405).json(false);
}
const connection = req.body.connection as Connection;
let connection = req.body.connection as Connection;
if (connection.engineType === Engine.TiDBServerless) {
connection = changeTiDBConnectionToMySQL(connection);
}
try {
const connector = newConnector(connection);
await connector.testConnection();

View File

@@ -4,12 +4,15 @@ export enum Engine {
MySQL = "MYSQL",
PostgreSQL = "POSTGRESQL",
MSSQL = "MSSQL",
TiDBServerless = "TiDBServerless",
}
export interface SSLOptions {
ca?: string;
cert?: string;
key?: string;
minVersion?: string;
rejectUnauthorized?: boolean;
}
export interface Connection {

View File

@@ -4,3 +4,4 @@ export * from "./sql";
export * from "./execution";
export * from "./model";
export * from "./feature";
export * from "./tidb";

16
src/utils/tidb.ts Normal file
View File

@@ -0,0 +1,16 @@
import { Connection } from "@/types";
import { Engine } from "@/types/connection";
export const changeTiDBConnectionToMySQL = (connection: Connection) => {
if (connection.engineType === Engine.TiDBServerless) {
return {
...connection,
engineType: Engine.MySQL,
ssl: {
minVersion: 'TLSv1.2',
rejectUnauthorized: true
}
};
}
return connection;
}