Skip to content

Commit 3c73865

Browse files
committed
refactor weblite
1 parent 8038b49 commit 3c73865

File tree

10 files changed

+239
-111
lines changed

10 files changed

+239
-111
lines changed

src/drivers/constants.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ if (typeof Deno !== 'undefined') {
1313

1414
export const DEFAULT_HEADERS = {
1515
'X-Client-Info': `sqlitecloud-js-${JS_ENV}/${version}`,
16-
'Content-Type': 'application/octet-stream'
1716
}
1817
export const DEFAULT_GLOBAL_OPTIONS = {
1918
headers: DEFAULT_HEADERS

src/drivers/database.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,7 @@ export class Database extends EventEmitter {
486486
* data changes in the database table. It also enables sending messages to anyone
487487
* subscribed to a specific channel.
488488
* @returns {PubSub} A PubSub object
489+
* DEPRECATED: use PubSubClient instead
489490
*/
490491
public async getPubSub(): Promise<PubSub> {
491492
return new Promise((resolve, reject) => {

src/packages/SQLiteCloudClient.ts

Lines changed: 48 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,103 @@
11
import { Database } from '../drivers/database'
22
import { Fetch, fetchWithAuth } from './utils/fetch'
3-
import { PubSubClient } from './pubsub/PubSubClient'
3+
import { PubSubClient } from './_pubsub/PubSubClient'
44
import { WebliteClient } from './weblite/WebliteClient'
5-
import { StorageClient } from './storage/StorageClient'
6-
import { SQLiteCloudCommand, SQLiteCloudError } from '../drivers/types'
7-
import { cleanConnectionString, getDefaultDatabase } from './utils'
5+
import { StorageClient } from './_storage/StorageClient'
6+
import { SQLiteCloudDataTypes, SQLiteCloudError } from '../drivers/types'
7+
import { cleanConnectionString } from './utils'
88
import { FunctionsClient } from './_functions/FunctionsClient'
99
import { SQLiteCloudClientConfig } from './types'
10+
import { DEFAULT_HEADERS } from '../drivers/constants'
11+
12+
const validateConfig = (config: SQLiteCloudClientConfig | string) => {
13+
if (!(config)) throw new SQLiteCloudError('No configuration provided')
14+
if (typeof config === 'string') {
15+
if (!config.includes('sqlitecloud://')) throw new SQLiteCloudError('Invalid connection string')
16+
}
17+
18+
if (typeof config === 'object') {
19+
if (!config.connectionString) throw new SQLiteCloudError('No connection string provided')
20+
if (!config.connectionString.includes('sqlitecloud://')) throw new SQLiteCloudError('Invalid connection string')
21+
}
22+
}
1023

1124
export class SQLiteCloudClient {
1225
protected connectionString: string
1326
protected fetch: Fetch
14-
protected globalHeaders: Record<string, string>
15-
protected _defaultDb: string
16-
protected _db: Database
27+
protected _globalHeaders: Record<string, string>
28+
protected _db: Database | null
29+
protected _pubSub: PubSubClient | null
30+
protected _weblite: WebliteClient | null
1731

1832
constructor(config: SQLiteCloudClientConfig | string) {
1933
try {
20-
if (!config) {
21-
throw new SQLiteCloudError('Invalid connection string or config')
22-
}
34+
validateConfig(config)
2335
let connectionString: string
2436
let customFetch: Fetch | undefined
2537
let globalHeaders: Record<string, string> = {}
26-
38+
2739
if (typeof config === 'string') {
2840
connectionString = cleanConnectionString(config)
29-
globalHeaders = {}
41+
globalHeaders = DEFAULT_HEADERS
3042
} else {
3143
connectionString = config.connectionString
3244
customFetch = config.global?.fetch
33-
globalHeaders = config.global?.headers ?? {}
45+
globalHeaders = config.global?.headers ? { ...DEFAULT_HEADERS, ...config.global.headers } : DEFAULT_HEADERS
3446
}
3547

3648
this.connectionString = connectionString
3749
this.fetch = fetchWithAuth(this.connectionString, customFetch)
38-
this.globalHeaders = globalHeaders
39-
this._defaultDb = getDefaultDatabase(this.connectionString) ?? ''
40-
this._db = new Database(this.connectionString)
50+
this._globalHeaders = globalHeaders
51+
this._db = null
52+
this._pubSub = null
53+
this._weblite = null
4154

4255
} catch (error) {
4356
throw new SQLiteCloudError('failed to initialize SQLiteCloudClient')
4457
}
4558
}
46-
47-
async sql(sql: TemplateStringsArray | string | SQLiteCloudCommand, ...values: any[]) {
48-
this.db.exec(`USE DATABASE ${this._defaultDb}`)
49-
try {
50-
const result = await this.db.sql(sql, ...values)
51-
return { data: result, error: null }
52-
} catch (error) {
53-
return { error, data: null }
54-
}
59+
// Defaults to HTTP API
60+
async sql(sql: TemplateStringsArray | string, ...values: SQLiteCloudDataTypes[]) {
61+
return await this.weblite.sql(sql, ...values)
5562
}
5663

5764
get pubSub() {
58-
return new PubSubClient(this.db.getConfiguration())
65+
if (!this._pubSub) {
66+
this._pubSub = new PubSubClient(this.db.getConfiguration())
67+
}
68+
return this._pubSub
5969
}
6070

6171
get db() {
72+
if (!this._db) {
73+
this._db = new Database(this.connectionString)
74+
}
6275
return this._db
6376
}
6477

6578
get weblite() {
66-
return new WebliteClient(this.connectionString, {
67-
customFetch: this.fetch,
68-
headers: this.globalHeaders
69-
})
79+
if (!this._weblite) {
80+
this._weblite = new WebliteClient(this.connectionString, {
81+
fetch: this.fetch,
82+
headers: this._globalHeaders
83+
})
84+
}
85+
return this._weblite
7086
}
7187

7288
get files() {
7389
return new StorageClient(this.connectionString, {
7490
customFetch: this.fetch,
75-
headers: this.globalHeaders
91+
headers: this._globalHeaders
7692
})
7793
}
7894

7995
get functions() {
8096
return new FunctionsClient(this.connectionString, {
8197
customFetch: this.fetch,
82-
headers: this.globalHeaders
98+
headers: this._globalHeaders
8399
})
84100
}
85-
86-
set defaultDb(dbName: string) {
87-
this._defaultDb = dbName
88-
}
89-
90-
get defaultDb() {
91-
return this._defaultDb
92-
}
93101
}
94102

95103
export function createClient(config: SQLiteCloudClientConfig | string): SQLiteCloudClient {
Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Fetch, fetchWithAuth } from "../utils/fetch"
55
import { Storage } from "../types"
66

77
// TODO: add consistent return types
8+
89
export class StorageClient implements Storage {
910
protected filesUrl: string
1011
protected webliteSQLUrl: string
@@ -16,30 +17,33 @@ export class StorageClient implements Storage {
1617
options: {
1718
customFetch?: Fetch,
1819
headers?: Record<string, string>
19-
} = {}) {
20+
} = {
21+
headers: {}
22+
}) {
2023
this.filesUrl = getAPIUrl(connectionString, 'files')
2124
this.webliteSQLUrl = getAPIUrl(connectionString, 'weblite/sql')
2225
this.fetch = options.customFetch || fetchWithAuth(connectionString)
23-
this.headers = options.headers ? { ...DEFAULT_HEADERS, ...options.headers } : { ...DEFAULT_HEADERS }
26+
this.headers = { ...DEFAULT_HEADERS, ...options.headers }
2427
}
2528

2629
async createBucket(bucket: string) {
27-
const sql = `USE DATABASE files; INSERT INTO files (Bucket) VALUES ('${bucket}');`
28-
2930
try {
3031
const response = await this.fetch(this.webliteSQLUrl, {
31-
method: 'POST',
32-
body: JSON.stringify({ sql }),
33-
headers: this.headers
32+
method: 'POST',
33+
body: JSON.stringify({
34+
database: 'files.sqlite',
35+
sql: `INSERT INTO files (Bucket, Pathname, Data) VALUES ('${bucket}', '/', '' );` }
36+
),
37+
headers: this.headers,
3438
})
3539

3640
if (!response.ok) {
3741
throw new SQLiteCloudError(`Failed to create bucket: ${response.statusText}`)
3842
}
3943

40-
return { data: await response.json(), error: null }
44+
return await response.json();
4145
} catch (error) {
42-
return { data: null, error }
46+
return { error, data: null, metadata: null }
4347
}
4448
}
4549

src/packages/test/client.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { CHINOOK_DATABASE_URL } from '../../../test/shared'
2+
import { SQLiteCloudClient } from '../SQLiteCloudClient'
3+
4+
const DEFAULT_TABLE_NAME = 'albums';
5+
6+
const client = new SQLiteCloudClient(CHINOOK_DATABASE_URL)
7+
8+
describe('SQLiteCloudClient test suite', () => {
9+
it('should be able to create a client', () => {
10+
expect(client).toBeDefined()
11+
expect(client).toBeInstanceOf(SQLiteCloudClient)
12+
})
13+
14+
it('should throw errors if no valid params are provided', () => {
15+
expect(() => new SQLiteCloudClient('')).toThrow()
16+
expect(() => new SQLiteCloudClient({ connectionString: '' })).toThrow()
17+
expect(() => new SQLiteCloudClient({ connectionString: 'invalid' })).toThrow()
18+
})
19+
20+
it('should be able to query the database', async () => {
21+
const { data, error } = await client.sql`SELECT * FROM ${DEFAULT_TABLE_NAME}`;
22+
23+
expect(data).toBeDefined()
24+
expect(error).toBeNull()
25+
})
26+
27+
})

src/packages/test/storage.test.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,37 @@
11
import { expect } from '@jest/globals'
2-
import { StorageClient } from '../storage/StorageClient'
2+
import { StorageClient } from '../_storage/StorageClient'
33
import { CHINOOK_DATABASE_URL } from '../../../test/shared'
44

5+
const TEST_BUCKET_NAME = 'test_bucket'
56

6-
const storage = new StorageClient(CHINOOK_DATABASE_URL)
7+
const storage = new StorageClient(CHINOOK_DATABASE_URL,
8+
{
9+
headers: {
10+
'Content-Type': 'application/json'
11+
}
12+
}
13+
)
714

815
describe('StorageClient', () => {
916
it('should be able to create a bucket', async () => {
1017
expect(storage).toBeDefined()
18+
const getBucketResponse = await storage.getBucket(TEST_BUCKET_NAME)
19+
console.log(getBucketResponse)
1120

12-
const bucket = await storage.createBucket('test-bucket')
13-
14-
expect(bucket).toBeDefined()
21+
const { data, error } = await storage.createBucket(TEST_BUCKET_NAME)
22+
console.log(data, error)
23+
expect(error).toBeNull()
24+
expect(data).toBeDefined()
1525
})
16-
})
1726

27+
it('should get a bucket', async () => {
28+
expect(storage).toBeDefined()
29+
30+
const { data, error } = await storage.getBucket(TEST_BUCKET_NAME)
31+
console.log(data)
32+
expect(error).toBeNull()
33+
expect(data).toBeDefined()
34+
})
1835

36+
37+
})

src/packages/types/index.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ interface StorageResponse {
4444
* @param list - List all files in a bucket.
4545
*/
4646
interface Storage {
47-
createBucket(bucket: string): Promise<StorageResponse>
4847
getBucket(bucket: string): Promise<StorageResponse>
4948
deleteBucket(bucket: string): Promise<StorageResponse>
5049
listBuckets(): Promise<StorageResponse>

src/packages/utils/fetch.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const fetchWithAuth = (authorization: string, customFetch?: Fetch): Fetch
2727
const fetch = resolveFetch(customFetch)
2828
const HeadersConstructor = resolveHeadersConstructor()
2929

30-
return async (input, init) => {
30+
return (input, init) => {
3131
const headers = new HeadersConstructor(init?.headers)
3232
if (!headers.has('Authorization')) {
3333
headers.set('Authorization', `Bearer ${authorization}`)

0 commit comments

Comments
 (0)