Skip to content

Commit 87b9d08

Browse files
Unify connection string parameters with C SDK #68
1 parent a50071e commit 87b9d08

12 files changed

+761
-761
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,4 @@
8787
"arrowParens": "avoid",
8888
"printWidth": 160
8989
}
90-
}
90+
}

src/drivers/connection-ws.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ export class SQLiteCloudWebsocketConnection extends SQLiteCloudConnection {
2929
console.assert(!this.connected, 'Connection already established')
3030
if (!this.socket) {
3131
this.config = config
32-
const connectionString = this.config.connectionString as string
33-
const gatewayUrl = this.config?.gatewayUrl || `${this.config.host === 'localhost' ? 'ws' : 'wss'}://${this.config.host as string}:4000`
34-
this.socket = io(gatewayUrl, { auth: { token: connectionString } })
32+
const connectionstring = this.config.connectionstring as string
33+
const gatewayUrl = this.config?.gatewayurl || `${this.config.host === 'localhost' ? 'ws' : 'wss'}://${this.config.host as string}:4000`
34+
this.socket = io(gatewayUrl, { auth: { token: connectionstring } })
3535
}
3636
callback?.call(this, null)
3737
} catch (error) {

src/drivers/connection.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ import { anonimizeCommand, getUpdateResults } from './utilities'
1212
* Actual connection management and communication with the server in concrete classes.
1313
*/
1414
export abstract class SQLiteCloudConnection {
15-
/** Parse and validate provided connectionString or configuration */
15+
/** Parse and validate provided connectionstring or configuration */
1616
constructor(config: SQLiteCloudConfig | string, callback?: ErrorCallback) {
1717
if (typeof config === 'string') {
18-
this.config = validateConfiguration({ connectionString: config })
18+
this.config = validateConfiguration({ connectionstring: config })
1919
} else {
2020
this.config = validateConfiguration(config)
2121
}
@@ -68,7 +68,7 @@ export abstract class SQLiteCloudConnection {
6868
protected log(message: string, ...optionalParams: any[]): void {
6969
if (this.config.verbose) {
7070
message = anonimizeCommand(message)
71-
console.log(`${new Date().toISOString()} ${this.config.clientId as string}: ${message}`, ...optionalParams)
71+
console.log(`${new Date().toISOString()} ${this.config.clientid as string}: ${message}`, ...optionalParams)
7272
}
7373
}
7474

src/drivers/database.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export class Database extends EventEmitter {
3434
constructor(config: SQLiteCloudConfig | string, mode?: number, callback?: ErrorCallback)
3535
constructor(config: SQLiteCloudConfig | string, mode?: number | ErrorCallback, callback?: ErrorCallback) {
3636
super()
37-
this.config = typeof config === 'string' ? { connectionString: config } : config
37+
this.config = typeof config === 'string' ? { connectionstring: config } : config
3838

3939
// mode is optional and so is callback
4040
// https://github.com/TryGhost/node-sqlite3/wiki/API#new-sqlite3databasefilename--mode--callback
@@ -70,7 +70,7 @@ export class Database extends EventEmitter {
7070
callback?.call(this, null, this.connections[0])
7171
} else {
7272
// connect using websocket if tls is not supported or if explicitly requested
73-
const useWebsocket = isBrowser || this.config?.useWebsocket || this.config?.gatewayUrl
73+
const useWebsocket = isBrowser || this.config?.usewebsocket || this.config?.gatewayurl
7474
if (useWebsocket) {
7575
// socket.io transport works in both node.js and browser environments and connects via SQLite Cloud Gateway
7676
import('./connection-ws')

src/drivers/types.ts

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,24 @@ export const DEFAULT_TIMEOUT = 300 * 1000
99
/** Default tls connection port */
1010
export const DEFAULT_PORT = 9960
1111

12-
/** Configuration for SQLite cloud connection */
12+
/**
13+
* Configuration for SQLite cloud connection
14+
* @note Options are all lowecase so they 1:1 compatible with C SDK
15+
*/
1316
export interface SQLiteCloudConfig {
1417
/** Connection string in the form of sqlitecloud://user:password@host:port/database?options */
15-
connectionString?: string
18+
connectionstring?: string
1619

17-
/** User name is required unless connectionString is provided */
20+
/** User name is required unless connectionstring is provided */
1821
username?: string
1922
/** Password is required unless connection string is provided */
2023
password?: string
2124
/** True if password is hashed, default is false */
22-
passwordHashed?: boolean
25+
password_hashed?: boolean
2326
/** API key can be provided instead of username and password */
24-
apiKey?: string
27+
apikey?: string
2528

26-
/** Host name is required unless connectionString is provided, eg: xxx.sqlitecloud.io */
29+
/** Host name is required unless connectionstring is provided, eg: xxx.sqlitecloud.io */
2730
host?: string
2831
/** Port number for tls socket */
2932
port?: number
@@ -36,32 +39,32 @@ export interface SQLiteCloudConfig {
3639
database?: string
3740

3841
/** Create the database if it doesn't exist? */
39-
createDatabase?: boolean
42+
create?: boolean
4043
/** Database will be created in memory */
41-
dbMemory?: boolean
44+
memory?: boolean
4245
/* Enable compression */
4346
compression?: boolean
4447
/** Request for immediate responses from the server node without waiting for linerizability guarantees */
45-
nonlinearizable?: boolean
48+
non_linearizable?: boolean
4649
/** Server should send BLOB columns */
47-
noBlob?: boolean
50+
noblob?: boolean
4851
/** Do not send columns with more than max_data bytes */
49-
maxData?: number
52+
maxdata?: number
5053
/** Server should chunk responses with more than maxRows */
51-
maxRows?: number
54+
maxrows?: number
5255
/** Server should limit total number of rows in a set to maxRowset */
53-
maxRowset?: number
56+
maxrowset?: number
5457

5558
/** Custom options and configurations for tls socket, eg: additional certificates */
56-
tlsOptions?: tls.ConnectionOptions
59+
tlsoptions?: tls.ConnectionOptions
5760

5861
/** True if we should force use of SQLite Cloud Gateway and websocket connections, default: true in browsers, false in node.js */
59-
useWebsocket?: boolean
62+
usewebsocket?: boolean
6063
/** Url where we can connect to a SQLite Cloud Gateway that has a socket.io deamon waiting to connect, eg. wss://host:4000 */
61-
gatewayUrl?: string
64+
gatewayurl?: string
6265

6366
/** Optional identifier used for verbose logging */
64-
clientId?: string
67+
clientid?: string
6568
/** True if connection should enable debug logs */
6669
verbose?: boolean
6770
}

src/drivers/utilities.ts

Lines changed: 44 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -39,35 +39,35 @@ export function getInitializationCommands(config: SQLiteCloudConfig): string {
3939
// first user authentication, then all other commands
4040
let commands = ''
4141

42-
if (config.apiKey) {
43-
commands = `AUTH APIKEY ${config.apiKey}; `
42+
if (config.apikey) {
43+
commands = `AUTH APIKEY ${config.apikey}; `
4444
} else {
45-
commands = `AUTH USER ${config.username || ''} ${config.passwordHashed ? 'HASH' : 'PASSWORD'} ${config.password || ''}; `
45+
commands = `AUTH USER ${config.username || ''} ${config.password_hashed ? 'HASH' : 'PASSWORD'} ${config.password || ''}; `
4646
}
4747

4848
if (config.database) {
49-
if (config.createDatabase && !config.dbMemory) {
49+
if (config.create && !config.memory) {
5050
commands += `CREATE DATABASE ${config.database} IF NOT EXISTS; `
5151
}
5252
commands += `USE DATABASE ${config.database}; `
5353
}
5454
if (config.compression) {
5555
commands += 'SET CLIENT KEY COMPRESSION TO 1; '
5656
}
57-
if (config.nonlinearizable) {
57+
if (config.non_linearizable) {
5858
commands += 'SET CLIENT KEY NONLINEARIZABLE TO 1; '
5959
}
60-
if (config.noBlob) {
60+
if (config.noblob) {
6161
commands += 'SET CLIENT KEY NOBLOB TO 1; '
6262
}
63-
if (config.maxData) {
64-
commands += `SET CLIENT KEY MAXDATA TO ${config.maxData}; `
63+
if (config.maxdata) {
64+
commands += `SET CLIENT KEY MAXDATA TO ${config.maxdata}; `
6565
}
66-
if (config.maxRows) {
67-
commands += `SET CLIENT KEY MAXROWS TO ${config.maxRows}; `
66+
if (config.maxrows) {
67+
commands += `SET CLIENT KEY MAXROWS TO ${config.maxrows}; `
6868
}
69-
if (config.maxRowset) {
70-
commands += `SET CLIENT KEY MAXROWSET TO ${config.maxRowset}; `
69+
if (config.maxrowset) {
70+
commands += `SET CLIENT KEY MAXROWSET TO ${config.maxrowset}; `
7171
}
7272

7373
return commands
@@ -202,39 +202,39 @@ export function popCallback<T extends ErrorCallback = ErrorCallback>(
202202
/** Validate configuration, apply defaults, throw if something is missing or misconfigured */
203203
export function validateConfiguration(config: SQLiteCloudConfig): SQLiteCloudConfig {
204204
console.assert(config, 'SQLiteCloudConnection.validateConfiguration - missing config')
205-
if (config.connectionString) {
205+
if (config.connectionstring) {
206206
config = {
207207
...config,
208-
...parseConnectionString(config.connectionString),
209-
connectionString: config.connectionString // keep original connection string
208+
...parseconnectionstring(config.connectionstring),
209+
connectionstring: config.connectionstring // keep original connection string
210210
}
211211
}
212212

213213
// apply defaults where needed
214214
config.port ||= DEFAULT_PORT
215215
config.timeout = config.timeout && config.timeout > 0 ? config.timeout : DEFAULT_TIMEOUT
216-
config.clientId ||= 'SQLiteCloud'
216+
config.clientid ||= 'SQLiteCloud'
217217

218218
config.verbose = parseBoolean(config.verbose)
219-
config.noBlob = parseBoolean(config.noBlob)
219+
config.noblob = parseBoolean(config.noblob)
220220
config.compression = parseBoolean(config.compression)
221-
config.createDatabase = parseBoolean(config.createDatabase)
222-
config.nonlinearizable = parseBoolean(config.nonlinearizable)
221+
config.create = parseBoolean(config.create)
222+
config.non_linearizable = parseBoolean(config.non_linearizable)
223223
config.insecure = parseBoolean(config.insecure)
224224

225-
const hasCredentials = (config.username && config.password) || config.apiKey
225+
const hasCredentials = (config.username && config.password) || config.apikey
226226
if (!config.host || !hasCredentials) {
227227
console.error('SQLiteCloudConnection.validateConfiguration - missing arguments', config)
228-
throw new SQLiteCloudError('The user, password and host arguments or the ?apiKey= must be specified.', { errorCode: 'ERR_MISSING_ARGS' })
228+
throw new SQLiteCloudError('The user, password and host arguments or the ?apikey= must be specified.', { errorCode: 'ERR_MISSING_ARGS' })
229229
}
230230

231-
if (!config.connectionString) {
231+
if (!config.connectionstring) {
232232
// build connection string from configuration, values are already validated
233233
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
234-
if (config.apiKey) {
235-
config.connectionString = `sqlitecloud://${config.host}:${config.port}/${config.database || ''}?apiKey=${config.apiKey}`
234+
if (config.apikey) {
235+
config.connectionstring = `sqlitecloud://${config.host}:${config.port}/${config.database || ''}?apikey=${config.apikey}`
236236
} else {
237-
config.connectionString = `sqlitecloud://${encodeURIComponent(config.username || '')}:${encodeURIComponent(config.password || '')}@${config.host}:${
237+
config.connectionstring = `sqlitecloud://${encodeURIComponent(config.username || '')}:${encodeURIComponent(config.password || '')}@${config.host}:${
238238
config.port
239239
}/${config.database}`
240240
}
@@ -244,44 +244,24 @@ export function validateConfiguration(config: SQLiteCloudConfig): SQLiteCloudCon
244244
}
245245

246246
/**
247-
* Parse connectionString like sqlitecloud://username:password@host:port/database?option1=xxx&option2=xxx
248-
* or sqlitecloud://host.sqlite.cloud:8860/chinook.sqlite?apiKey=mIiLARzKm9XBVllbAzkB1wqrgijJ3Gx0X5z1Agm3xBo
247+
* Parse connectionstring like sqlitecloud://username:password@host:port/database?option1=xxx&option2=xxx
248+
* or sqlitecloud://host.sqlite.cloud:8860/chinook.sqlite?apikey=mIiLARzKm9XBVllbAzkB1wqrgijJ3Gx0X5z1Agm3xBo
249249
* into its basic components.
250250
*/
251-
export function parseConnectionString(connectionString: string): SQLiteCloudConfig {
251+
export function parseconnectionstring(connectionstring: string): SQLiteCloudConfig {
252252
try {
253253
// The URL constructor throws a TypeError if the URL is not valid.
254254
// in spite of having the same structure as a regular url
255255
// protocol://username:password@host:port/database?option1=xxx&option2=xxx)
256256
// the sqlitecloud: protocol is not recognized by the URL constructor in browsers
257257
// so we need to replace it with https: to make it work
258-
const knownProtocolUrl = connectionString.replace('sqlitecloud:', 'https:')
258+
const knownProtocolUrl = connectionstring.replace('sqlitecloud:', 'https:')
259259
const url = new URL(knownProtocolUrl)
260-
const options: { [key: string]: string } = {}
261-
262-
// properties that are mixed case in the connection string should be accepted even if the
263-
// customer mistakenly write them in camelCase or kebab-case or whateverTheCase
264-
const mixedCaseProperties = [
265-
'connectionString',
266-
'passwordHashed',
267-
'apiKey',
268-
'createDatabase',
269-
'dbMemory',
270-
'compression',
271-
'noBlob',
272-
'maxData',
273-
'maxRows',
274-
'maxRowset',
275-
'tlsOptions',
276-
'useWebsocket',
277-
'gatewayUrl',
278-
'clientId'
279-
]
280260

261+
// all lowecase options
262+
const options: { [key: string]: string } = {}
281263
url.searchParams.forEach((value, key) => {
282-
let normalizedKey = key.toLowerCase().replaceAll('-', '').replaceAll('_', '')
283-
const mixedCaseKey = mixedCaseProperties.find(mixedCaseProperty => mixedCaseProperty.toLowerCase() == normalizedKey)
284-
options[mixedCaseKey || normalizedKey] = value
264+
options[key.toLowerCase().replaceAll('-', '_')] = value
285265
})
286266

287267
const config: SQLiteCloudConfig = {
@@ -292,10 +272,10 @@ export function parseConnectionString(connectionString: string): SQLiteCloudConf
292272
...options
293273
}
294274

295-
// either you use an apiKey or username and password
296-
if (config.apiKey) {
275+
// either you use an apikey or username and password
276+
if (config.apikey) {
297277
if (config.username || config.password) {
298-
console.warn('SQLiteCloudConnection.parseConnectionString - apiKey and username/password are both specified, using apiKey')
278+
console.warn('SQLiteCloudConnection.parseconnectionstring - apikey and username/password are both specified, using apikey')
299279
}
300280
delete config.username
301281
delete config.password
@@ -308,7 +288,7 @@ export function parseConnectionString(connectionString: string): SQLiteCloudConf
308288

309289
return config
310290
} catch (error) {
311-
throw new SQLiteCloudError(`Invalid connection string: ${connectionString}`)
291+
throw new SQLiteCloudError(`Invalid connection string: ${connectionstring}`)
312292
}
313293
}
314294

@@ -319,3 +299,11 @@ export function parseBoolean(value: string | boolean | null | undefined): boolea
319299
}
320300
return value ? true : false
321301
}
302+
303+
/** Returns true if value is 1 or true */
304+
export function parseBooleanToZeroOne(value: string | boolean | null | undefined): 0 | 1 {
305+
if (typeof value === 'string') {
306+
return value.toLowerCase() === 'true' || value === '1' ? 1 : 0
307+
}
308+
return value ? 1 : 0
309+
}

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ export { Statement } from './drivers/statement'
1212
export { SQLiteCloudConnection } from './drivers/connection'
1313
export { type SQLiteCloudConfig, type SQLCloudRowsetMetadata, SQLiteCloudError, type ResultsCallback, type ErrorCallback } from './drivers/types'
1414
export { SQLiteCloudRowset, SQLiteCloudRow } from './drivers/rowset'
15-
export { escapeSqlParameter, prepareSql, parseConnectionString, validateConfiguration } from './drivers/utilities'
15+
export { escapeSqlParameter, prepareSql, parseconnectionstring, validateConfiguration } from './drivers/utilities'

0 commit comments

Comments
 (0)