diff --git a/frontend/src/pages/Admin/Agents/SQLConnectorSelection/NewConnectionModal.jsx b/frontend/src/pages/Admin/Agents/SQLConnectorSelection/NewConnectionModal.jsx index 1d781894ce7..e2a8b313298 100644 --- a/frontend/src/pages/Admin/Agents/SQLConnectorSelection/NewConnectionModal.jsx +++ b/frontend/src/pages/Admin/Agents/SQLConnectorSelection/NewConnectionModal.jsx @@ -11,6 +11,7 @@ function assembleConnectionString({ host = "", port = "", database = "", + encrypt = false, }) { if ([username, password, host, database].every((i) => !!i) === false) return `Please fill out all the fields above.`; @@ -20,7 +21,7 @@ function assembleConnectionString({ case "mysql": return `mysql://${username}:${password}@${host}:${port}/${database}`; case "sql-server": - return `mssql://${username}:${password}@${host}:${port}/${database}`; + return `mssql://${username}:${password}@${host}:${port}/${database}?encrypt=${encrypt}`; default: return null; } @@ -33,6 +34,7 @@ const DEFAULT_CONFIG = { host: null, port: null, database: null, + encrypt: false, }; export default function NewSQLConnection({ isOpen, closeModal, onSubmit }) { @@ -54,6 +56,7 @@ export default function NewSQLConnection({ isOpen, closeModal, onSubmit }) { host: form.get("host").trim(), port: form.get("port").trim(), database: form.get("database").trim(), + encrypt: form.get("encrypt") === "true", }); } @@ -226,6 +229,26 @@ export default function NewSQLConnection({ isOpen, closeModal, onSubmit }) { spellCheck={false} /> + + {engine === "sql-server" && ( +
+ +
+ )} +

{assembleConnectionString({ engine, ...config })}

diff --git a/server/__tests__/utils/SQLConnectors/connectionParser.test.js b/server/__tests__/utils/SQLConnectors/connectionParser.test.js new file mode 100644 index 00000000000..b984391ddf0 --- /dev/null +++ b/server/__tests__/utils/SQLConnectors/connectionParser.test.js @@ -0,0 +1,178 @@ +/* eslint-env jest */ +const { ConnectionStringParser } = require("../../../utils/agents/aibitat/plugins/sql-agent/SQLConnectors/utils"); + +describe("ConnectionStringParser", () => { + describe("Basic Parsing", () => { + test("should parse a basic connection string without options", () => { + const parser = new ConnectionStringParser({ scheme: "mssql" }); + const result = parser.parse("mssql://user:pass@localhost:1433/mydb"); + + expect(result).toEqual({ + scheme: "mssql", + username: "user", + password: "pass", + hosts: [{ host: "localhost", port: 1433 }], + endpoint: "mydb", + options: undefined + }); + }); + + test("should parse a connection string with options", () => { + const parser = new ConnectionStringParser({ scheme: "mssql" }); + const result = parser.parse("mssql://user:pass@localhost:1433/mydb?encrypt=true&trustServerCertificate=true"); + + expect(result).toEqual({ + scheme: "mssql", + username: "user", + password: "pass", + hosts: [{ host: "localhost", port: 1433 }], + endpoint: "mydb", + options: { + encrypt: "true", + trustServerCertificate: "true" + } + }); + }); + + test("should handle empty passwords", () => { + const parser = new ConnectionStringParser({ scheme: "mssql" }); + const result = parser.parse("mssql://user@localhost:1433/mydb"); + + expect(result).toEqual({ + scheme: "mssql", + username: "user", + password: undefined, + hosts: [{ host: "localhost", port: 1433 }], + endpoint: "mydb", + options: undefined + }); + }); + }); + + describe("Error Handling", () => { + test("should throw error for invalid scheme", () => { + const parser = new ConnectionStringParser({ scheme: "mssql" }); + expect(() => parser.parse("mysql://user:pass@localhost:3306/mydb")) + .toThrow("URI must start with 'mssql://'"); + }); + + test("should throw error for missing scheme", () => { + const parser = new ConnectionStringParser({ scheme: "mssql" }); + expect(() => parser.parse("user:pass@localhost:1433/mydb")) + .toThrow("No scheme found in URI"); + }); + }); + + describe("Special Characters", () => { + test("should handle special characters in username and password", () => { + const parser = new ConnectionStringParser({ scheme: "mssql" }); + const result = parser.parse("mssql://user%40domain:p%40ssw%3Ard@localhost:1433/mydb"); + + expect(result).toEqual({ + scheme: "mssql", + username: "user@domain", + password: "p@ssw:rd", + hosts: [{ host: "localhost", port: 1433 }], + endpoint: "mydb", + options: undefined + }); + }); + + test("should handle special characters in database name", () => { + const parser = new ConnectionStringParser({ scheme: "mssql" }); + const result = parser.parse("mssql://user:pass@localhost:1433/my%20db"); + + expect(result).toEqual({ + scheme: "mssql", + username: "user", + password: "pass", + hosts: [{ host: "localhost", port: 1433 }], + endpoint: "my db", + options: undefined + }); + }); + }); + + describe("Multiple Hosts", () => { + test("should parse multiple hosts", () => { + const parser = new ConnectionStringParser({ scheme: "mssql" }); + const result = parser.parse("mssql://user:pass@host1:1433,host2:1434/mydb"); + + expect(result).toEqual({ + scheme: "mssql", + username: "user", + password: "pass", + hosts: [ + { host: "host1", port: 1433 }, + { host: "host2", port: 1434 } + ], + endpoint: "mydb", + options: undefined + }); + }); + + test("should handle hosts without ports", () => { + const parser = new ConnectionStringParser({ scheme: "mssql" }); + const result = parser.parse("mssql://user:pass@host1,host2/mydb"); + + expect(result).toEqual({ + scheme: "mssql", + username: "user", + password: "pass", + hosts: [ + { host: "host1" }, + { host: "host2" } + ], + endpoint: "mydb", + options: undefined + }); + }); + }); + + describe("Provider-Specific Tests", () => { + test("should parse MySQL connection string", () => { + const parser = new ConnectionStringParser({ scheme: "mysql" }); + const result = parser.parse("mysql://user:pass@localhost:3306/mydb?ssl=true"); + + expect(result).toEqual({ + scheme: "mysql", + username: "user", + password: "pass", + hosts: [{ host: "localhost", port: 3306 }], + endpoint: "mydb", + options: { ssl: "true" } + }); + }); + + test("should parse PostgreSQL connection string", () => { + const parser = new ConnectionStringParser({ scheme: "postgresql" }); + const result = parser.parse("postgresql://user:pass@localhost:5432/mydb?sslmode=require"); + + expect(result).toEqual({ + scheme: "postgresql", + username: "user", + password: "pass", + hosts: [{ host: "localhost", port: 5432 }], + endpoint: "mydb", + options: { sslmode: "require" } + }); + }); + + test("should parse MSSQL connection string with encryption options", () => { + const parser = new ConnectionStringParser({ scheme: "mssql" }); + const result = parser.parse("mssql://user:pass@localhost:1433/mydb?encrypt=true&trustServerCertificate=true"); + + expect(result).toEqual({ + scheme: "mssql", + username: "user", + password: "pass", + hosts: [{ host: "localhost", port: 1433 }], + endpoint: "mydb", + options: { + encrypt: "true", + trustServerCertificate: "true" + } + }); + }); + }); +}); \ No newline at end of file diff --git a/server/utils/agents/aibitat/plugins/sql-agent/SQLConnectors/MSSQL.js b/server/utils/agents/aibitat/plugins/sql-agent/SQLConnectors/MSSQL.js index 153c8c77015..46a88234db6 100644 --- a/server/utils/agents/aibitat/plugins/sql-agent/SQLConnectors/MSSQL.js +++ b/server/utils/agents/aibitat/plugins/sql-agent/SQLConnectors/MSSQL.js @@ -45,6 +45,10 @@ class MSSQLConnector { database: parsed?.endpoint, server: parsed?.hosts?.[0]?.host, port: parsed?.hosts?.[0]?.port, + options: { + ...this.connectionConfig.options, + encrypt: parsed?.options?.encrypt === "true", + }, }; }