DefinitelyTyped/types/sharedb/sharedb-tests.ts
Eric Hwang e341115317 [sharedb] Add Agent class and extraDbs option for ShareDB constructor, loosen query params to any (#38109)
* [sharedb] Add Agent class and extraDbs option for ShareDB constructor

* Loosen query spec for Connection createFetchQuery and createSubscribeQuery
2019-09-09 20:12:38 -07:00

213 lines
6.1 KiB
TypeScript

/// <reference types="qunit/v1" />
import * as ShareDB from 'sharedb';
import * as http from 'http';
import * as WebSocket from 'ws';
import { Duplex } from 'stream';
import * as ShareDBClient from 'sharedb/lib/client';
// Adapted from https://github.com/avital/websocket-json-stream
class WebSocketJSONStream extends Duplex {
constructor(private readonly ws: WebSocket) {
super({ objectMode: true });
this.ws.on('message', (msg: string) => {
this.push(JSON.parse(msg));
});
this.ws.on('close', () => {
this.push(null); // end readable stream
this.end(); // end writable stream
this.emit('close');
this.emit('end');
});
this.on('error', () => { ws.close(); });
this.on('end', () => { ws.close(); });
}
_read(): void {}
_write(msg: any, encoding: string, next: () => void): void {
this.ws.send(JSON.stringify(msg));
next();
}
}
class CustomExtraDb {
query(collection: string, query: any, fields: unknown, options: {customDbOption: boolean}, callback: ShareDB.DBQueryCallback) {
callback(null, [], {});
}
close() {}
}
const backend = new ShareDB({
extraDbs: {myDb: new CustomExtraDb()}
});
console.log(backend.db);
console.log(backend.pubsub);
console.log(backend.extraDbs);
backend.addProjection('notes_minimal', 'notes', {title: true, creator: true, lastUpdateTime: true});
// Test module augmentation to attach custom typed properties to `agent.custom`.
import _ShareDbAgent = require('sharedb/lib/agent');
declare module 'sharedb/lib/agent' {
interface Custom {
user?: {id: string};
}
}
// Exercise middleware (backend.use)
type SubmitRelatedActions = 'afterSubmit' | 'apply' | 'commit' | 'submit';
const submitRelatedActions: SubmitRelatedActions[] = ['afterSubmit', 'apply', 'commit', 'submit'];
for (const action of submitRelatedActions) {
backend.use(action, (request, callback) => {
if (request.agent.custom.user) {
console.log(request.agent.custom.user.id);
}
console.log(
request.action,
request.agent,
request.backend,
request.index,
request.collection,
request.projection,
request.id,
request.op,
request.options,
request.snapshot,
request.ops,
request.channels,
);
callback();
});
}
backend.use('connect', (context, callback) => {
// `context.req` is the HTTP / websocket request.
// This assumes that some request middleware has handled auth and popuplated `req.userId`.
if (context.req.userId) {
context.agent.custom.user = {id: context.req.userId};
}
console.log(
context.action,
context.agent,
context.agent.backend === context.backend,
context.agent.stream.isServer,
context.backend,
context.stream,
context.req,
);
callback();
});
backend.use('op', (context, callback) => {
console.log(
context.action,
context.agent,
context.backend,
context.collection,
context.id,
context.op,
);
callback();
});
backend.use('query', (context, callback) => {
console.log(
context.action,
context.agent,
context.backend,
context.index,
context.collection,
context.projection,
context.fields,
context.channel,
context.query,
context.options,
context.snapshotProjection,
);
callback();
});
backend.use('receive', (context, callback) => {
console.log(
context.action,
context.agent,
context.backend,
context.data,
);
callback();
});
backend.use('reply', (context, callback) => {
console.log(
context.action,
context.agent,
context.backend,
context.request,
context.reply,
);
// Usage note: It's only necessary to write `context.reply['data']` in
// TypeScript <= 2.1.
//
// In TypeScript 2.2+, `context.reply.data` is OK, as 2.2 added support for
// dotted property access for types with string index signatures.
console.log(context.reply && context.reply['data'] &&
context.reply['data'].someProperty);
callback();
});
backend.use('readSnapshots', (context, callback) => {
console.log(
context.action,
context.agent,
context.backend,
context.collection,
context.snapshots,
context.snapshotType,
);
callback();
});
const connection = backend.connect();
const netRequest = {}; // Passed through to 'connect' middleware, not used by sharedb itself
const connectionWithReq = backend.connect(null, netRequest);
const reboundConnection = backend.connect(backend.connect(), netRequest);
const doc = connection.get('examples', 'counter');
doc.fetch((err) => {
if (err) { throw err; }
if (doc.type === null) {
doc.create({ numClicks: 0 }, startServer);
} else {
startServer();
}
});
function startServer() {
const server = http.createServer();
// Connect any incoming WebSocket connection to ShareDB
const wss = new WebSocket.Server({ server });
wss.on('connection', (ws, req) => {
const stream = new WebSocketJSONStream(ws);
backend.listen(stream);
});
server.listen(8080);
// console.log('Listening on http://localhost:8080');
startClient(() => {
server.close();
wss.close();
});
}
function startClient(callback) {
const socket = new WebSocket('ws://localhost:8080');
const connection = new ShareDBClient.Connection(socket);
const doc = connection.get('examples', 'counter');
doc.subscribe(() => {
doc.submitOp([{p: ['numClicks'], na: 1}]);
callback();
});
// sharedb-mongo query object
connection.createSubscribeQuery('examples', {numClicks: {$gte: 5}}, null, (err, results) => {
console.log(err, results);
});
// SQL-ish query adapter that takes a string query condition
connection.createSubscribeQuery('examples', 'numClicks >= 5', null, (err, results) => {
console.log(err, results);
});
}