@quillsql/node 0.3.8 → 0.4.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.
@@ -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,49 +43,49 @@ 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
  });
95
91
  // if there is no metadata object in the response, create one
@@ -98,6 +94,8 @@ export default class QuillClass {
98
94
  }
99
95
  const results = await this.runQueries(
100
96
  response.queries,
97
+ this.targetConnection.databaseType,
98
+ metadata.databaseType,
101
99
  response.metadata.runQueryConfig
102
100
  );
103
101
  // QUICK JANKY FIX TO UPDATE METADATA AFTER GETTING MAPPED ARRAYS
@@ -134,37 +132,87 @@ export default class QuillClass {
134
132
 
135
133
  private async runQueries(
136
134
  queries: string[] | undefined,
135
+ pkDatabaseType: DatabaseType,
136
+ databaseType?: string,
137
137
  runQueryConfig?: AdditionalProcessing
138
138
  ) {
139
139
  let results: any;
140
140
  if (!queries) return { ...results, queryResults: [] };
141
- if (!queries) return { ...results, queryResults: [] };
141
+ if (
142
+ databaseType &&
143
+ databaseType.toLowerCase() !== pkDatabaseType.toLowerCase()
144
+ ) {
145
+ return {
146
+ dbMismatched: true,
147
+ backendDatbaseType: pkDatabaseType,
148
+ queryResults: [],
149
+ };
150
+ }
142
151
  if (runQueryConfig?.arrayToMap) {
143
- const mappedArray = await mapQueries(
144
- queries,
145
- runQueryConfig.arrayToMap,
146
- this.targetConnection
147
- );
152
+ const mappedArray = await mapQueries(queries, this.targetConnection);
148
153
  return { ...results, queryResults: [], mappedArray };
154
+ } else if (runQueryConfig?.getColumns) {
155
+ const queryResult = await this.targetConnection.query(
156
+ `${queries[0]} limit 1`
157
+ );
158
+ const columns = queryResult.fields.map((field: any) => {
159
+ return {
160
+ fieldType: convertTypeToPostgres(field.dataTypeID),
161
+ name: field.name,
162
+ displayName: field.name,
163
+ isVisible: true,
164
+ field: field.name,
165
+ };
166
+ });
167
+ return { columns };
168
+ } else if (runQueryConfig?.getTables) {
169
+ const queryResult = await getTablesBySchemaByDatabase(
170
+ this.targetConnection.databaseType,
171
+ this.targetConnection.pool,
172
+ runQueryConfig.schemaNames! || runQueryConfig.schema
173
+ );
174
+ const schemaInfo = await getColumnInfoBySchemaByDatabase(
175
+ this.targetConnection.databaseType,
176
+ this.targetConnection.pool,
177
+ runQueryConfig.schema!,
178
+ queryResult!
179
+ );
180
+ return schemaInfo;
149
181
  } else {
182
+ if (runQueryConfig?.limitThousand) {
183
+ queries = queries.map((query) => {
184
+ return query.replace(/;/, "") + " limit 1000;";
185
+ });
186
+ }
150
187
  const queryResults = await Promise.all(
151
188
  queries.map(async (query) => {
152
189
  return await this.targetConnection.query(query);
153
190
  })
154
191
  );
155
192
  results = { ...results, queryResults };
156
- if (runQueryConfig?.getSchema) {
157
- results = {
158
- ...results,
159
- columns: await getTableSchema(queryResults[0], this.targetConnection),
160
- };
161
- }
162
193
  if (runQueryConfig?.removeFields) {
163
194
  results = {
164
195
  ...results,
165
196
  queryResults: removeFields(queryResults, runQueryConfig.removeFields),
166
197
  };
167
198
  }
199
+ if (runQueryConfig?.convertDatatypes) {
200
+ results = queryResults.map((result) => {
201
+ return {
202
+ fields: result.fields.map((field: any) => {
203
+ return {
204
+ ...field,
205
+ fieldType: convertTypeToPostgres(field.dataTypeID),
206
+ isVisible: true,
207
+ field: field.name,
208
+ displayName: field.name,
209
+ name: field.name,
210
+ };
211
+ }),
212
+ rows: result.rows,
213
+ };
214
+ });
215
+ }
168
216
  }
169
217
  return results;
170
218
  }
@@ -189,6 +237,7 @@ export default class QuillClass {
189
237
  const Quill = ({
190
238
  privateKey,
191
239
  databaseConnectionString,
240
+ databaseConfig,
192
241
  cache,
193
242
  databaseType,
194
243
  metadataServerURL,
@@ -197,13 +246,15 @@ const Quill = ({
197
246
  databaseConnectionString: string;
198
247
  cache?: Partial<CacheCredentials>;
199
248
  databaseType: DatabaseType;
249
+ databaseConfig: any;
200
250
  metadataServerURL?: string;
201
251
  }) => {
202
252
  return new QuillClass(
203
253
  privateKey,
254
+ databaseType,
204
255
  databaseConnectionString,
256
+ databaseConfig,
205
257
  cache,
206
- databaseType,
207
258
  metadataServerURL
208
259
  );
209
260
  };
@@ -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,8 +53,16 @@ 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;
56
66
  }
57
67
 
58
68
  export interface QuillClientResponse {
@@ -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
+ }