-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #102 from sqlitecloud/pub-sub
feat(pubsub): initial implementation
- Loading branch information
Showing
8 changed files
with
376 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,6 +32,34 @@ We aim for full compatibility with the established [sqlite3 API](https://www.npm | |
|
||
The package is developed entirely in TypeScript and is fully compatible with JavaScript. It doesn't require any native libraries. This makes it a straightforward and effective tool for managing cloud-based databases in a familiar SQLite environment. | ||
|
||
## Publish / Subscribe (Pub/Sub) | ||
|
||
```ts | ||
import { Database } from '@sqlitecloud/drivers' | ||
import { PubSub, PUBSUB_ENTITY_TYPE } from '@sqlitecloud/drivers/lib/drivers/pubsub' | ||
|
||
let database = new Database('sqlitecloud://user:[email protected]:8860/chinook.sqlite') | ||
// or use sqlitecloud://xxx.sqlite.cloud:8860?apikey=xxxxxxx | ||
|
||
const pubSub: PubSub = await database.getPubSub() | ||
|
||
await pubSub.listen(PUBSUB_ENTITY_TYPE.TABLE, 'albums', (error, results, data) => { | ||
if (results) { | ||
// Changes on albums table will be received here as JSON object | ||
console.log('Received message:', results) | ||
} | ||
}) | ||
|
||
await database.sql`INSERT INTO albums (Title, ArtistId) values ('Brand new song', 1)` | ||
|
||
// Stop listening changes on the table | ||
await pubSub.unlisten(PUBSUB_ENTITY_TYPE.TABLE, 'albums') | ||
``` | ||
|
||
Pub/Sub is a messaging pattern that allows multiple applications to communicate with each other asynchronously. In the context of SQLiteCloud, Pub/Sub can be used to provide real-time updates and notifications to subscribed applications whenever data changes in the database or it can be used to send payloads (messages) to anyone subscribed to a channel. | ||
|
||
Pub/Sub Documentation: [https://docs.sqlitecloud.io/docs/pub-sub](https://docs.sqlitecloud.io/docs/pub-sub) | ||
|
||
## More | ||
|
||
How do I deploy SQLite in the cloud? | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import { SQLiteCloudConnection } from './connection' | ||
import SQLiteCloudTlsConnection from './connection-tls' | ||
import { PubSubCallback } from './types' | ||
|
||
export enum PUBSUB_ENTITY_TYPE { | ||
TABLE = 'TABLE', | ||
CHANNEL = 'CHANNEL' | ||
} | ||
|
||
/** | ||
* Pub/Sub class to receive changes on database tables or to send messages to channels. | ||
*/ | ||
export class PubSub { | ||
constructor(connection: SQLiteCloudConnection) { | ||
this.connection = connection | ||
this.connectionPubSub = new SQLiteCloudTlsConnection(connection.getConfig()) | ||
} | ||
|
||
private connection: SQLiteCloudConnection | ||
private connectionPubSub: SQLiteCloudConnection | ||
|
||
/** | ||
* Listen for a table or channel and start to receive messages to the provided callback. | ||
* @param entityType One of TABLE or CHANNEL' | ||
* @param entityName Name of the table or the channel | ||
* @param callback Callback to be called when a message is received | ||
* @param data Extra data to be passed to the callback | ||
*/ | ||
public async listen(entityType: PUBSUB_ENTITY_TYPE, entityName: string, callback: PubSubCallback, data?: any): Promise<any> { | ||
const entity = entityType === 'TABLE' ? 'TABLE ' : '' | ||
|
||
const authCommand: string = await this.connection.sql(`LISTEN ${entity}${entityName};`) | ||
|
||
return new Promise((resolve, reject) => { | ||
this.connectionPubSub.sendCommands(authCommand, (error, results) => { | ||
if (error) { | ||
callback.call(this, error, null, data) | ||
reject(error) | ||
} else { | ||
// skip results from pubSub auth command | ||
if (results !== 'OK') { | ||
callback.call(this, null, results, data) | ||
} | ||
resolve(results) | ||
} | ||
}) | ||
}) | ||
} | ||
|
||
/** | ||
* Stop receive messages from a table or channel. | ||
* @param entityType One of TABLE or CHANNEL | ||
* @param entityName Name of the table or the channel | ||
*/ | ||
public async unlisten(entityType: string, entityName: string): Promise<any> { | ||
const subject = entityType === 'TABLE' ? 'TABLE ' : '' | ||
|
||
return this.connection.sql(`UNLISTEN ${subject}?;`, entityName) | ||
} | ||
|
||
/** | ||
* Create a channel to send messages to. | ||
* @param name Channel name | ||
* @param failIfExists Raise an error if the channel already exists | ||
*/ | ||
public async createChannel(name: string, failIfExists: boolean = true): Promise<any> { | ||
let notExistsCommand = '' | ||
if (!failIfExists) { | ||
notExistsCommand = 'IF NOT EXISTS;' | ||
} | ||
|
||
return this.connection.sql(`CREATE CHANNEL ? ${notExistsCommand}`, name) | ||
} | ||
|
||
/** | ||
* Send a message to the channel. | ||
*/ | ||
public notifyChannel(channelName: string, message: string): Promise<any> { | ||
return this.connection.sql`NOTIFY ${channelName} ${message};` | ||
} | ||
|
||
/** | ||
* Ask the server to close the connection to the database and | ||
* to keep only open the Pub/Sub connection. | ||
* Only interaction with Pub/Sub commands will be allowed. | ||
*/ | ||
public setPubSubOnly(): Promise<any> { | ||
return new Promise((resolve, reject) => { | ||
this.connection.sendCommands('PUBSUB ONLY;', (error, results) => { | ||
if (error) { | ||
reject(error) | ||
} else { | ||
this.connection.close() | ||
resolve(results) | ||
} | ||
}) | ||
}) | ||
} | ||
|
||
/** True if Pub/Sub connection is open. */ | ||
public connected(): boolean { | ||
return this.connectionPubSub.connected | ||
} | ||
|
||
/** Close Pub/Sub connection. */ | ||
public close(): void { | ||
this.connectionPubSub.close() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.