|
1 | 1 | import { config } from '../config/index.js' |
2 | | -import prisma, { |
3 | | - SQLServerDataSource, |
4 | | - getSQLServerCert, |
5 | | - getSQLServerPassword, |
6 | | -} from '@briefer/database' |
7 | | -import sql from 'mssql' |
8 | | -import { logger } from '../logger.js' |
9 | | -import { DataSourceColumn, DataSourceConnectionError } from '@briefer/types' |
| 2 | +import prisma, { SQLServerDataSource } from '@briefer/database' |
10 | 3 | import { DataSourceStatus } from './index.js' |
11 | | -import { OnTable } from './structure.js' |
12 | | - |
13 | | -type ConnectionConfig = { |
14 | | - user: string |
15 | | - password: string |
16 | | - server: string |
17 | | - database: string |
18 | | - ssl?: { ca: Buffer } |
19 | | -} |
20 | | - |
21 | | -async function getSQLServerConfig( |
22 | | - datasource: SQLServerDataSource |
23 | | -): Promise<ConnectionConfig> { |
24 | | - const password = await getSQLServerPassword( |
25 | | - datasource, |
26 | | - config().DATASOURCES_ENCRYPTION_KEY |
27 | | - ) |
28 | | - |
29 | | - const cert = await getSQLServerCert( |
30 | | - datasource, |
31 | | - config().DATASOURCES_ENCRYPTION_KEY |
32 | | - ) |
33 | | - |
34 | | - return { |
35 | | - user: datasource.username, |
36 | | - password, |
37 | | - database: datasource.database, |
38 | | - server: datasource.host, |
39 | | - ssl: cert ? { ca: cert } : undefined, |
40 | | - } |
41 | | -} |
| 4 | +import { pingSQLServer } from '../python/query/sqlserver.js' |
42 | 5 |
|
43 | 6 | export async function ping( |
44 | | - datasource: SQLServerDataSource |
| 7 | + ds: SQLServerDataSource |
45 | 8 | ): Promise<SQLServerDataSource> { |
46 | 9 | const lastConnection = new Date() |
47 | | - const SQLServerConfig = await getSQLServerConfig(datasource) |
48 | | - |
49 | | - const err = await pingSQLServerFromConfig(SQLServerConfig) |
50 | 10 |
|
| 11 | + const err = await pingSQLServer(ds, config().DATASOURCES_ENCRYPTION_KEY) |
51 | 12 | if (!err) { |
52 | | - return updateConnStatus(datasource, { |
| 13 | + return updateConnStatus(ds, { |
53 | 14 | connStatus: 'online', |
54 | 15 | lastConnection, |
55 | 16 | }) |
56 | 17 | } |
57 | 18 |
|
58 | | - return updateConnStatus(datasource, { connStatus: 'offline', connError: err }) |
59 | | -} |
60 | | - |
61 | | -async function createConnection( |
62 | | - config: ConnectionConfig |
63 | | -): Promise<sql.ConnectionPool> { |
64 | | - const mustEncrypt = config.ssl ? true : false |
65 | | - return sql.connect({ |
66 | | - user: config.user, |
67 | | - password: config.password, |
68 | | - server: config.server, |
69 | | - database: config.database, |
70 | | - options: { |
71 | | - trustServerCertificate: mustEncrypt ? false : true, |
72 | | - encrypt: mustEncrypt, |
73 | | - cryptoCredentialsDetails: { |
74 | | - ca: config.ssl?.ca, |
75 | | - }, |
76 | | - }, |
77 | | - }) |
78 | | -} |
79 | | - |
80 | | -async function pingSQLServerFromConfig( |
81 | | - config: ConnectionConfig |
82 | | -): Promise<DataSourceConnectionError | null> { |
83 | | - try { |
84 | | - const connection = await createConnection(config) |
85 | | - |
86 | | - return await Promise.race([ |
87 | | - new Promise<DataSourceConnectionError>((resolve) => |
88 | | - setTimeout( |
89 | | - () => |
90 | | - resolve({ |
91 | | - name: 'TimeoutError', |
92 | | - message: 'Did not receive response from SQLServer within 10s', |
93 | | - }), |
94 | | - 10000 // 10s timeout |
95 | | - ) |
96 | | - ), |
97 | | - new Promise<DataSourceConnectionError | null>(async (resolve) => { |
98 | | - try { |
99 | | - await connection.query('SELECT 1') |
100 | | - resolve(null) |
101 | | - } catch (err) { |
102 | | - logger().info({ err }, 'Error pinging SQLServer') |
103 | | - const parsedError = DataSourceConnectionError.safeParse(err) |
104 | | - if (!parsedError.success) { |
105 | | - logger().error( |
106 | | - { |
107 | | - error: err, |
108 | | - }, |
109 | | - 'Failed to parse error from SQLServer ping' |
110 | | - ) |
111 | | - resolve({ name: 'UnknownError', message: 'Unknown error' }) |
112 | | - return |
113 | | - } |
114 | | - |
115 | | - resolve(parsedError.data) |
116 | | - } finally { |
117 | | - connection.close() |
118 | | - } |
119 | | - }), |
120 | | - ]) |
121 | | - } catch (err) { |
122 | | - logger().info({ err }, 'Error pinging SQLServer') |
123 | | - const parsedError = DataSourceConnectionError.safeParse(err) |
124 | | - if (!parsedError.success) { |
125 | | - logger().error( |
126 | | - { |
127 | | - error: err, |
128 | | - }, |
129 | | - 'Failed to parse error from SQLServer ping' |
130 | | - ) |
131 | | - return { name: 'UnknownError', message: 'Unknown error' } |
132 | | - } |
133 | | - |
134 | | - return parsedError.data |
135 | | - } |
136 | | -} |
137 | | - |
138 | | -export async function getSQLServerSchemaFromConfig( |
139 | | - datasourceId: string, |
140 | | - sqlServerConfig: ConnectionConfig, |
141 | | - onTable: OnTable |
142 | | -): Promise<void> { |
143 | | - const connection = await createConnection(sqlServerConfig) |
144 | | - |
145 | | - // select all tables with their column names and types from all schemas |
146 | | - const result = await connection.query(` |
147 | | - SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, DATA_TYPE |
148 | | - FROM INFORMATION_SCHEMA.COLUMNS |
149 | | - WHERE TABLE_SCHEMA NOT IN ('information_schema', 'sys', 'master', 'tempdb', 'model', 'msdb'); |
150 | | - `) |
151 | | - await connection.close() |
152 | | - |
153 | | - const schemas: Record< |
154 | | - string, |
155 | | - Record<string, { columns: DataSourceColumn[] }> |
156 | | - > = {} |
157 | | - |
158 | | - for (const row of result.recordset) { |
159 | | - const schemaName = row.TABLE_SCHEMA |
160 | | - const tableName = row.TABLE_NAME |
161 | | - const columnName = row.COLUMN_NAME |
162 | | - const dataType = row.DATA_TYPE |
163 | | - |
164 | | - let schema = schemas[schemaName] |
165 | | - if (!schema) { |
166 | | - schema = {} |
167 | | - schemas[schemaName] = schema |
168 | | - } |
169 | | - |
170 | | - let table = schema[tableName] |
171 | | - if (!table) { |
172 | | - table = { |
173 | | - columns: [], |
174 | | - } |
175 | | - schema[tableName] = table |
176 | | - } |
177 | | - |
178 | | - table.columns.push({ name: columnName, type: dataType }) |
179 | | - } |
180 | | - |
181 | | - for (const [schemaName, schema] of Object.entries(schemas)) { |
182 | | - for (const [tableName, table] of Object.entries(schema)) { |
183 | | - onTable(schemaName, tableName, table, datasourceId) |
184 | | - } |
185 | | - } |
186 | | -} |
187 | | - |
188 | | -export async function getSqlServerSchema( |
189 | | - datasource: SQLServerDataSource, |
190 | | - onTable: OnTable |
191 | | -): Promise<void> { |
192 | | - const SQLServerConfig = await getSQLServerConfig(datasource) |
193 | | - |
194 | | - return getSQLServerSchemaFromConfig(datasource.id, SQLServerConfig, onTable) |
| 19 | + return updateConnStatus(ds, { connStatus: 'offline', connError: err }) |
195 | 20 | } |
196 | 21 |
|
197 | 22 | export async function updateConnStatus( |
|
0 commit comments