@dbml/connector 5.5.1 → 5.6.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,311 +0,0 @@
1
- "use strict";
2
- //
3
- // Description:
4
- // Snowflake info schema lacks reference direction for relationships.
5
- // Constraints and uniqueness cannot be auto-generated from Snowflake info schema.
6
- // Snowflake does not support ENUMs.
7
- // Snowflake does not support indexes for standard tables.
8
- //
9
- var __importDefault = (this && this.__importDefault) || function (mod) {
10
- return (mod && mod.__esModule) ? mod : { "default": mod };
11
- };
12
- Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.fetchSchemaJson = void 0;
14
- const snowflake_sdk_1 = __importDefault(require("snowflake-sdk"));
15
- const parseSchema_1 = require("../../utils/parseSchema");
16
- const parseConnectionString = (connectionString) => {
17
- const params = {};
18
- const regex = /([^;=]+)=([^;]*)/g;
19
- let match;
20
- while ((match = regex.exec(connectionString)) !== null) {
21
- params[match[1].trim()] = match[2].trim();
22
- }
23
- return params;
24
- };
25
- const connect = async (connection) => {
26
- return new Promise((resolve, reject) => {
27
- console.log('Attempting to connect...');
28
- connection.connect((err) => {
29
- if (err) {
30
- reject(err);
31
- }
32
- else {
33
- resolve();
34
- }
35
- });
36
- });
37
- };
38
- const executeQuery = (connection, sqlText) => {
39
- return new Promise((resolve, reject) => {
40
- console.log('Executing query...');
41
- connection.execute({
42
- sqlText,
43
- complete: (err, stmt, rows) => {
44
- if (err) {
45
- reject(err);
46
- }
47
- else {
48
- console.log('Query executed successfully.');
49
- if (rows) {
50
- resolve(rows);
51
- }
52
- }
53
- },
54
- });
55
- });
56
- };
57
- const isLogLevel = (value) => {
58
- return ['ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE'].includes(value);
59
- };
60
- const connectToSnowflake = async (config) => {
61
- const logLevel = isLogLevel(config.LOG_LEVEL) ? config.LOG_LEVEL : 'INFO';
62
- const isDebugMode = config.IS_DEBUG_MODE === 'true';
63
- snowflake_sdk_1.default.configure({
64
- logLevel,
65
- additionalLogToConsole: isDebugMode,
66
- });
67
- // https://docs.snowflake.com/en/developer-guide/node-js/nodejs-driver-options#authentication-options
68
- let authConfig = {};
69
- switch (config.AUTHENTICATOR) {
70
- case 'SNOWFLAKE_JWT':
71
- // https://docs.snowflake.com/en/developer-guide/node-js/nodejs-driver-authenticate#label-nodejs-key-pair-authentication
72
- // Support private key file
73
- authConfig = {
74
- privateKeyPath: config.PRIVATE_KEY_PATH,
75
- privateKeyPass: config.PASSPHRASE,
76
- };
77
- break;
78
- case 'SNOWFLAKE':
79
- case undefined:
80
- authConfig = {
81
- password: config.PWD,
82
- };
83
- break;
84
- default:
85
- throw new Error('Unsupported authenticator');
86
- }
87
- // "SERVER=myaccount.snowflakecomputing.com;UID=myusername;PWD=mypassword;DATABASE=mydatabase;WAREHOUSE=mywarehouse;ROLE=myrole";
88
- const connectionOptions = {
89
- authenticator: config.AUTHENTICATOR,
90
- account: config.SERVER,
91
- username: config.UID,
92
- database: config.DATABASE,
93
- warehouse: config.WAREHOUSE,
94
- sfRetryMaxLoginRetries: 3,
95
- timeout: 10000,
96
- ...authConfig,
97
- };
98
- const connection = snowflake_sdk_1.default.createConnection(connectionOptions);
99
- // Connect to Snowflake
100
- await connect(connection);
101
- // Execute the query
102
- await executeQuery(connection, 'SELECT CURRENT_VERSION();');
103
- return connection;
104
- };
105
- const convertQueryBoolean = (val) => val === 'YES';
106
- const getFieldType = (data_type, character_maximum_length, numeric_precision, numeric_scale) => {
107
- if (character_maximum_length) {
108
- return `${data_type}(${character_maximum_length})`;
109
- }
110
- if (numeric_precision && numeric_scale) {
111
- return `${data_type}(${numeric_precision},${numeric_scale})`;
112
- }
113
- return data_type;
114
- };
115
- const getDbdefault = (column_default, default_type) => {
116
- if (default_type === 'string') {
117
- return {
118
- type: default_type,
119
- value: column_default.slice(1, -1),
120
- };
121
- }
122
- return {
123
- type: default_type,
124
- value: column_default,
125
- };
126
- };
127
- const generateField = (row) => {
128
- const { COLUMN_NAME: column_name, DATA_TYPE: data_type, CHARACTER_MAXIMUM_LENGTH: character_maximum_length, NUMERIC_PRECISION: numeric_precision, NUMERIC_SCALE: numeric_scale, IDENTITY_INCREMENT: identity_increment, IS_NULLABLE: is_nullable, COLUMN_DEFAULT: column_default, DEFAULT_TYPE: default_type, COLUMN_COMMENT: column_comment, } = row;
129
- const dbdefault = column_default ? getDbdefault(column_default, default_type) : null;
130
- const fieldType = {
131
- type_name: getFieldType(data_type, character_maximum_length, numeric_precision, numeric_scale),
132
- schemaName: null,
133
- };
134
- return {
135
- name: column_name,
136
- type: fieldType,
137
- dbdefault,
138
- not_null: !convertQueryBoolean(is_nullable),
139
- increment: !!identity_increment,
140
- note: column_comment ? { value: column_comment } : { value: '' },
141
- };
142
- };
143
- const generateTablesAndFields = async (conn, schemas) => {
144
- const fields = {};
145
- const schemaSql = schemas.length > 0 ? `AND c.table_schema IN (${schemas.map((schema) => `'${schema}'`).join(',')})` : '';
146
- const tablesAndFieldsSql = `
147
- SELECT
148
- c.table_schema,
149
- c.table_name,
150
- c.column_name,
151
- c.data_type,
152
- c.character_maximum_length,
153
- c.numeric_precision,
154
- c.numeric_scale,
155
- c.is_nullable,
156
- c.column_default,
157
- CASE
158
- WHEN c.column_default IS NULL THEN NULL
159
- WHEN c.column_default LIKE '''%' THEN 'string'
160
- WHEN LOWER(c.column_default) = 'true' OR LOWER(c.column_default) = 'false' THEN 'boolean'
161
- WHEN c.column_default REGEXP '^-?[0-9]+(\\.[0-9]+)?$' THEN 'number'
162
- ELSE 'expression'
163
- END AS default_type,
164
- t.comment AS table_comment,
165
- c.comment AS column_comment,
166
- c.identity_increment
167
- FROM
168
- information_schema.columns c
169
- JOIN information_schema.tables t
170
- ON c.table_name = t.table_name
171
- AND c.table_schema = t.table_schema
172
- WHERE
173
- t.table_type = 'BASE TABLE'
174
- AND t.table_schema NOT IN ('INFORMATION_SCHEMA')
175
- ${schemaSql}
176
- ORDER BY
177
- c.table_schema,
178
- t.created,
179
- c.ordinal_position;
180
- `;
181
- const tablesAndFieldsResult = await executeQuery(conn, tablesAndFieldsSql);
182
- const tables = tablesAndFieldsResult.reduce((acc, row) => {
183
- const { TABLE_SCHEMA: table_schema, TABLE_NAME: table_name, TABLE_COMMENT: table_comment } = row;
184
- const key = `${table_schema}.${table_name}`;
185
- if (!acc[key]) {
186
- acc[key] = {
187
- name: table_name,
188
- schemaName: table_schema,
189
- note: table_comment ? { value: table_comment } : { value: '' },
190
- };
191
- }
192
- if (!fields[key])
193
- fields[key] = [];
194
- const field = generateField(row);
195
- fields[key].push(field);
196
- return acc;
197
- }, {});
198
- return {
199
- tables: Object.values(tables),
200
- fields,
201
- };
202
- };
203
- const createConstraintKeysMap = (keys, schemas, constraintType) => {
204
- return keys.reduce((acc, row) => {
205
- const { schema_name, table_name, column_name, constraint_name, } = row;
206
- const selectedSchema = schemas.length > 0 ? schemas.includes(schema_name) : true;
207
- if (!selectedSchema) {
208
- return acc;
209
- }
210
- const key = `${schema_name}.${table_name}.${constraint_name}`;
211
- if (acc[key]) {
212
- const { columnNames } = acc[key];
213
- columnNames.push(column_name);
214
- return acc;
215
- }
216
- acc[key] = {
217
- schemaName: schema_name,
218
- tableName: table_name,
219
- constraintName: constraint_name,
220
- columnNames: [column_name],
221
- type: '',
222
- };
223
- acc[key][constraintType] = true;
224
- return acc;
225
- }, {});
226
- };
227
- const generateIndexes = async (conn, databaseName, schemas) => {
228
- const getPrimaryKeysSql = `
229
- SHOW PRIMARY KEYS IN DATABASE ${databaseName};
230
- `;
231
- const getUniqueKeysSql = `
232
- SHOW UNIQUE KEYS IN DATABASE ${databaseName};
233
- `;
234
- const primaryKeys = await executeQuery(conn, getPrimaryKeysSql);
235
- const uniqueKeys = await executeQuery(conn, getUniqueKeysSql);
236
- const primaryKeysByConstraint = createConstraintKeysMap(primaryKeys, schemas, 'primary');
237
- const uniqueKeysByConstraint = createConstraintKeysMap(uniqueKeys, schemas, 'unique');
238
- const allConstraints = [Object.values(primaryKeysByConstraint), Object.values(uniqueKeysByConstraint)].flat();
239
- const { indexes, tableConstraints } = allConstraints.reduce((acc, row) => {
240
- const { schemaName, tableName, constraintName, columnNames, type, primary, unique, } = row;
241
- const key = `${schemaName}.${tableName}`;
242
- if (columnNames.length < 2) {
243
- acc.tableConstraints[key] = {
244
- [columnNames[0]]: {
245
- pk: primary,
246
- unique,
247
- checks: [],
248
- },
249
- };
250
- return acc;
251
- }
252
- const index = {
253
- name: constraintName,
254
- type,
255
- unique,
256
- pk: primary,
257
- columns: columnNames.map((columnName) => ({
258
- type: 'column',
259
- value: columnName,
260
- })),
261
- };
262
- if (acc.indexes[key]) {
263
- acc.indexes[key].push(index);
264
- }
265
- else {
266
- acc.indexes[key] = [index];
267
- }
268
- return acc;
269
- }, { indexes: {}, tableConstraints: {} });
270
- return {
271
- indexes,
272
- tableConstraints,
273
- };
274
- };
275
- const fetchSchemaJson = async (connection) => {
276
- const config = parseConnectionString(connection);
277
- const conn = await connectToSnowflake(config);
278
- if (conn instanceof Error) {
279
- throw conn;
280
- }
281
- // Schemas: schema1,schema2,schema3
282
- const schemas = config.SCHEMAS ? (0, parseSchema_1.parseSchema)(config.SCHEMAS) : [];
283
- const databaseName = config.DATABASE;
284
- const tablesAndFieldsRes = generateTablesAndFields(conn, schemas);
285
- const indexesRes = generateIndexes(conn, databaseName, schemas);
286
- const res = await Promise.all([
287
- tablesAndFieldsRes,
288
- indexesRes,
289
- ]);
290
- conn.destroy((err) => {
291
- if (err) {
292
- throw err;
293
- }
294
- else {
295
- console.log('Connection destroyed successfully.');
296
- }
297
- });
298
- const { tables, fields } = res[0];
299
- const { indexes, tableConstraints } = res[1];
300
- return {
301
- tables,
302
- fields,
303
- refs: [],
304
- enums: [],
305
- indexes,
306
- tableConstraints,
307
- // Checks are not supported in snowflake
308
- checks: {},
309
- };
310
- };
311
- exports.fetchSchemaJson = fetchSchemaJson;
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,58 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.loadCredentialFromFile = loadCredentialFromFile;
4
- exports.parseBigQueryCredential = parseBigQueryCredential;
5
- const promises_1 = require("fs/promises");
6
- async function loadCredentialFromFile(filename) {
7
- try {
8
- const data = await (0, promises_1.readFile)(filename, { encoding: 'utf8' });
9
- return data;
10
- }
11
- catch (error) {
12
- if (error.name === 'TypeError' || error.name === 'Error') {
13
- throw new Error(`Load credential error: ${error.message}`);
14
- }
15
- throw error;
16
- }
17
- }
18
- function parseBigQueryCredential(credentialString) {
19
- try {
20
- const credentialJson = JSON.parse(credentialString);
21
- const { project_id: projectId, client_email: clientEmail, private_key: privateKey, datasets, } = credentialJson;
22
- // valid datasets: ['dataset_1', 'dataset_2']
23
- const parsedDatasets = !Array.isArray(datasets)
24
- ? []
25
- : datasets.filter((dataset) => typeof dataset === 'string');
26
- const parsedCredentials = {
27
- datasets: parsedDatasets,
28
- };
29
- // Validate and add projectId if provided
30
- if (projectId !== undefined) {
31
- if (typeof projectId !== 'string' || !projectId || !projectId.trim()) {
32
- throw new Error('project_id must be a non-empty string when provided');
33
- }
34
- parsedCredentials.projectId = projectId;
35
- }
36
- // Validate and add credentials if provided
37
- // Note: If one credential field is provided, both must be provided
38
- if (clientEmail !== undefined || privateKey !== undefined) {
39
- if (!clientEmail || typeof clientEmail !== 'string' || !clientEmail.trim()) {
40
- throw new Error('client_email must be a non-empty string when credentials are provided');
41
- }
42
- if (!privateKey || typeof privateKey !== 'string' || !privateKey.trim()) {
43
- throw new Error('private_key must be a non-empty string when credentials are provided');
44
- }
45
- parsedCredentials.credentials = {
46
- clientEmail,
47
- privateKey,
48
- };
49
- }
50
- return parsedCredentials;
51
- }
52
- catch (error) {
53
- if (error instanceof SyntaxError) {
54
- throw new Error('Credentials must be in JSON format');
55
- }
56
- throw error;
57
- }
58
- }
@@ -1,61 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getIntersection = getIntersection;
4
- exports.getTableSchemaKey = getTableSchemaKey;
5
- exports.mergeTables = mergeTables;
6
- exports.mergeFieldDictionary = mergeFieldDictionary;
7
- exports.mergeIndexDictionary = mergeIndexDictionary;
8
- exports.mergeTableConstraintDictionary = mergeTableConstraintDictionary;
9
- exports.mergeCheckConstraintDictionary = mergeCheckConstraintDictionary;
10
- function getIntersection(firstList, secondList) {
11
- return firstList.filter((item) => secondList.includes(item));
12
- }
13
- function getTableSchemaKey(schemaName, tableName) {
14
- if (!tableName || !tableName.trim()) {
15
- throw new Error('Table name must be a non-empty string');
16
- }
17
- return schemaName ? `${schemaName}.${tableName}` : `${tableName}`;
18
- }
19
- function mergeTables(firstTableList, secondTable) {
20
- return firstTableList.concat(secondTable);
21
- }
22
- function mergeFieldDictionary(firstDict, secondDict) {
23
- const result = { ...firstDict };
24
- Object.keys(secondDict).forEach((key) => {
25
- if (!result[key]) {
26
- result[key] = [];
27
- }
28
- result[key] = result[key].concat(secondDict[key]);
29
- });
30
- return result;
31
- }
32
- function mergeIndexDictionary(firstDict, secondDict) {
33
- const result = { ...firstDict };
34
- Object.keys(secondDict).forEach((key) => {
35
- if (!result[key]) {
36
- result[key] = [];
37
- }
38
- result[key] = result[key].concat(secondDict[key]);
39
- });
40
- return result;
41
- }
42
- function mergeTableConstraintDictionary(firstDict, secondDict) {
43
- const result = { ...firstDict };
44
- Object.keys(secondDict).forEach((key) => {
45
- if (!result[key]) {
46
- result[key] = {};
47
- }
48
- result[key] = secondDict[key];
49
- });
50
- return result;
51
- }
52
- function mergeCheckConstraintDictionary(firstDict, secondDict) {
53
- const result = { ...firstDict };
54
- Object.keys(secondDict).forEach((key) => {
55
- if (!result[key]) {
56
- result[key] = [];
57
- }
58
- result[key] = result[key].concat(secondDict[key]);
59
- });
60
- return result;
61
- }
@@ -1,59 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildSchemaQuery = exports.parseConnectionString = exports.parseJdbcSchema = exports.parseSchema = void 0;
4
- // You cannot use the "=" inside database's schema name. It will be treated as a delimiter.
5
- const SCHEMAS_DELIMITER = 'schemas=';
6
- const haveSchemas = (str) => {
7
- return str.toLowerCase().includes(SCHEMAS_DELIMITER);
8
- };
9
- const getSchemasPart = (str) => {
10
- return str ? str.toLowerCase().split(SCHEMAS_DELIMITER)[1] : '';
11
- };
12
- const noSchemaResult = (connectionString) => ({
13
- connectionString,
14
- schemas: [],
15
- });
16
- // Inputs: schemas string -> Output: schema array string[]
17
- // schema1, schema2 -> ['schema1', 'schema2']
18
- // schema3,schema4 -> ['schema3", 'schema4']
19
- // "schema5,gg", "schema6", schema7 -> ['schema5,gg', 'schema6', 'schema7']
20
- // schema8 -> ['schema8']
21
- const parseSchema = (schemas) => {
22
- if (!schemas)
23
- return [];
24
- const schemasWithDoubleQuotes = schemas.split(/,(?=(?:[^"]*(?:"[^"]*")*)*$)/).map((s) => s.trim());
25
- return schemasWithDoubleQuotes.map((s) => s.replace(/"/g, ''));
26
- };
27
- exports.parseSchema = parseSchema;
28
- const parseOdbcSchema = (connectionString) => {
29
- const connectionParts = connectionString.split(';');
30
- const schemasPart = connectionParts.find((part) => haveSchemas(part));
31
- return {
32
- connectionString: connectionParts.filter((part) => !haveSchemas(part)).join(';'),
33
- schemas: (0, exports.parseSchema)(getSchemasPart(schemasPart)),
34
- };
35
- };
36
- const parseJdbcSchema = (connectionString) => {
37
- const connectionParts = connectionString.split('?');
38
- const connectionArgs = connectionParts[1].split('&');
39
- const schemasPart = connectionArgs.find((part) => haveSchemas(part));
40
- return {
41
- connectionString: connectionParts[0],
42
- schemas: (0, exports.parseSchema)(getSchemasPart(schemasPart)),
43
- };
44
- };
45
- exports.parseJdbcSchema = parseJdbcSchema;
46
- const parseConnectionString = (connectionString, connectionStringType) => {
47
- if (!haveSchemas(connectionString))
48
- return noSchemaResult(connectionString);
49
- return connectionStringType === 'jdbc'
50
- ? (0, exports.parseJdbcSchema)(connectionString)
51
- : parseOdbcSchema(connectionString);
52
- };
53
- exports.parseConnectionString = parseConnectionString;
54
- const buildSchemaQuery = (columnName, schemas, prefix = 'AND') => {
55
- if (schemas.length === 0)
56
- return '';
57
- return `${prefix} ${columnName} IN (${schemas.map((schema) => `'${schema}'`).join(', ')})`;
58
- };
59
- exports.buildSchemaQuery = buildSchemaQuery;
package/jest.config.ts DELETED
@@ -1,18 +0,0 @@
1
- import { type Config } from 'jest';
2
-
3
- const config: Config = {
4
- testMatch: ["**/?(*.)+(spec|test).?([mc])[jt]s?(x)"],
5
- preset: 'ts-jest',
6
- testEnvironment: 'node',
7
- transform: {
8
- '^.+\\.[tj]sx?$': 'babel-jest',
9
- },
10
- moduleNameMapper: {
11
- '\\.(css|less|scss)$': 'identity-obj-proxy',
12
- },
13
- collectCoverage: true,
14
- coverageReporters: ['json', 'json-summary', 'html', 'text'],
15
- coverageDirectory: 'coverage',
16
- };
17
-
18
- export default config;