Skip to content

Commit

Permalink
add back getPubSub to database
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobprall committed Dec 26, 2024
1 parent 4e601b3 commit 8038b49
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 80 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sqlitecloud/drivers",
"version": "1.0.359",
"version": "1.0.360",
"description": "SQLiteCloud drivers for Typescript/Javascript in edge, web and node clients",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
Expand Down
20 changes: 20 additions & 0 deletions src/drivers/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { ErrorCallback, ResultsCallback, RowCallback, RowsCallback } from './typ
import EventEmitter from 'eventemitter3'
import { isBrowser } from './utilities'
import { Statement } from './statement'
import { PubSub } from './pubsub'

// Uses eventemitter3 instead of node events for browser compatibility
// https://github.com/primus/eventemitter3
Expand Down Expand Up @@ -478,4 +479,23 @@ export class Database extends EventEmitter {
})
})
}
/**
* PubSub class provides a Pub/Sub real-time updates and notifications system to
* allow multiple applications to communicate with each other asynchronously.
* It allows applications to subscribe to tables and receive notifications whenever
* data changes in the database table. It also enables sending messages to anyone
* subscribed to a specific channel.
* @returns {PubSub} A PubSub object
*/
public async getPubSub(): Promise<PubSub> {
return new Promise((resolve, reject) => {
this.getConnection((error, connection) => {
if (error || !connection) {
reject(error)
} else {
resolve(new PubSub(connection))
}
})
})
}
}
46 changes: 31 additions & 15 deletions src/packages/SQLiteCloudClient.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import { Database } from '../drivers/database'
import { Fetch, fetchWithAuth } from './utils/fetch'
import { PubSubClient } from './pubsub/PubSubClient'
import { WebliteClient } from './weblite/SQLiteCloudWebliteClient'
import { WebliteClient } from './weblite/WebliteClient'
import { StorageClient } from './storage/StorageClient'
import { SQLiteCloudCommand, SQLiteCloudError } from '../drivers/types'
import { cleanConnectionString, getDefaultDatabase } from './utils'

interface SQLiteCloudClientConfig {
connectionString: string
fetch?: Fetch
}
import { FunctionsClient } from './_functions/FunctionsClient'
import { SQLiteCloudClientConfig } from './types'

export class SQLiteCloudClient {
protected connectionString: string
protected fetch: Fetch
protected globalHeaders: Record<string, string>
protected _defaultDb: string
protected _db: Database

constructor(config: SQLiteCloudClientConfig | string) {
Expand All @@ -23,25 +22,30 @@ export class SQLiteCloudClient {
}
let connectionString: string
let customFetch: Fetch | undefined
let globalHeaders: Record<string, string> = {}

if (typeof config === 'string') {
connectionString = cleanConnectionString(config)
globalHeaders = {}
} else {
connectionString = config.connectionString
customFetch = config.fetch
customFetch = config.global?.fetch
globalHeaders = config.global?.headers ?? {}
}

this.connectionString = connectionString
this.fetch = fetchWithAuth(this.connectionString, customFetch)
this.defaultDb = getDefaultDatabase(this.connectionString) ?? ''
this.globalHeaders = globalHeaders
this._defaultDb = getDefaultDatabase(this.connectionString) ?? ''
this._db = new Database(this.connectionString)

} catch (error) {
throw new SQLiteCloudError('failed to initialize SQLiteCloudClient')
}
}

async sql(sql: TemplateStringsArray | string | SQLiteCloudCommand, ...values: any[]) {
this.db.exec(`USE DATABASE ${this.defaultDb}`)
this.db.exec(`USE DATABASE ${this._defaultDb}`)
try {
const result = await this.db.sql(sql, ...values)
return { data: result, error: null }
Expand All @@ -59,20 +63,32 @@ export class SQLiteCloudClient {
}

get weblite() {
return new WebliteClient(this.connectionString, { customFetch: this.fetch })
return new WebliteClient(this.connectionString, {
customFetch: this.fetch,
headers: this.globalHeaders
})
}

get files() {
return new StorageClient(this.connectionString, { customFetch: this.fetch })
return new StorageClient(this.connectionString, {
customFetch: this.fetch,
headers: this.globalHeaders
})
}

get functions() {
// return new SQLiteCloudFunctionsClient(this.connectionString, this.fetch)
return null
return new FunctionsClient(this.connectionString, {
customFetch: this.fetch,
headers: this.globalHeaders
})
}

set defaultDb(dbName: string) {
this.defaultDb = dbName
this._defaultDb = dbName
}

get defaultDb() {
return this._defaultDb
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,39 +41,38 @@ export class FunctionsClient {
}

async invoke(functionId: string, options: FunctionInvokeOptions) {
const { headers, args } = options
let body;
let _headers: Record<string, string> = {}
if (args &&
((headers && !Object.prototype.hasOwnProperty.call(headers, 'Content-Type')) || !headers)
if (options.args &&
((options.headers && !Object.prototype.hasOwnProperty.call(options.headers, 'Content-Type')) || !options.headers)
) {
if (
(typeof Blob !== 'undefined' && args instanceof Blob) ||
args instanceof ArrayBuffer
(typeof Blob !== 'undefined' && options.args instanceof Blob) ||
options.args instanceof ArrayBuffer
) {
// will work for File as File inherits Blob
// also works for ArrayBuffer as it is the same underlying structure as a Blob
_headers['Content-Type'] = 'application/octet-stream'
body = args
} else if (typeof args === 'string') {
body = options.args
} else if (typeof options.args === 'string') {
// plain string
_headers['Content-Type'] = 'text/plain'
body = args
} else if (typeof FormData !== 'undefined' && args instanceof FormData) {
body = options.args
} else if (typeof FormData !== 'undefined' && options.args instanceof FormData) {
_headers['Content-Type'] = 'multipart/form-data'
body = args
body = options.args
} else {
// default, assume this is JSON
_headers['Content-Type'] = 'application/json'
body = JSON.stringify(args)
body = JSON.stringify(options.args)
}
}

try {
const response = await this.fetch(`${this.url}/${functionId}`, {
method: 'POST',
body: JSON.stringify(args),
headers: { ..._headers, ...this.headers, ...headers }
body: JSON.stringify(options.args),
headers: { ..._headers, ...this.headers, ...options.headers }
})

if (!response.ok) {
Expand Down
File renamed without changes.
36 changes: 2 additions & 34 deletions src/packages/storage/StorageClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,9 @@ import { DEFAULT_HEADERS } from "../../drivers/constants"
import { SQLiteCloudError } from "../../drivers/types"
import { getAPIUrl } from "../utils"
import { Fetch, fetchWithAuth } from "../utils/fetch"
import { Storage } from "../types"

// TODO: add consistent return types


/**
* StorageResponse
* @param data - The data returned from the operation.
* @param error - The error that occurred.
*/
interface StorageResponse {
data: any
error: any
}

/**
* Storage
* @param createBucket - Create a bucket.
* @param getBucket - Get a bucket.
* @param deleteBucket - Delete a bucket.
* @param listBuckets - List all buckets.
* @param upload - Upload a file.
* @param download - Download a file.
* @param remove - Remove a file.
* @param list - List all files in a bucket.
*/
interface Storage {
createBucket(bucket: string): Promise<StorageResponse>
getBucket(bucket: string): Promise<StorageResponse>
deleteBucket(bucket: string): Promise<StorageResponse>
listBuckets(): Promise<StorageResponse>
upload(bucket: string, pathname: string, file: File | Buffer | Blob | string, options: { headers?: Record<string, string> }): Promise<StorageResponse>
download(bucket: string, pathname: string): Promise<StorageResponse>
remove(bucket: string, pathName: string): Promise<StorageResponse>
list(bucket: string): Promise<StorageResponse>
}
export class StorageClient implements Storage {
protected filesUrl: string
protected webliteSQLUrl: string
Expand Down Expand Up @@ -187,7 +155,7 @@ export class StorageClient implements Storage {
}
}

async list(bucket: string) {
async listBucketContents(bucket: string) {
const sql = `USE DATABASE files.sqlite; SELECT * FROM files WHERE bucket = '${bucket}'`
try {
const response = await this.fetch(this.webliteSQLUrl, {
Expand Down
18 changes: 18 additions & 0 deletions src/packages/test/storage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { expect } from '@jest/globals'
import { StorageClient } from '../storage/StorageClient'
import { CHINOOK_DATABASE_URL } from '../../../test/shared'


const storage = new StorageClient(CHINOOK_DATABASE_URL)

describe('StorageClient', () => {
it('should be able to create a bucket', async () => {
expect(storage).toBeDefined()

const bucket = await storage.createBucket('test-bucket')

expect(bucket).toBeDefined()
})
})


Empty file added src/packages/test/utils.ts
Empty file.
55 changes: 55 additions & 0 deletions src/packages/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Fetch } from '../utils/fetch'


export interface SQLiteCloudClientConfig {
connectionString: string
global?: {
fetch?: Fetch
headers?: Record<string, string>
}
}

export interface WebliteResponse {
data: any, // TODO: type this
error: SQLiteCloudError | null
}
export interface Weblite {
upload(dbName: string, file: File | Buffer | Blob | string, opts: UploadOptions): Promise<WebliteResponse>
download(dbName: string): Promise<WebliteResponse>
delete(dbName: string): Promise<WebliteResponse>
listDatabases(): Promise<WebliteResponse>
create(dbName: string): Promise<WebliteResponse>
}


/**
* StorageResponse
* @param data - The data returned from the operation.
* @param error - The error that occurred.
*/
interface StorageResponse {
data: any
error: any
}

/**
* Storage
* @param createBucket - Create a bucket.
* @param getBucket - Get a bucket.
* @param deleteBucket - Delete a bucket.
* @param listBuckets - List all buckets.
* @param upload - Upload a file.
* @param download - Download a file.
* @param remove - Remove a file.
* @param list - List all files in a bucket.
*/
interface Storage {
createBucket(bucket: string): Promise<StorageResponse>
getBucket(bucket: string): Promise<StorageResponse>
deleteBucket(bucket: string): Promise<StorageResponse>
listBuckets(): Promise<StorageResponse>
upload(bucket: string, pathname: string, file: File | Buffer | Blob | string, options: { headers?: Record<string, string> }): Promise<StorageResponse>
download(bucket: string, pathname: string): Promise<StorageResponse>
remove(bucket: string, pathName: string): Promise<StorageResponse>
listBucketContents(bucket: string): Promise<StorageResponse>
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,7 @@ import { SQLiteCloudError, UploadOptions } from '../../drivers/types'
import { Fetch, fetchWithAuth } from '../utils/fetch'
import { DEFAULT_HEADERS } from '../../drivers/constants'
import { getAPIUrl } from '../utils'

interface WebliteResponse {
data: any, // TODO: type this
error: SQLiteCloudError | null
}
interface Weblite {
upload(dbName: string, file: File | Buffer | Blob | string, opts: UploadOptions): Promise<WebliteResponse>
download(dbName: string): Promise<WebliteResponse>
delete(dbName: string): Promise<WebliteResponse>
listDatabases(): Promise<WebliteResponse>
create(dbName: string): Promise<WebliteResponse>
}
import { Weblite } from '../types'

export class WebliteClient implements Weblite {
protected webliteUrl: string
Expand All @@ -25,11 +14,13 @@ export class WebliteClient implements Weblite {
options: {
customFetch?: Fetch,
headers?: Record<string, string>
} = {}
} = {
headers: {}
}
) {
this.webliteUrl = getAPIUrl(connectionString, 'weblite')
this.fetch = options?.customFetch || fetchWithAuth(connectionString)
this.headers = options.headers ? { ...DEFAULT_HEADERS, ...options.headers } : { ...DEFAULT_HEADERS }
this.headers = { ...DEFAULT_HEADERS, ...options.headers }
}

async upload(
Expand All @@ -39,7 +30,6 @@ export class WebliteClient implements Weblite {
) {
const url = `${this.webliteUrl}/${dbName}`
let body: File | Buffer | Blob | string
let headers = {}
if (file instanceof File) {
body = file

Expand All @@ -52,9 +42,9 @@ export class WebliteClient implements Weblite {
body = new Blob([file])
}

headers = {
const headers = {
...(opts.headers ?? {}),
...headers,
...this.headers,
...DEFAULT_HEADERS,
}

Expand Down

0 comments on commit 8038b49

Please sign in to comment.