@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.
- package/package.json +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.
|
|
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": "
|
|
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
|
-
|
|
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 (
|
|
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,
|
|
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,
|
|
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 += "
|
|
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 += "
|
|
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 + "
|
|
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
|
|
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 + "(
|
|
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
|
|
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
|
|
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 + "
|
|
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
|
|
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 += "}";
|