|
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