Skip to content

Commit af179d0

Browse files
committed
Allow for string IDs
1 parent 576e5c7 commit af179d0

File tree

4 files changed

+29
-9
lines changed

4 files changed

+29
-9
lines changed

src/db.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const Provider = {
3535
type Provider = Values<typeof Provider>;
3636

3737
export interface Client {
38-
type: string;
38+
config: ClientConfig;
3939
close(): Promise<void>;
4040
execute(sql: string, parameters?: Parameter[], debug?: boolean): Promise<{ affectedRows?: number; lastInsertId?: number }>;
4141
query(sql: string, parameters?: Parameter[], debug?: boolean): Promise<Row[]>;
@@ -57,6 +57,9 @@ export interface ClientConfig {
5757
}
5858

5959
async function connect(config: ClientConfig): Promise<Client> {
60+
// Make sure we have a valid configuration
61+
config = Object.assign({ hostname: "127.0.0.1", port: 3306 });
62+
6063
// Set the debug flag
6164
DB.debug = Deno.env.get("DEBUG")?.includes("dbx") || config.debug || false;
6265

@@ -71,7 +74,7 @@ async function connect(config: ClientConfig): Promise<Client> {
7174
charset: "utf8mb4",
7275
});
7376
return new class implements Client {
74-
type = config.type;
77+
config = config;
7578
close() {
7679
return nativeClient.end();
7780
}
@@ -95,7 +98,7 @@ async function connect(config: ClientConfig): Promise<Client> {
9598
config = Object.assign(config, { user: config.username });
9699
const nativeClient = await new postgres.Pool(config, config.poolSize ?? 1).connect() as Postgres;
97100
return new class implements Client {
98-
type = config.type;
101+
config = config;
99102
close() {
100103
return nativeClient.end();
101104
}
@@ -115,7 +118,7 @@ async function connect(config: ClientConfig): Promise<Client> {
115118
const sqlite = await import("jsr:@db/sqlite@0");
116119
const nativeClient = new sqlite.Database(config.database ?? Deno.env.get("DB_FILE") ?? ":memory:") as SQLite;
117120
return new class implements Client {
118-
type = config.type;
121+
config = config;
119122
close() {
120123
return Promise.resolve(nativeClient.close());
121124
}
@@ -179,6 +182,21 @@ export class DB {
179182
await this.client.close();
180183
}
181184

185+
static async ensure(safe = false) {
186+
// Follow JDBC URL format: db://hostname:port/database
187+
const config = DB.client.config;
188+
const url = config.type + ":" + config.username + "@" + config.hostname + ":" + config.port + "/" + config.database;
189+
190+
try {
191+
const [ { sum } ] = await DB.query("SELECT 1+1 AS sum");
192+
if (sum === 2) return url;
193+
} catch (ex) {
194+
const message = "❌ Could not connect to DB '" + url + "', review DB configuration";
195+
if (!safe) new Error(message);
196+
else console.error(message, ex);
197+
}
198+
}
199+
182200
// Transforms parameters (and SQL) into array-like and references via `?`
183201
static _transformParameters(sql: string, objectParameters: { [key: string]: unknown }, arrayParameters: unknown[], safe?: boolean): string {
184202
arrayParameters.splice(0, arrayParameters.length);
@@ -291,10 +309,10 @@ export class DB {
291309
const RESERVED = ["ACCESSIBLE","ADD","ALL","ALTER","ANALYZE","AND","AS","ASC","ASENSITIVE","BEFORE","BETWEEN","BIGINT","BINARY","BLOB","BOTH","BY","CALL","CASCADE","CASE","CHANGE","CHAR","CHARACTER","CHECK","COLLATE","COLUMN","CONDITION","CONSTRAINT","CONTINUE","CONVERT","CREATE","CROSS","CUBE","CUME_DIST","CURRENT_DATE","CURRENT_TIME","CURRENT_TIMESTAMP","CURRENT_USER","CURSOR","DATABASE","DATABASES","DAY_HOUR","DAY_MICROSECOND","DAY_MINUTE","DAY_SECOND","DEC","DECIMAL","DECLARE","DEFAULT","DELAYED","DELETE","DENSE_RANK","DESC","DESCRIBE","DETERMINISTIC","DISTINCT","DISTINCTROW","DIV","DOUBLE","DROP","DUAL","EACH","ELSE","ELSEIF","EMPTY","ENCLOSED","ESCAPED","EXCEPT","EXISTS","EXIT","EXPLAIN","FALSE","FETCH","FIRST_VALUE","FLOAT","FLOAT4","FLOAT8","FOR","FORCE","FOREIGN","FROM","FULLTEXT","FUNCTION","GENERATED","GET","GRANT","GROUP","GROUPING","GROUPS","HAVING","HIGH_PRIORITY","HOUR_MICROSECOND","HOUR_MINUTE","HOUR_SECOND","IF","IGNORE","IN","INDEX","INFILE","INNER","INOUT","INSENSITIVE","INSERT","INT","INT1","INT2","INT3","INT4","INT8","INTEGER","INTERSECT","INTERVAL","INTO","IO_AFTER_GTIDS","IO_BEFORE_GTIDS","IS","ITERATE","JOIN","JSON_TABLE","KEY","KEYS","KILL","LAG","LAST_VALUE","LATERAL","LEAD","LEADING","LEAVE","LEFT","LIKE","LIMIT","LINEAR","LINES","LOAD","LOCALTIME","LOCALTIMESTAMP","LOCK","LONG","LONGBLOB","LONGTEXT","LOOP","LOW_PRIORITY","MASTER_BIND","MASTER_SSL_VERIFY_SERVER_CERT","MATCH","MAXVALUE","MEDIUMBLOB","MEDIUMINT","MEDIUMTEXT","MIDDLEINT","MINUTE_MICROSECOND","MINUTE_SECOND","MOD","MODIFIES","NATURAL","NOT","NO_WRITE_TO_BINLOG","NTH_VALUE","NTILE","NULL","NUMERIC","OF","ON","OPTIMIZE","OPTIMIZER_COSTS","OPTION","OPTIONALLY","OR","ORDER","OUT","OUTER","OUTFILE","OVER","PARTITION","PERCENT_RANK","PRECISION","PRIMARY","PROCEDURE","PURGE","RANGE","RANK","READ","READS","READ_WRITE","REAL","RECURSIVE","REFERENCES","REGEXP","RELEASE","RENAME","REPEAT","REPLACE","REQUIRE","RESIGNAL","RESTRICT","RETURN","REVOKE","RIGHT","RLIKE","ROW","ROWS","ROW_NUMBER","SCHEMA","SCHEMAS","SECOND_MICROSECOND","SELECT","SENSITIVE","SEPARATOR","SET","SHOW","SIGNAL","SMALLINT","SPATIAL","SPECIFIC","SQL","SQLEXCEPTION","SQLSTATE","SQLWARNING","SQL_BIG_RESULT","SQL_CALC_FOUND_ROWS","SQL_SMALL_RESULT","SSL","STARTING","STORED","STRAIGHT_JOIN","SYSTEM","TABLE","TERMINATED","THEN","TINYBLOB","TINYINT","TINYTEXT","TO","TRAILING","TRIGGER","TRUE","UNDO","UNION","UNIQUE","UNLOCK","UNSIGNED","UPDATE","USAGE","USE","USING","UTC_DATE","UTC_TIME","UTC_TIMESTAMP","VALUES","VARBINARY","VARCHAR","VARCHARACTER","VARYING","VIRTUAL","WHEN","WHERE","WHILE","WINDOW","WITH","WRITE","XOR","YEAR_MONTH","ZEROFILL"];
292310

293311
class DebugClient implements Client {
294-
type: string;
312+
config: ClientConfig;
295313
reserved: RegExp;
296314
constructor(private client: Client) {
297-
this.type = client.type;
315+
this.config = client.config;
298316
this.reserved = new RegExp("\\b(" + RESERVED.join("|") + ")\\b", "g");
299317
}
300318
close(): Promise<void> {

src/repository.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,6 @@ export class Repository<T extends Identifiable> extends EventTarget {
154154

155155
// https://dev.mysql.com/doc/refman/8.0/en/select.html
156156
async findById(id: number | string, debug?: boolean): Promise<T | undefined> {
157-
assert(typeof id === "number" && Number.isInteger(id), "Parameter 'id' must be an integer");
158-
159157
const whereTree: Primitive[] = [];
160158
const sql = `SELECT * FROM ${this.table} WHERE id = ? AND ${Repository._where(this.baseWhere, whereTree)}`;
161159
const parameters = [id, ...whereTree];

src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export interface Property {
1919
pattern?: string;
2020
readOnly?: boolean;
2121
uniqueItems?: boolean;
22-
type: "boolean" | "date" | "integer" | "number" | "object" | "string";
22+
type: "array" | "boolean" | "date" | "integer" | "number" | "object" | "string";
2323
writeOnly?: boolean;
2424
}
2525

test/basic.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ test("Basic entity store/retrieve", options, async function () {
3333
account = await repo.findById(id) as AccountModel;
3434
assertExists(account);
3535

36+
// Retrieve by String ID (using lax SQL parameter parsing)
37+
account = await repo.findById(id.toString()) as AccountModel;
38+
assertExists(account);
39+
3640
// // Make sure JSON is retrieved correctly
3741
assertEquals(account.preferences, { wrap: true, minAge: 18 });
3842
});

0 commit comments

Comments
 (0)