@quillsql/node 0.3.8 → 0.4.1

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.
@@ -47,11 +47,18 @@ export async function getSchemasPostgres(pool: Pool): Promise<string[]> {
47
47
 
48
48
  export async function getTablesBySchemaPostgres(
49
49
  pool: Pool,
50
- schemaName: string
51
- ): Promise<string[]> {
52
- const sql = `SELECT table_name FROM information_schema.tables WHERE table_schema = '${schemaName}'`;
53
- const results = await runQueryPostgres(sql, pool);
54
- return results.rows.map((row) => row.table_name);
50
+ schemaNames: string[]
51
+ ): Promise<{ tableName: string; schemaName: string }[]> {
52
+ const allColumns = await Promise.all(
53
+ schemaNames.map(async (schema) => {
54
+ const sql = `SELECT table_name, table_schema FROM information_schema.tables WHERE table_schema = '${schema}'`;
55
+ const results = await runQueryPostgres(sql, pool);
56
+ return results.rows.map((row) => {
57
+ return { tableName: row.table_name, schemaName: row.table_schema };
58
+ });
59
+ })
60
+ );
61
+ return allColumns.flat();
55
62
  }
56
63
 
57
64
  export async function getColumnsByTablePostgres(
@@ -81,7 +88,9 @@ export async function getForeignKeysPostgres(
81
88
  let foreignKeysString = results.rows.map((key) => {
82
89
  return key.column_name;
83
90
  });
84
- foreignKeysString = foreignKeysString.filter((key) => key !== "id" && key !== "_id_");
91
+ foreignKeysString = foreignKeysString.filter(
92
+ (key) => key !== "id" && key !== "_id_"
93
+ );
85
94
  foreignKeysString = [...new Set(foreignKeysString)];
86
95
  if (foreignKeysString.length === 0) {
87
96
  sql = `SELECT column_name FROM information_schema.columns
@@ -104,23 +113,23 @@ export async function getForeignKeysPostgres(
104
113
  export async function getSchemaColumnInfoPostgress(
105
114
  pool: Pool,
106
115
  schemaName: string,
107
- tableNames: string[]
116
+ tableNames: { tableName: string; schemaName: string }[]
108
117
  ): Promise<
109
- { tableName: string; columns: { columnName: string; dataTypeId: number }[] }[]
118
+ { tableName: string; columns: { columnName: string; dataTypeID: number }[] }[]
110
119
  > {
111
120
  const allColumns = await Promise.all(
112
121
  tableNames.map(async (tableName) => {
113
122
  const query = `
114
123
  SELECT column_name as "columnName", udt_name as "fieldType"
115
124
  FROM information_schema.columns
116
- WHERE table_schema = '${schemaName}'
117
- AND table_name = '${tableName}'
125
+ WHERE table_schema = '${tableName.schemaName}'
126
+ AND table_name = '${tableName.tableName}'
118
127
  ORDER BY ordinal_position;
119
128
  `;
120
129
  const results = await runQueryPostgres(query, pool);
121
130
  return {
122
- tableName,
123
- displayName: tableName,
131
+ tableName: `${tableName.schemaName}.${tableName.tableName}`,
132
+ displayName: `${tableName.schemaName}.${tableName.tableName}`,
124
133
  columns: results.rows.map((row) => {
125
134
  let pgType = PG_TYPES.find((pgType) => {
126
135
  return pgType.typname === row.fieldType;
@@ -131,7 +140,7 @@ export async function getSchemaColumnInfoPostgress(
131
140
  return {
132
141
  columnName: row.columnName,
133
142
  displayName: row.columnName,
134
- dataTypeId: pgType,
143
+ dataTypeID: pgType,
135
144
  fieldType: row.fieldType,
136
145
  };
137
146
  }),
@@ -66,11 +66,23 @@ export async function getSchemasSnowflake(
66
66
 
67
67
  export async function getTablesBySchemaSnowflake(
68
68
  connection: snowflake.Connection,
69
- schema: string
70
- ): Promise<string[]> {
71
- const sql = `SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '${schema}'`;
72
- const results = await runQuerySnowflake(sql, connection);
73
- return results.rows.map((row) => row.TABLE_NAME);
69
+ schemaNames: string[]
70
+ ): Promise<{ tableName: string; schemaName: string }[]> {
71
+ const allColumns = await Promise.all(
72
+ schemaNames.map(async (schema) => {
73
+ const query = `SELECT
74
+ TABLE_NAME as "tableName",
75
+ TABLE_SCHEMA as "schemaName"
76
+ FROM INFORMATION_SCHEMA.TABLES
77
+ WHERE TABLE_SCHEMA = '${schema}';
78
+ `;
79
+ const results = await runQuerySnowflake(query, connection);
80
+ return results.rows.map((row) => {
81
+ return { tableName: row.tableName, schemaName: row.schemaName };
82
+ });
83
+ })
84
+ );
85
+ return allColumns.flat();
74
86
  }
75
87
 
76
88
  export async function getColumnsByTableSnowflake(
@@ -162,25 +174,25 @@ export async function getForeignKeysSnowflake(
162
174
  export async function getSchemaColumnInfoSnowflake(
163
175
  connection: snowflake.Connection,
164
176
  schemaName: string,
165
- tableNames: string[]
177
+ tableNames: { tableName: string; schemaName: string }[]
166
178
  ) {
167
179
  const allColumns = await Promise.all(
168
180
  tableNames.map(async (tableName) => {
169
181
  const query = `SELECT
170
182
  COLUMN_NAME as "columnName", DATA_TYPE as "dataType"
171
183
  FROM INFORMATION_SCHEMA.COLUMNS
172
- WHERE TABLE_SCHEMA = '${schemaName}' AND TABLE_NAME = '${tableName}';
184
+ WHERE TABLE_SCHEMA = '${tableName.schemaName}' AND TABLE_NAME = '${tableName.tableName}';
173
185
  `;
174
186
  const results = await runQuerySnowflake(query, connection);
175
187
  return {
176
- tableName,
177
- displayName: tableName,
188
+ tableName: `${tableName.schemaName}.${tableName.tableName}`,
189
+ displayName: `${tableName.schemaName}.${tableName.tableName}`,
178
190
  columns: results.rows.map((row) => {
179
191
  const postgresType = POSTGRES_SNOWFLAKE_MAP[row.dataType];
180
192
  return {
181
193
  columnName: row.columnName,
182
194
  displayName: row.columnName,
183
- dataTypeId: postgresType,
195
+ dataTypeID: postgresType,
184
196
  fieldType: row.dataType,
185
197
  };
186
198
  }),
package/src/index.ts CHANGED
@@ -8,12 +8,7 @@ import {
8
8
  import { CachedConnection } from "./db/CachedConnection";
9
9
  import axios from "axios";
10
10
  import "dotenv/config";
11
- import {
12
- getTableSchema,
13
- mapQueries,
14
- removeFields,
15
- } from "./utils/RunQueryProcesses";
16
- import { PG_TYPES } from "./assets/pgtypes";
11
+ import { mapQueries, removeFields } from "./utils/RunQueryProcesses";
17
12
  import {
18
13
  DatabaseType,
19
14
  connectToDatabase,
@@ -25,6 +20,7 @@ import {
25
20
  getTablesBySchemaByDatabase,
26
21
  runQueryByDatabase,
27
22
  } from "./db/DatabaseHelper";
23
+ import { convertTypeToPostgres } from "./utils/schemaConversion";
28
24
 
29
25
  const HOST =
30
26
  process.env.ENV === "development"
@@ -47,57 +43,62 @@ export default class QuillClass {
47
43
 
48
44
  constructor(
49
45
  privateKey: string,
50
- databaseConnectionString: string,
51
- cache: Partial<CacheCredentials> = {},
52
46
  databaseType: DatabaseType,
47
+ databaseConnectionString?: string,
48
+ databaseCredentials?: any,
49
+ cache: Partial<CacheCredentials> = {},
53
50
  metadataServerURL?: string
54
51
  ) {
55
52
  this.baseUrl = metadataServerURL ? metadataServerURL : HOST;
56
53
  this.config = { headers: { Authorization: `Bearer ${privateKey}` } };
54
+ let credentials = databaseCredentials;
55
+ if (databaseConnectionString) {
56
+ credentials = getDatabaseCredentials(
57
+ databaseType,
58
+ databaseConnectionString
59
+ );
60
+ }
57
61
  this.targetConnection = new CachedConnection(
58
62
  databaseType,
59
- getDatabaseCredentials(databaseType, databaseConnectionString),
63
+ credentials,
60
64
  cache
61
65
  );
62
66
  }
63
67
 
64
68
  public async query({ orgId, metadata }: QuillQueryParams): Promise<any> {
65
69
  this.targetConnection.orgId = orgId;
66
-
67
70
  try {
68
- // Initial Query Request
69
- const limitedQueries = metadata.preQueries
70
- ? metadata.preQueries?.map((query) => query + " limit 1")
71
- : [];
72
71
  const preQueryResults = metadata.preQueries
73
- ? await this.runQueries(limitedQueries)
74
- : [];
75
- const columns = metadata.preQueries
76
- ? preQueryResults.queryResults[0].fields.map((field: any) => {
77
- return {
78
- fieldType: PG_TYPES.find((type) => field.dataTypeID === type.oid)
79
- ? PG_TYPES.find((type) => field.dataTypeID === type.oid)
80
- ?.typname
81
- : field.dataTypeID,
82
- name: field.name,
83
- displayName: field.name,
84
- isVisible: true,
85
- field: field.name,
86
- };
87
- })
88
- : metadata.columns;
72
+ ? await this.runQueries(
73
+ metadata.preQueries,
74
+ this.targetConnection.databaseType,
75
+ metadata.databaseType,
76
+ metadata.runQueryConfig
77
+ )
78
+ : {};
79
+ if (metadata.runQueryConfig?.overridePost) {
80
+ return {
81
+ data: { queryResults: preQueryResults },
82
+ status: "success",
83
+ };
84
+ }
89
85
  const response = await this.postQuill(metadata.task, {
90
86
  ...metadata,
87
+ ...preQueryResults,
91
88
  orgId,
92
- columns,
93
89
  viewQuery: metadata.preQueries ? metadata.preQueries[0] : undefined,
94
90
  });
91
+ if (response.error) {
92
+ return { status: "error", error: response.error };
93
+ }
95
94
  // if there is no metadata object in the response, create one
96
95
  if (!response.metadata) {
97
96
  response.metadata = {};
98
97
  }
99
98
  const results = await this.runQueries(
100
99
  response.queries,
100
+ this.targetConnection.databaseType,
101
+ metadata.databaseType,
101
102
  response.metadata.runQueryConfig
102
103
  );
103
104
  // QUICK JANKY FIX TO UPDATE METADATA AFTER GETTING MAPPED ARRAYS
@@ -134,37 +135,91 @@ export default class QuillClass {
134
135
 
135
136
  private async runQueries(
136
137
  queries: string[] | undefined,
138
+ pkDatabaseType: DatabaseType,
139
+ databaseType?: string,
137
140
  runQueryConfig?: AdditionalProcessing
138
141
  ) {
139
142
  let results: any;
140
143
  if (!queries) return { ...results, queryResults: [] };
141
- if (!queries) return { ...results, queryResults: [] };
144
+ if (
145
+ databaseType &&
146
+ databaseType.toLowerCase() !== pkDatabaseType.toLowerCase()
147
+ ) {
148
+ return {
149
+ dbMismatched: true,
150
+ backendDatbaseType: pkDatabaseType,
151
+ queryResults: [],
152
+ };
153
+ }
142
154
  if (runQueryConfig?.arrayToMap) {
143
- const mappedArray = await mapQueries(
144
- queries,
145
- runQueryConfig.arrayToMap,
146
- this.targetConnection
147
- );
155
+ const mappedArray = await mapQueries(queries, this.targetConnection);
148
156
  return { ...results, queryResults: [], mappedArray };
157
+ } else if (runQueryConfig?.getColumns) {
158
+ const queryResult = await this.targetConnection.query(
159
+ `${queries[0].replace(/;/, "")} limit 1`
160
+ );
161
+ const columns = queryResult.fields.map((field: any) => {
162
+ return {
163
+ fieldType: convertTypeToPostgres(field.dataTypeID),
164
+ name: field.name,
165
+ displayName: field.name,
166
+ isVisible: true,
167
+ field: field.name,
168
+ };
169
+ });
170
+ return { columns };
171
+ } else if (runQueryConfig?.getTables) {
172
+ const queryResult = await getTablesBySchemaByDatabase(
173
+ this.targetConnection.databaseType,
174
+ this.targetConnection.pool,
175
+ runQueryConfig.schemaNames! || runQueryConfig.schema
176
+ );
177
+ const schemaInfo = await getColumnInfoBySchemaByDatabase(
178
+ this.targetConnection.databaseType,
179
+ this.targetConnection.pool,
180
+ runQueryConfig.schema!,
181
+ queryResult!
182
+ );
183
+ return schemaInfo;
149
184
  } else {
185
+ if (runQueryConfig?.limitThousand) {
186
+ queries = queries.map((query) => {
187
+ return query.replace(/;/, "") + " limit 1000;";
188
+ });
189
+ } else if (runQueryConfig?.limitBy) {
190
+ queries = queries.map((query) => {
191
+ return query.replace(/;/, "") + " limit " + runQueryConfig.limitBy;
192
+ });
193
+ }
150
194
  const queryResults = await Promise.all(
151
195
  queries.map(async (query) => {
152
196
  return await this.targetConnection.query(query);
153
197
  })
154
198
  );
155
199
  results = { ...results, queryResults };
156
- if (runQueryConfig?.getSchema) {
157
- results = {
158
- ...results,
159
- columns: await getTableSchema(queryResults[0], this.targetConnection),
160
- };
161
- }
162
200
  if (runQueryConfig?.removeFields) {
163
201
  results = {
164
202
  ...results,
165
203
  queryResults: removeFields(queryResults, runQueryConfig.removeFields),
166
204
  };
167
205
  }
206
+ if (runQueryConfig?.convertDatatypes) {
207
+ results = queryResults.map((result) => {
208
+ return {
209
+ fields: result.fields.map((field: any) => {
210
+ return {
211
+ ...field,
212
+ fieldType: convertTypeToPostgres(field.dataTypeID),
213
+ isVisible: true,
214
+ field: field.name,
215
+ displayName: field.name,
216
+ name: field.name,
217
+ };
218
+ }),
219
+ rows: result.rows,
220
+ };
221
+ });
222
+ }
168
223
  }
169
224
  return results;
170
225
  }
@@ -189,6 +244,7 @@ export default class QuillClass {
189
244
  const Quill = ({
190
245
  privateKey,
191
246
  databaseConnectionString,
247
+ databaseConfig,
192
248
  cache,
193
249
  databaseType,
194
250
  metadataServerURL,
@@ -197,13 +253,15 @@ const Quill = ({
197
253
  databaseConnectionString: string;
198
254
  cache?: Partial<CacheCredentials>;
199
255
  databaseType: DatabaseType;
256
+ databaseConfig: any;
200
257
  metadataServerURL?: string;
201
258
  }) => {
202
259
  return new QuillClass(
203
260
  privateKey,
261
+ databaseType,
204
262
  databaseConnectionString,
263
+ databaseConfig,
205
264
  cache,
206
- databaseType,
207
265
  metadataServerURL
208
266
  );
209
267
  };
@@ -9,9 +9,9 @@ describe("Quill", () => {
9
9
  beforeEach(() => {
10
10
  quill = new Quill(
11
11
  "dummy_private_key",
12
+ DatabaseType.postgres,
12
13
  "dummy_db_url",
13
14
  {},
14
- DatabaseType.postgres,
15
15
  undefined
16
16
  );
17
17
  quill.targetConnection.query = jest.fn().mockResolvedValue([]);
@@ -7,6 +7,7 @@ export interface QuillRequestMetadata {
7
7
  // a query to be run
8
8
  queries?: string[];
9
9
  preQueries?: string[];
10
+ runQueryConfig?: AdditionalProcessing;
10
11
  query?: string;
11
12
  // a report to be fetched
12
13
  id?: string;
@@ -25,6 +26,7 @@ export interface QuillRequestMetadata {
25
26
  template?: boolean;
26
27
  clientId?: string;
27
28
  deleted?: boolean;
29
+ databaseType?: string;
28
30
  }
29
31
 
30
32
  export interface QuillQueryParams {
@@ -51,12 +53,22 @@ export interface QuillConfig {
51
53
 
52
54
  export interface AdditionalProcessing {
53
55
  getSchema?: boolean;
56
+ getColumns?: boolean;
57
+ getTables?: boolean;
58
+ schema?: string;
59
+ schemaNames?: string[];
60
+ table?: string;
54
61
  removeFields?: string[];
55
62
  arrayToMap?: { arrayName: string; field: string };
63
+ overridePost?: boolean;
64
+ convertDatatypes?: boolean;
65
+ limitThousand?: boolean;
66
+ limitBy?: number;
56
67
  }
57
68
 
58
69
  export interface QuillClientResponse {
59
70
  queries: string[];
60
71
  metadata: any;
61
72
  runQueryConfig: AdditionalProcessing;
73
+ error?: string;
62
74
  }
@@ -7,28 +7,6 @@ interface TableSchemaInfo {
7
7
  isVisible: boolean;
8
8
  }
9
9
 
10
- export async function getTableSchema(
11
- queryResults: any,
12
- targetConnection: CachedConnection
13
- ) {
14
- const typesQuery = await targetConnection.query(
15
- "select typname, oid, typarray from pg_type order by oid;"
16
- );
17
- const schema: TableSchemaInfo[] = queryResults[0].fields.map(
18
- (field: { dataTypeID: any; name: any }) => {
19
- return {
20
- fieldType: typesQuery.rows.filter(
21
- (type: { oid: any }) => field.dataTypeID === type.oid
22
- )[0].typname,
23
- name: field.name,
24
- displayName: field.name,
25
- isVisible: true,
26
- };
27
- }
28
- );
29
- return schema;
30
- }
31
-
32
10
  export function removeFields(queryResults: any, fieldsToRemove: string[]): any {
33
11
  const fields = queryResults.fields.filter((field: { name: any }) =>
34
12
  fieldsToRemove.includes(field.name)
@@ -43,7 +21,6 @@ export function removeFields(queryResults: any, fieldsToRemove: string[]): any {
43
21
 
44
22
  export async function mapQueries(
45
23
  queries: string[],
46
- arrayToMap: { arrayName: string; field: string },
47
24
  targetConnection: CachedConnection
48
25
  ): Promise<any[]> {
49
26
  const mappedArray = [];
@@ -0,0 +1,11 @@
1
+ import { PG_TYPES } from "../assets/pgtypes";
2
+
3
+ export function convertTypeToPostgres(data_type_id: number): string {
4
+ const type = PG_TYPES.find((type) => data_type_id === type.oid)
5
+ ? PG_TYPES.find((type) => data_type_id === type.oid)?.typname
6
+ : undefined;
7
+ if (!type) {
8
+ return "varchar";
9
+ }
10
+ return type;
11
+ }