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

vzakharchenko / forge-sql-orm / 18982321441

31 Oct 2025 06:52PM UTC coverage: 80.321%. Remained the same
18982321441

push

github

vzakharchenko
new Release

457 of 643 branches covered (71.07%)

Branch coverage included in aggregate %.

26 of 39 new or added lines in 5 files covered. (66.67%)

14 existing lines in 2 files now uncovered.

943 of 1100 relevant lines covered (85.73%)

19.5 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;
4✔
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.`;
4✔
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);
49✔
120

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

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

131
  return { rows: [result.rows] };
46✔
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);
41✔
143

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

148
  const result = (await withTimeout(
41✔
149
    sqlStatement.execute(),
150
    timeoutMessage,
151
    timeoutMs,
152
  )) as ForgeSQLResult;
153
  await saveMetaDataToContext(result.metadata);
41✔
154

155
  if (!result.rows) {
41!
UNCOV
156
    return { rows: [] };
×
157
  }
158

159
  const rows = (result.rows as any[]).map((r) => Object.values(r as Record<string, unknown>));
56✔
160

161
  return { rows };
41✔
162
}
163

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

203
  // Handle execute method (UPDATE, INSERT, DELETE)
204
  if (method === "execute") {
90✔
205
    return await processExecuteMethod(query, params ?? []);
49!
206
  }
207

208
  // Handle all method (SELECT)
209
  return await processAllMethod(query, params ?? []);
41!
210
};
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