@heyhru/business-dms-approval 0.7.0 → 0.7.2

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,9 +1,13 @@
1
- export declare function listApprovals(filters?: {
1
+ interface ApprovalFilters {
2
2
  status?: string;
3
3
  submittedBy?: string;
4
- }): Promise<Record<string, unknown>[]>;
4
+ limit?: number;
5
+ offset?: number;
6
+ }
7
+ export declare function countApprovals(filters?: ApprovalFilters): Promise<number>;
8
+ export declare function listApprovals(filters?: ApprovalFilters): Promise<Record<string, unknown>[]>;
5
9
  export declare function getApprovalById(id: string): Promise<Record<string, unknown> | undefined>;
6
- export declare function insertApproval(dataSourceId: string, sqlText: string, submittedBy: string): Promise<Record<string, unknown> | undefined>;
10
+ export declare function insertApproval(dataSourceId: string, dbName: string | null, sqlText: string, submittedBy: string): Promise<Record<string, unknown> | undefined>;
7
11
  export declare function updateReview(id: string, status: string, reviewedBy: string, rejectReason: string | null): Promise<Record<string, unknown> | undefined>;
8
12
  export declare function setExecuting(id: string): Promise<{
9
13
  changes: number;
@@ -11,4 +15,5 @@ export declare function setExecuting(id: string): Promise<{
11
15
  export declare function setExecuteResult(id: string, status: string, result: string): Promise<{
12
16
  changes: number;
13
17
  }>;
18
+ export {};
14
19
  //# sourceMappingURL=approvals.model.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"approvals.model.d.ts","sourceRoot":"","sources":["../src/approvals.model.ts"],"names":[],"mappings":"AAGA,wBAAsB,aAAa,CAAC,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,sCAgBtF;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,gDAEzC;AAED,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,gDAExF;AAED,wBAAgB,YAAY,CAC1B,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,GAAG,IAAI,gDAG5B;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM;;GAEtC;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;GAE1E"}
1
+ {"version":3,"file":"approvals.model.d.ts","sourceRoot":"","sources":["../src/approvals.model.ts"],"names":[],"mappings":"AAGA,UAAU,eAAe;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAgBD,wBAAsB,cAAc,CAAC,OAAO,CAAC,EAAE,eAAe,mBAO7D;AAED,wBAAsB,aAAa,CAAC,OAAO,CAAC,EAAE,eAAe,sCAS5D;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,gDAEzC;AAED,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,gDAE/G;AAED,wBAAgB,YAAY,CAC1B,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,GAAG,IAAI,gDAG5B;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM;;GAEtC;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;GAE1E"}
@@ -1 +1 @@
1
- {"version":3,"file":"approvals.service.d.ts","sourceRoot":"","sources":["../src/approvals.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,YAAY,EAA0B,MAAM,SAAS,CAAC;AAYzF,UAAU,oBAAqB,SAAQ,cAAc;IACnD,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;CACtB;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,oBAAoB,EAAE,KAAK,EAAE,YAAY,kBAQhF;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,kBAKzE;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,oBAAoB,EAAE,KAAK,EAAE,YAAY,kBAQlF;AAkBD,wBAAsB,eAAe,CAAC,GAAG,EAAE,oBAAoB,EAAE,KAAK,EAAE,YAAY,kBAUnF;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,oBAAoB,EAAE,KAAK,EAAE,YAAY,kBAUlF;AAED,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,IACrC,KAAK,oBAAoB,EAAE,OAAO,YAAY,oBAY7D"}
1
+ {"version":3,"file":"approvals.service.d.ts","sourceRoot":"","sources":["../src/approvals.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,YAAY,EAA0B,MAAM,SAAS,CAAC;AAazF,UAAU,oBAAqB,SAAQ,cAAc;IACnD,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;CACtB;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,oBAAoB,EAAE,KAAK,EAAE,YAAY,kBAUhF;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,kBAKzE;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,oBAAoB,EAAE,KAAK,EAAE,YAAY,kBAQlF;AAkBD,wBAAsB,eAAe,CAAC,GAAG,EAAE,oBAAoB,EAAE,KAAK,EAAE,YAAY,kBAUnF;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,oBAAoB,EAAE,KAAK,EAAE,YAAY,kBAUlF;AAED,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,IACrC,KAAK,oBAAoB,EAAE,OAAO,YAAY,oBAY7D"}
@@ -1,5 +1,5 @@
1
1
  export declare const FIND_BY_ID = "\nSELECT *\nFROM approvals\nWHERE id = ?";
2
- export declare const CREATE = "\nINSERT INTO approvals (data_source_id, sql_text, submitted_by)\nVALUES (?, ?, ?)\nRETURNING *";
2
+ export declare const CREATE = "\nINSERT INTO approvals (data_source_id, db_name, sql_text, submitted_by)\nVALUES (?, ?, ?, ?)\nRETURNING *";
3
3
  export declare const UPDATE_REVIEW: () => string;
4
4
  export declare const UPDATE_EXECUTING: () => string;
5
5
  export declare const UPDATE_RESULT: () => string;
@@ -1 +1 @@
1
- {"version":3,"file":"approvals.sql.d.ts","sourceRoot":"","sources":["../src/approvals.sql.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,6CAGV,CAAC;AAEd,eAAO,MAAM,MAAM,oGAGP,CAAC;AAEb,eAAO,MAAM,aAAa,cAId,CAAC;AAEb,eAAO,MAAM,gBAAgB,cAGhB,CAAC;AAEd,eAAO,MAAM,aAAa,cAGb,CAAC"}
1
+ {"version":3,"file":"approvals.sql.d.ts","sourceRoot":"","sources":["../src/approvals.sql.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,6CAGV,CAAC;AAEd,eAAO,MAAM,MAAM,gHAGP,CAAC;AAEb,eAAO,MAAM,aAAa,cAId,CAAC;AAEb,eAAO,MAAM,gBAAgB,cAGhB,CAAC;AAEd,eAAO,MAAM,aAAa,cAGb,CAAC"}
package/dist/index.js CHANGED
@@ -42,8 +42,8 @@ SELECT *
42
42
  FROM approvals
43
43
  WHERE id = ?`;
44
44
  var CREATE = `
45
- INSERT INTO approvals (data_source_id, sql_text, submitted_by)
46
- VALUES (?, ?, ?)
45
+ INSERT INTO approvals (data_source_id, db_name, sql_text, submitted_by)
46
+ VALUES (?, ?, ?, ?)
47
47
  RETURNING *`;
48
48
  var UPDATE_REVIEW = () => `
49
49
  UPDATE approvals
@@ -60,28 +60,42 @@ SET status = ?, execute_result = ?, updated_at = NOW()
60
60
  WHERE id = ?`;
61
61
 
62
62
  // src/approvals.model.ts
63
- async function listApprovals(filters) {
64
- let query = `SELECT a.*, u.username AS submitter_name
65
- FROM approvals a
66
- LEFT JOIN users u ON u.id = a.submitted_by
67
- WHERE 1=1`;
63
+ function buildWhereParams(filters) {
64
+ const clauses = [];
68
65
  const params = [];
69
66
  if (filters?.status) {
70
- query += " AND a.status = ?";
67
+ clauses.push("a.status = ?");
71
68
  params.push(filters.status);
72
69
  }
73
70
  if (filters?.submittedBy) {
74
- query += " AND a.submitted_by = ?";
71
+ clauses.push("a.submitted_by = ?");
75
72
  params.push(filters.submittedBy);
76
73
  }
77
- query += " ORDER BY a.created_at DESC";
74
+ return { where: clauses.length ? " AND " + clauses.join(" AND ") : "", params };
75
+ }
76
+ async function countApprovals(filters) {
77
+ const { where, params } = buildWhereParams(filters);
78
+ const row = await (0, import_server_plugin_pg.getPgDb)().queryOne(
79
+ `SELECT COUNT(*) AS total FROM approvals a WHERE 1=1` + where,
80
+ params
81
+ );
82
+ return Number(row?.["total"] ?? 0);
83
+ }
84
+ async function listApprovals(filters) {
85
+ let query = `SELECT a.*, u.username AS submitter_name
86
+ FROM approvals a
87
+ LEFT JOIN users u ON u.id = a.submitted_by
88
+ WHERE 1=1`;
89
+ const { where, params } = buildWhereParams(filters);
90
+ query += where + " ORDER BY a.created_at DESC LIMIT ? OFFSET ?";
91
+ params.push(filters?.limit ?? 50, filters?.offset ?? 0);
78
92
  return (0, import_server_plugin_pg.getPgDb)().query(query, params);
79
93
  }
80
94
  function getApprovalById(id) {
81
95
  return (0, import_server_plugin_pg.getPgDb)().queryOne(FIND_BY_ID, [id]);
82
96
  }
83
- function insertApproval(dataSourceId, sqlText, submittedBy) {
84
- return (0, import_server_plugin_pg.getPgDb)().queryOne(CREATE, [dataSourceId, sqlText, submittedBy]);
97
+ function insertApproval(dataSourceId, dbName, sqlText, submittedBy) {
98
+ return (0, import_server_plugin_pg.getPgDb)().queryOne(CREATE, [dataSourceId, dbName, sqlText, submittedBy]);
85
99
  }
86
100
  function updateReview(id, status, reviewedBy, rejectReason) {
87
101
  return (0, import_server_plugin_pg.getPgDb)().queryOne(UPDATE_REVIEW(), [status, reviewedBy, rejectReason, id]);
@@ -95,13 +109,15 @@ function setExecuteResult(id, status, result) {
95
109
 
96
110
  // src/approvals.service.ts
97
111
  async function approvalList(req, reply) {
98
- const { status, mine } = req.body ?? {};
99
- return reply.send(
100
- await listApprovals({
101
- status,
102
- submittedBy: mine === "true" ? req.user.id : void 0
103
- })
104
- );
112
+ const { status, mine, limit, offset } = req.body ?? {};
113
+ const filters = {
114
+ status,
115
+ submittedBy: mine === "true" ? req.user.id : void 0,
116
+ limit: limit ? Number(limit) : void 0,
117
+ offset: offset ? Number(offset) : void 0
118
+ };
119
+ const [rows, total] = await Promise.all([listApprovals(filters), countApprovals(filters)]);
120
+ return reply.send({ rows, total });
105
121
  }
106
122
  async function approvalGet(req, reply) {
107
123
  const { id } = req.body ?? {};
@@ -110,11 +126,11 @@ async function approvalGet(req, reply) {
110
126
  return reply.send(approval);
111
127
  }
112
128
  async function approvalCreate(req, reply) {
113
- const { dataSourceId, sql } = req.body ?? {};
129
+ const { dataSourceId, database, sql } = req.body ?? {};
114
130
  if (!dataSourceId || !sql) {
115
131
  return reply.code(400).send({ error: "Data source ID and SQL are required" });
116
132
  }
117
- const approval = await insertApproval(dataSourceId, sql, req.user.id);
133
+ const approval = await insertApproval(dataSourceId, database ?? null, sql, req.user.id);
118
134
  req.log.info("Approval submitted (user=%s)", req.user.id);
119
135
  return reply.code(201).send(approval);
120
136
  }
@@ -170,19 +186,21 @@ async function doExecuteApproval({ id, userId, ip, encryptionKey }, log) {
170
186
  }
171
187
  await setExecuting(id);
172
188
  try {
173
- const ds = await (0, import_business_dms_datasource.getDataSourceWithPassword)(
174
- approval["data_source_id"],
175
- encryptionKey
176
- );
177
- if (!ds) throw new Error("Data source not found");
178
- const pool = (0, import_business_dms_datasource.getPool)({ ...ds, database: ds.database ?? "" });
189
+ const dataSourceId = approval["data_source_id"];
190
+ const dbName = approval["db_name"];
191
+ const pool = dbName ? await (0, import_business_dms_datasource.getPoolForDatabase)(dataSourceId, dbName, encryptionKey) : await (async () => {
192
+ const ds = await (0, import_business_dms_datasource.getDataSourceWithPassword)(dataSourceId, encryptionKey);
193
+ if (!ds) throw new Error("Data source not found");
194
+ return (0, import_business_dms_datasource.getPool)({ ...ds, database: ds.database ?? "" });
195
+ })();
196
+ if (!pool) throw new Error("Data source not found");
179
197
  const rows = await pool.execute(approval["sql_text"]);
180
198
  const result = `${rows.length} rows affected`;
181
199
  await setExecuteResult(id, "executed", result);
182
200
  log.info("Approval executed (id=%s, user=%s)", id, userId);
183
201
  await (0, import_business_dms_audit.writeAuditLog)({
184
202
  userId,
185
- dataSourceId: approval["data_source_id"],
203
+ dataSourceId,
186
204
  action: "DML_EXECUTE",
187
205
  sqlText: approval["sql_text"],
188
206
  resultSummary: result,
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/approvals.service.ts
2
- import { getDataSourceWithPassword, getPool } from "@heyhru/business-dms-datasource";
2
+ import { getDataSourceWithPassword, getPool, getPoolForDatabase } from "@heyhru/business-dms-datasource";
3
3
  import { writeAuditLog } from "@heyhru/business-dms-audit";
4
4
 
5
5
  // src/approvals.model.ts
@@ -11,8 +11,8 @@ SELECT *
11
11
  FROM approvals
12
12
  WHERE id = ?`;
13
13
  var CREATE = `
14
- INSERT INTO approvals (data_source_id, sql_text, submitted_by)
15
- VALUES (?, ?, ?)
14
+ INSERT INTO approvals (data_source_id, db_name, sql_text, submitted_by)
15
+ VALUES (?, ?, ?, ?)
16
16
  RETURNING *`;
17
17
  var UPDATE_REVIEW = () => `
18
18
  UPDATE approvals
@@ -29,28 +29,42 @@ SET status = ?, execute_result = ?, updated_at = NOW()
29
29
  WHERE id = ?`;
30
30
 
31
31
  // src/approvals.model.ts
32
- async function listApprovals(filters) {
33
- let query = `SELECT a.*, u.username AS submitter_name
34
- FROM approvals a
35
- LEFT JOIN users u ON u.id = a.submitted_by
36
- WHERE 1=1`;
32
+ function buildWhereParams(filters) {
33
+ const clauses = [];
37
34
  const params = [];
38
35
  if (filters?.status) {
39
- query += " AND a.status = ?";
36
+ clauses.push("a.status = ?");
40
37
  params.push(filters.status);
41
38
  }
42
39
  if (filters?.submittedBy) {
43
- query += " AND a.submitted_by = ?";
40
+ clauses.push("a.submitted_by = ?");
44
41
  params.push(filters.submittedBy);
45
42
  }
46
- query += " ORDER BY a.created_at DESC";
43
+ return { where: clauses.length ? " AND " + clauses.join(" AND ") : "", params };
44
+ }
45
+ async function countApprovals(filters) {
46
+ const { where, params } = buildWhereParams(filters);
47
+ const row = await getPgDb().queryOne(
48
+ `SELECT COUNT(*) AS total FROM approvals a WHERE 1=1` + where,
49
+ params
50
+ );
51
+ return Number(row?.["total"] ?? 0);
52
+ }
53
+ async function listApprovals(filters) {
54
+ let query = `SELECT a.*, u.username AS submitter_name
55
+ FROM approvals a
56
+ LEFT JOIN users u ON u.id = a.submitted_by
57
+ WHERE 1=1`;
58
+ const { where, params } = buildWhereParams(filters);
59
+ query += where + " ORDER BY a.created_at DESC LIMIT ? OFFSET ?";
60
+ params.push(filters?.limit ?? 50, filters?.offset ?? 0);
47
61
  return getPgDb().query(query, params);
48
62
  }
49
63
  function getApprovalById(id) {
50
64
  return getPgDb().queryOne(FIND_BY_ID, [id]);
51
65
  }
52
- function insertApproval(dataSourceId, sqlText, submittedBy) {
53
- return getPgDb().queryOne(CREATE, [dataSourceId, sqlText, submittedBy]);
66
+ function insertApproval(dataSourceId, dbName, sqlText, submittedBy) {
67
+ return getPgDb().queryOne(CREATE, [dataSourceId, dbName, sqlText, submittedBy]);
54
68
  }
55
69
  function updateReview(id, status, reviewedBy, rejectReason) {
56
70
  return getPgDb().queryOne(UPDATE_REVIEW(), [status, reviewedBy, rejectReason, id]);
@@ -64,13 +78,15 @@ function setExecuteResult(id, status, result) {
64
78
 
65
79
  // src/approvals.service.ts
66
80
  async function approvalList(req, reply) {
67
- const { status, mine } = req.body ?? {};
68
- return reply.send(
69
- await listApprovals({
70
- status,
71
- submittedBy: mine === "true" ? req.user.id : void 0
72
- })
73
- );
81
+ const { status, mine, limit, offset } = req.body ?? {};
82
+ const filters = {
83
+ status,
84
+ submittedBy: mine === "true" ? req.user.id : void 0,
85
+ limit: limit ? Number(limit) : void 0,
86
+ offset: offset ? Number(offset) : void 0
87
+ };
88
+ const [rows, total] = await Promise.all([listApprovals(filters), countApprovals(filters)]);
89
+ return reply.send({ rows, total });
74
90
  }
75
91
  async function approvalGet(req, reply) {
76
92
  const { id } = req.body ?? {};
@@ -79,11 +95,11 @@ async function approvalGet(req, reply) {
79
95
  return reply.send(approval);
80
96
  }
81
97
  async function approvalCreate(req, reply) {
82
- const { dataSourceId, sql } = req.body ?? {};
98
+ const { dataSourceId, database, sql } = req.body ?? {};
83
99
  if (!dataSourceId || !sql) {
84
100
  return reply.code(400).send({ error: "Data source ID and SQL are required" });
85
101
  }
86
- const approval = await insertApproval(dataSourceId, sql, req.user.id);
102
+ const approval = await insertApproval(dataSourceId, database ?? null, sql, req.user.id);
87
103
  req.log.info("Approval submitted (user=%s)", req.user.id);
88
104
  return reply.code(201).send(approval);
89
105
  }
@@ -139,19 +155,21 @@ async function doExecuteApproval({ id, userId, ip, encryptionKey }, log) {
139
155
  }
140
156
  await setExecuting(id);
141
157
  try {
142
- const ds = await getDataSourceWithPassword(
143
- approval["data_source_id"],
144
- encryptionKey
145
- );
146
- if (!ds) throw new Error("Data source not found");
147
- const pool = getPool({ ...ds, database: ds.database ?? "" });
158
+ const dataSourceId = approval["data_source_id"];
159
+ const dbName = approval["db_name"];
160
+ const pool = dbName ? await getPoolForDatabase(dataSourceId, dbName, encryptionKey) : await (async () => {
161
+ const ds = await getDataSourceWithPassword(dataSourceId, encryptionKey);
162
+ if (!ds) throw new Error("Data source not found");
163
+ return getPool({ ...ds, database: ds.database ?? "" });
164
+ })();
165
+ if (!pool) throw new Error("Data source not found");
148
166
  const rows = await pool.execute(approval["sql_text"]);
149
167
  const result = `${rows.length} rows affected`;
150
168
  await setExecuteResult(id, "executed", result);
151
169
  log.info("Approval executed (id=%s, user=%s)", id, userId);
152
170
  await writeAuditLog({
153
171
  userId,
154
- dataSourceId: approval["data_source_id"],
172
+ dataSourceId,
155
173
  action: "DML_EXECUTE",
156
174
  sqlText: approval["sql_text"],
157
175
  resultSummary: result,
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.7.0",
6
+ "version": "0.7.2",
7
7
  "description": "DMS approval workflow domain logic: service, model, sql",
8
8
  "main": "./dist/index.js",
9
9
  "module": "./dist/index.mjs",
@@ -26,7 +26,7 @@
26
26
  "clean": "rm -rf dist"
27
27
  },
28
28
  "dependencies": {
29
- "@heyhru/business-dms-audit": "0.6.1",
29
+ "@heyhru/business-dms-audit": "0.6.2",
30
30
  "@heyhru/business-dms-datasource": "0.8.1",
31
31
  "@heyhru/server-plugin-pg": "0.7.0",
32
32
  "fastify": "^5.8.4"
@@ -36,5 +36,5 @@
36
36
  "typescript": "^6.0.2",
37
37
  "vitest": "^4.1.4"
38
38
  },
39
- "gitHead": "af454f60f3d63c4bcc171b7d1981e60e9babfb21"
39
+ "gitHead": "d06a1293647cb401ff35e86252c9e96154a7b070"
40
40
  }