• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vzakharchenko / forge-sql-orm / 18980807892

31 Oct 2025 05:48PM UTC coverage: 80.264% (+3.6%) from 76.616%
18980807892

push

github

vzakharchenko
added dev Observability

457 of 643 branches covered (71.07%)

Branch coverage included in aggregate %.

22 of 35 new or added lines in 6 files covered. (62.86%)

13 existing lines in 2 files now uncovered.

942 of 1100 relevant lines covered (85.64%)

16.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.96
/src/utils/forgeDriver.ts
1
import { sql, UpdateQueryResponse } from "@forge/sql";
2
import { saveMetaDataToContext } from "./metadataContextUtils";
3
import { getOperationType } from "./requestTypeContextUtils";
4
import {withTimeout} from "./sqlUtils";
5

6
const timeoutMs =10000;
3✔
7
const timeoutMessage = `Atlassian @forge/sql did not return a response within ${timeoutMs}ms (${timeoutMs / 1000} seconds), so the request is blocked. Possible causes: slow query, network issues, or exceeding Forge SQL limits.`;
3✔
8

9
/**
10
 * Metadata structure for Forge SQL query results.
11
 * Contains execution timing, response size, and field information.
12
 */
13
export type ForgeSQLMetadata = {
14
  dbExecutionTime: number;
15
  responseSize: number;
16
  fields: {
17
    catalog: string;
18
    name: string;
19
    schema: string;
20
    characterSet: number;
21
    decimals: number;
22
    table: string;
23
    orgTable: string;
24
    orgName: string;
25
    flags: number;
26
    columnType: number;
27
    columnLength: number;
28
  }[];
29
};
30

31
/**
32
 * Result structure for Forge SQL queries.
33
 * Contains rows data and execution metadata.
34
 */
35
export interface ForgeSQLResult {
36
  rows: Record<string, unknown>[] | Record<string, unknown>;
37
  metadata: ForgeSQLMetadata;
38
}
39

40
/**
41
 * Driver result structure for Drizzle ORM compatibility.
42
 */
43
export interface ForgeDriverResult {
44
  rows: unknown[];
45
  insertId?: number;
46
  affectedRows?: number;
47
}
48

49
/**
50
 * Query execution method types.
51
 */
52
export type QueryMethod = "all" | "execute";
53

54
/**
55
 * Type guard to check if an object is an UpdateQueryResponse.
56
 *
57
 * @param obj - The object to check
58
 * @returns True if the object is an UpdateQueryResponse
59
 */
60
export function isUpdateQueryResponse(obj: unknown): obj is UpdateQueryResponse {
61
  return (
2✔
62
    obj !== null &&
8✔
63
    typeof obj === "object" &&
64
    typeof (obj as any).affectedRows === "number" &&
65
    typeof (obj as any).insertId === "number"
66
  );
67
}
68

69
function inlineParams(sql: string, params: unknown[]): string {
70
  let i = 0;
2✔
71
  return sql.replace(/\?/g, () => {
2✔
72
    const val = params[i++];
×
73
    if (val === null) return "NULL";
×
74
    if (typeof val === "number") return val.toString();
×
75
    return `'${String(val).replace(/'/g, "''")}'`;
×
76
  });
77
}
78

79
/**
80
 * Processes DDL query results and saves metadata.
81
 *
82
 * @param result - The DDL query result
83
 * @returns Processed result for Drizzle ORM
84
 */
85
async function processDDLResult(method: QueryMethod, result: any): Promise<ForgeDriverResult> {
86
  if (result.metadata) {
2!
87
    await saveMetaDataToContext(result.metadata as ForgeSQLMetadata);
2✔
88
  }
89

90
  if (!result?.rows) {
2!
91
    return { rows: [] };
×
92
  }
93

94
  if (isUpdateQueryResponse(result.rows)) {
2!
95
    const oneRow = result.rows as any;
2✔
96
    return { ...oneRow, rows: [oneRow] };
2✔
97
  }
98

99
  if (Array.isArray(result.rows)) {
×
100
    if (method === "execute") {
×
101
      return { rows: [result.rows] };
×
102
    } else {
103
      const rows = (result.rows as any[]).map((r) => Object.values(r as Record<string, unknown>));
×
104
      return { rows };
×
105
    }
106
  }
107

108
  return { rows: [] };
×
109
}
110

111
/**
112
 * Processes execute method results (UPDATE, INSERT, DELETE).
113
 *
114
 * @param query - The SQL query
115
 * @param params - Query parameters
116
 * @returns Processed result for Drizzle ORM
117
 */
118
async function processExecuteMethod(query: string, params: unknown[]): Promise<ForgeDriverResult> {
119
  const sqlStatement = sql.prepare<UpdateQueryResponse>(query);
45✔
120

121
  if (params) {
45!
122
    sqlStatement.bindParams(...params);
45✔
123
  }
124

125
  const result = await withTimeout(sqlStatement.execute(), timeoutMessage, timeoutMs);
45✔
126
  await saveMetaDataToContext(result.metadata as ForgeSQLMetadata);
42✔
127
  if (!result.rows) {
42!
128
    return { rows: [[]] };
×
129
  }
130

131
  return { rows: [result.rows] };
42✔
132
}
133

134
/**
135
 * Processes all method results (SELECT queries).
136
 *
137
 * @param query - The SQL query
138
 * @param params - Query parameters
139
 * @returns Processed result for Drizzle ORM
140
 */
141
async function processAllMethod(query: string, params: unknown[]): Promise<ForgeDriverResult> {
142
  const sqlStatement = await sql.prepare<unknown>(query);
32✔
143

144
  if (params) {
32!
145
    await sqlStatement.bindParams(...params);
32✔
146
  }
147

148
  const result = (await withTimeout(sqlStatement.execute(), timeoutMessage, timeoutMs)) as ForgeSQLResult;
32✔
149
  await saveMetaDataToContext(result.metadata);
150

151
  if (!result.rows) {
152
    return { rows: [] };
153
  }
32✔
154

155
  const rows = (result.rows as any[]).map((r) => Object.values(r as Record<string, unknown>));
32!
UNCOV
156

×
157
  return { rows };
158
}
159

47✔
160
/**
161
 * Main Forge SQL driver function for Drizzle ORM integration.
32✔
162
 * Handles DDL operations, execute operations (UPDATE/INSERT/DELETE), and select operations.
163
 *
164
 * @param query - The SQL query to execute
165
 * @param params - Query parameters
166
 * @param method - Execution method ("all" for SELECT, "execute" for UPDATE/INSERT/DELETE)
167
 * @returns Promise with query results compatible with Drizzle ORM
168
 *
169
 * @throws {Error} When DDL operations are called with parameters
170
 *
171
 * @example
172
 * ```typescript
173
 * // DDL operation
174
 * await forgeDriver("CREATE TABLE users (id INT)", [], "all");
175
 *
176
 * // SELECT operation
177
 * await forgeDriver("SELECT * FROM users WHERE id = ?", [1], "all");
178
 *
179
 * // UPDATE operation
180
 * await forgeDriver("UPDATE users SET name = ? WHERE id = ?", ["John", 1], "execute");
181
 * ```
182
 */
183
export const forgeDriver = async (
184
  query: string,
185
  params: unknown[],
186
  method: QueryMethod,
187
): Promise<ForgeDriverResult> => {
3✔
188
  const operationType = await getOperationType();
189
  // Handle DDL operations
190
  if (operationType === "DDL") {
191
    const result = await withTimeout(sql.executeDDL(inlineParams(query, params)), timeoutMessage, timeoutMs);
192
    return await processDDLResult(method, result);
79✔
193
  }
194

79✔
195
  // Handle execute method (UPDATE, INSERT, DELETE)
2✔
196
  if (method === "execute") {
197
    return await processExecuteMethod(query, params ?? []);
198
  }
199

200
  // Handle all method (SELECT)
2✔
201
  return await processAllMethod(query, params ?? []);
202
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc