@andymic/pigeon 2.0.0 → 2.1.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.
Files changed (2) hide show
  1. package/package.json +2 -2
  2. package/src/index.js +31 -25
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andymic/pigeon",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "author": "Andreas Michael <ateasm03@gmail.com>",
5
5
  "description": "Pigeon is a TypeScript-based tool for generating TypeScript classes and methods from PostgreSQL database schemas.",
6
6
  "keywords": [
@@ -35,5 +35,5 @@
35
35
  "pg": "^8.16.0",
36
36
  "prompt-sync": "^4.2.0"
37
37
  },
38
- "gitHead": "85641ef9cc7fe7b42c78f7470608793f192b880a"
38
+ "gitHead": "029bee420b59d84dfb269ec7eb3190aee486de10"
39
39
  }
package/src/index.js CHANGED
@@ -272,19 +272,21 @@ export function runGeneration(dir, db, tables, enums) {
272
272
  }
273
273
  for (const table of tables) {
274
274
  let ts = "";
275
- let hasForeign = false;
275
+ const imports = [];
276
276
  for (const column of table.columns) {
277
277
  if (column.isForeign && column.foreignColumn && column.foreignTable && column.foreignSchema) {
278
- hasForeign = true;
279
278
  if ((table.schema === column.foreignSchema) && (table.name === column.foreignTable))
280
279
  continue;
280
+ if (imports.includes(column.foreignSchema + "/" + column.foreignTable))
281
+ continue;
281
282
  ts += "import {get" + nameBeautifier(column.foreignTable).replaceAll(" ", "") + "} from \".";
282
283
  if (table.schema !== column.foreignSchema)
283
284
  ts += "./" + column.foreignSchema;
284
285
  ts += "/" + column.foreignTable + ".js\";\n";
286
+ imports.push(column.foreignSchema + "/" + column.foreignTable);
285
287
  }
286
288
  }
287
- if (hasForeign)
289
+ if (imports.length !== 0)
288
290
  ts += "\n";
289
291
  ts += "import query from \"./index.js\";\n\n";
290
292
  if (enums) {
@@ -326,9 +328,9 @@ export function runGeneration(dir, db, tables, enums) {
326
328
  else if ((column.defaultValue !== null && column.defaultValue.includes("nextval")) || (column.isIdentity && column.identityGeneration === "ALWAYS"))
327
329
  autoGenerated.push(column);
328
330
  }
329
- ts += createAdd(table.schema, table.name, required, optional, autoGenerated, hasForeign);
331
+ ts += createAdd(table.schema, table.name, required, optional, autoGenerated, imports.length !== 0);
330
332
  ts += "\n\n";
331
- ts += createUpdate(table.schema, table.name, table.columns, autoGenerated, hasForeign);
333
+ ts += createUpdate(table.schema, table.name, table.columns, autoGenerated, imports.length !== 0);
332
334
  ts += "\n\n";
333
335
  ts += createDelete(table.schema, table.name, table.columns);
334
336
  fs.writeFileSync(path.join(dir, table.schema, table.name + ".ts"), ts);
@@ -339,7 +341,7 @@ function createIndex(db) {
339
341
  text += "import pg from \"pg\";\n";
340
342
  text += "const { Client, Pool } = pg;\n";
341
343
  text += "\n";
342
- text += "const pool = new Pool({\n";
344
+ text += "export const pool = new Pool({\n";
343
345
  text += "\thost: \"" + db.host + "\",\n";
344
346
  text += "\tport: " + db.port + ",\n";
345
347
  text += "\tdatabase: \"" + db.db + "\",\n";
@@ -350,7 +352,7 @@ function createIndex(db) {
350
352
  text += "\tconnectionTimeoutMillis: 2000\n";
351
353
  text += "});\n";
352
354
  text += "\n";
353
- text += "const client = new Client({\n";
355
+ text += "export const client = new Client({\n";
354
356
  text += "\thost: \"" + db.host + "\",\n";
355
357
  text += "\tport: " + db.port + ",\n";
356
358
  text += "\tdatabase: \"" + db.db + "\",\n";
@@ -358,12 +360,9 @@ function createIndex(db) {
358
360
  text += "\tpassword: \"" + db.pass + "\"\n";
359
361
  text += "});\n";
360
362
  text += "\n";
361
- text += "/*\n";
362
- text += "export default (sql: string, params: any[]) => pool.query(sql, params);\n";
363
- text += "*/\n";
363
+ text += "const poolQuery = (sql: string, params: any[]) => pool.query(sql, params);\n";
364
364
  text += "\n";
365
- text += "/*\n";
366
- text += "export default async (sql: string, params: any[]) => {\n";
365
+ text += "const clientQuery = async (sql: string, params: any[]) => {\n";
367
366
  text += "\ttry {\n";
368
367
  text += "\t\tawait client.connect();\n";
369
368
  text += "\t\treturn await client.query(sql, params);\n";
@@ -373,7 +372,10 @@ function createIndex(db) {
373
372
  text += "\t\tawait client.end();\n";
374
373
  text += "\t}\n";
375
374
  text += "}\n";
376
- text += "*/";
375
+ text += "\n";
376
+ text += "//export default poolQuery;\n";
377
+ text += "\n";
378
+ text += "//export default clientQuery;\n";
377
379
  return text;
378
380
  }
379
381
  function createClass(tableName, columns) {
@@ -473,9 +475,10 @@ function createGet(tableSchema, tableName, columns) {
473
475
  text += " * @param {Partial<" + className + ">} criteria - An object containing properties of the " + className + " to filter by.\n";
474
476
  text += " * Only " + nameBeautifier(tableName).toLowerCase() + " matching all provided criteria will be returned.\n";
475
477
  text += " * An empty object or undefined criteria will fetch all " + className + " objects.\n";
478
+ text += " * @param {Function} [runQuery=query] - The function that executes the database query. This is optional and defaults to the production `query` function. It can be used for transactions when using pools.\n";
476
479
  text += " * @returns {Promise<" + className + "[]>} - A Promise object returning an array of " + nameBeautifier(tableName) + " matching the criteria.\n";
477
480
  text += " */\n";
478
- text += "export async function get" + nameBeautifier(tableName).replaceAll(" ", "") + "(criteria: Partial<" + className + ">): Promise<" + className + "[]> {\n";
481
+ text += "export async function get" + nameBeautifier(tableName).replaceAll(" ", "") + "(criteria: Partial<" + className + ">, runQuery: Function = query): Promise<" + className + "[]> {\n";
479
482
  text += "\tconst whereClauses: string[] = [];\n";
480
483
  text += "\tconst values: any[] = [];\n";
481
484
  text += "\tlet paramIndex = 1;\n";
@@ -493,7 +496,7 @@ function createGet(tableSchema, tableName, columns) {
493
496
  text += "\t\tsql += \" WHERE \" + whereClauses.join(\" AND \");\n";
494
497
  text += "\tsql += \";\";\n";
495
498
  text += "\n";
496
- text += "\tconst " + varName + "Query = await query(sql, values);\n";
499
+ text += "\tconst " + varName + "Query = await runQuery(sql, values);\n";
497
500
  text += arrayMaker(1, varName, className, columns) + "\n";
498
501
  text += "\treturn " + varName + ";\n";
499
502
  text += "}";
@@ -506,9 +509,9 @@ function createAdd(tableSchema, tableName, required, optional, autoGenerated, ha
506
509
  const functionName = "add" + singularize(nameBeautifier(tableName).replaceAll(" ", ""));
507
510
  const dataType = "Omit<" + className + ", '" + autoGenerated.concat(optional).map((column) => {
508
511
  return column.name;
509
- }).join("\' | \'") + "'> & Partial<Pick<" + className + ", '" + optional.map((column) => {
512
+ }).join("\' | \'") + "'>" + (optional.length > 0 ? " & Partial<Pick<" + className + ", '" + optional.map((column) => {
510
513
  return column.name;
511
- }).join("\' | \'") + "'>>";
514
+ }).join("\' | \'") + "'>>" : "");
512
515
  const editableColumns = required.concat(optional);
513
516
  text += "/**\n";
514
517
  text += " * Adds a new " + singularize(nameBeautifier(tableName)) + " object to the database.\n";
@@ -537,6 +540,7 @@ function createAdd(tableSchema, tableName, required, optional, autoGenerated, ha
537
540
  text += " * - The field" + (optional.length > 1 ? "s" : "") + " `" + optional.map((column) => {
538
541
  return column.name;
539
542
  }).join("`, `") + "` " + (optional.length > 1 ? "are" : "is") + " optional.\n";
543
+ text += " * @param {Function} [runQuery=query] - The function that executes the database query. This is optional and defaults to the production `query` function. It can be used for transactions when using pools.\n";
540
544
  text += " * @returns {Promise<" + className + ">} A Promise returning the newly created " + className + " object.\n";
541
545
  if (hasForeign) {
542
546
  text += " * @throws {Error} An exception in the case of the ";
@@ -547,7 +551,7 @@ function createAdd(tableSchema, tableName, required, optional, autoGenerated, ha
547
551
  text += " not existing in their table, or if other pre-insertion validation fails.\n";
548
552
  }
549
553
  text += " */\n";
550
- text += "export async function " + functionName + "( " + paramName + ": " + dataType.replaceAll("'", "\"") + "): Promise<" + className + "> {\n";
554
+ text += "export async function " + functionName + "(" + paramName + ": " + dataType.replaceAll("'", "\"") + ", runQuery: Function = query): Promise<" + className + "> {\n";
551
555
  if (hasForeign) {
552
556
  for (const column of editableColumns) {
553
557
  if (column.isForeign && column.foreignColumn && column.foreignTable && column.foreignSchema) {
@@ -588,7 +592,7 @@ function createAdd(tableSchema, tableName, required, optional, autoGenerated, ha
588
592
  text += "\t\tthrow new Error(\"No data provided for " + paramName + " creation.\");\n";
589
593
  text += "\n";
590
594
  text += "\tconst sql = \"INSERT INTO " + tableSchema + "." + tableName + " (\" + intoClauses.join(\", \") + \") VALUES (\" + valuesClauses.join(\", \") + \") RETURNING *;\";\n";
591
- text += "\tconst insertQuery = await query(sql, values);\n";
595
+ text += "\tconst insertQuery = await runQuery(sql, values);\n";
592
596
  text += "\treturn new " + className + "(\n";
593
597
  let columns = editableColumns.concat(autoGenerated);
594
598
  columns = [...new Set(columns)];
@@ -606,9 +610,9 @@ function createUpdate(tableSchema, tableName, columns, autoGenerated, hasForeign
606
610
  const className = singularize(nameBeautifier(tableName)).replaceAll(" ", "");
607
611
  const functionName = "update" + nameBeautifier(tableName).replaceAll(" ", "");
608
612
  const criteriaType = "Partial<" + className + ">";
609
- const updatesType = "Partial<Omit<" + className + ", '" + autoGenerated.map((column) => {
613
+ const updatesType = "Partial<" + (autoGenerated.length !== 0 ? "Omit<" + className + ", '" + autoGenerated.map((column) => {
610
614
  return column.name;
611
- }).join("' | '") + "'>>";
615
+ }).join("' | '") + "'>" : className) + ">";
612
616
  text += "/**\n";
613
617
  text += " * Updates existing " + className + " objects in the database based on the specified criteria.\n";
614
618
  text += " *\n";
@@ -618,11 +622,12 @@ function createUpdate(tableSchema, tableName, columns, autoGenerated, hasForeign
618
622
  text += " * An empty object would typically mean updating ALL " + nameBeautifier(tableName) + ", which should be handled with caution or disallowed.\n";
619
623
  text += " * @param {" + updatesType + "} updates - An object containing the new values for the fields to be updated.\n";
620
624
  text += " * Only fields present in this object will be updated.\n";
625
+ text += " * @param {Function} [runQuery=query] - The function that executes the database query. This is optional and defaults to the production `query` function. It can be used for transactions when using pools.\n";
621
626
  text += " * @returns {Promise<" + className + "[]>} A promise that resolves to an array of the updated " + className + " objects.\n";
622
627
  text += " * Returns an empty array if no records matched the criteria or if no rows were updated.\n";
623
628
  text += " * @throws {Error} If updates object is empty" + (hasForeign ? ", or if foreign key checks fail for provided update values" : "") + ".\n";
624
629
  text += " */\n";
625
- text += "export async function update" + nameBeautifier(tableName).replaceAll(" ", "") + "(criteria: " + criteriaType.replaceAll("'", "\"") + ", updates: " + updatesType.replaceAll("'", "\"") + "): Promise<" + className + "[]> {\n";
630
+ text += "export async function update" + nameBeautifier(tableName).replaceAll(" ", "") + "(criteria: " + criteriaType.replaceAll("'", "\"") + ", updates: " + updatesType.replaceAll("'", "\"") + ", runQuery: Function = query): Promise<" + className + "[]> {\n";
626
631
  text += "\tif (Object.keys(updates).length === 0)\n";
627
632
  text += "\t\tthrow new Error(\"No update data provided.\");\n";
628
633
  text += "\n";
@@ -672,7 +677,7 @@ function createUpdate(tableSchema, tableName, columns, autoGenerated, hasForeign
672
677
  text += "\n";
673
678
  text += "\tlet sql = \"UPDATE " + tableSchema + "." + tableName + " SET \" + setClauses.join(\", \") + (whereClauses.length !== 0 ? \" WHERE \" + whereClauses.join(\" AND \") : \"\") + \" RETURNING *;\";\n";
674
679
  text += "\n";
675
- text += "\tconst updateQuery = await query(sql, values);\n";
680
+ text += "\tconst updateQuery = await runQuery(sql, values);\n";
676
681
  text += arrayMaker(1, "update", className, columns) + "\n";
677
682
  text += "\treturn update;\n";
678
683
  text += "}";
@@ -690,9 +695,10 @@ function createDelete(tableSchema, tableName, columns) {
690
695
  text += " * @param {Partial<" + className + ">} criteria - An object containing properties of the " + className + " to filter by for deletion.\n";
691
696
  text += " * An empty object would typically mean deleting ALL " + nameBeautifier(tableName) + ", which should be handled with caution or disallowed.\n";
692
697
  text += " * Only accounts matching all provided criteria will be deleted.\n";
698
+ text += " * @param {Function} [runQuery=query] - The function that executes the database query. This is optional and defaults to the production `query` function. It can be used for transactions when using pools.\n";
693
699
  text += " * @returns {Promise<" + className + "[]>} - A Promise object returning the deleted " + nameBeautifier(tableName) + ".\n";
694
700
  text += " */\n";
695
- text += "export async function " + functionName + "(criteria : Partial<" + className + ">): Promise<" + className + "[]> {\n";
701
+ text += "export async function " + functionName + "(criteria : Partial<" + className + ">, runQuery: Function = query): Promise<" + className + "[]> {\n";
696
702
  text += "\tconst whereClauses: string[] = [];\n";
697
703
  text += "\tconst values: any[] = [];\n";
698
704
  text += "\tlet paramIndex = 1;\n";
@@ -709,7 +715,7 @@ function createDelete(tableSchema, tableName, columns) {
709
715
  text += "\n";
710
716
  text += "\tlet sql = \"DELETE FROM " + tableSchema + "." + tableName + "\" + (whereClauses.length !== 0 ? \" WHERE \" + whereClauses.join(\" AND \") : \"\") + \" RETURNING *;\";\n";
711
717
  text += "\n";
712
- text += "\tconst removeQuery = await query(sql, values);\n";
718
+ text += "\tconst removeQuery = await runQuery(sql, values);\n";
713
719
  text += arrayMaker(1, "remove", className, columns) + "\n";
714
720
  text += "\treturn remove;\n";
715
721
  text += "}";