refactored collection into abstract and basic in preparation for adding relational
This commit is contained in:
parent
c6f6ece504
commit
6e0dccdfea
|
@ -1,5 +1,5 @@
|
||||||
import Debug from 'debug';
|
import Debug from 'debug';
|
||||||
import {Collection} from "../src/collection";
|
import {BasicCollection} from '../src/collection-basic';
|
||||||
import {Entity} from "../src/entity";
|
import {Entity} from "../src/entity";
|
||||||
import {RhizomeNode} from "../src/node";
|
import {RhizomeNode} from "../src/node";
|
||||||
const debug = Debug('example-app');
|
const debug = Debug('example-app');
|
||||||
|
@ -23,7 +23,7 @@ type User = {
|
||||||
// Enable API to read lossless view
|
// Enable API to read lossless view
|
||||||
rhizomeNode.httpServer.httpApi.serveLossless();
|
rhizomeNode.httpServer.httpApi.serveLossless();
|
||||||
|
|
||||||
const users = new Collection("user");
|
const users = new BasicCollection("user");
|
||||||
users.rhizomeConnect(rhizomeNode);
|
users.rhizomeConnect(rhizomeNode);
|
||||||
|
|
||||||
users.onUpdate((u: Entity) => {
|
users.onUpdate((u: Entity) => {
|
||||||
|
|
|
@ -1,38 +1,31 @@
|
||||||
// A basic collection of entities
|
|
||||||
// This may be extended to house a collection of objects that all follow a common schema.
|
|
||||||
// It should enable operations like removing a property removes the value from the entities in the collection
|
|
||||||
// It could then be further extended with e.g. table semantics like filter, sort, join
|
|
||||||
|
|
||||||
import Debug from 'debug';
|
import Debug from 'debug';
|
||||||
import {randomUUID} from "node:crypto";
|
import {randomUUID} from "node:crypto";
|
||||||
import EventEmitter from "node:events";
|
import EventEmitter from "node:events";
|
||||||
import {Delta} from "./delta";
|
import {Delta} from "./delta";
|
||||||
import {Entity, EntityProperties} from "./entity";
|
import {Entity, EntityProperties} from "./entity";
|
||||||
import {LastWriteWins, ResolvedViewOne} from './last-write-wins';
|
import {ResolvedViewOne} from './last-write-wins';
|
||||||
import {RhizomeNode} from "./node";
|
import {RhizomeNode} from "./node";
|
||||||
import {DomainEntityID} from "./types";
|
import {DomainEntityID} from "./types";
|
||||||
const debug = Debug('rz:collection');
|
const debug = Debug('rz:abstract-collection');
|
||||||
|
|
||||||
export class Collection {
|
export abstract class Collection<T> {
|
||||||
rhizomeNode?: RhizomeNode;
|
rhizomeNode?: RhizomeNode;
|
||||||
name: string;
|
name: string;
|
||||||
eventStream = new EventEmitter();
|
eventStream = new EventEmitter();
|
||||||
lossy?: LastWriteWins;
|
lossy?: T;
|
||||||
|
|
||||||
constructor(name: string) {
|
constructor(name: string) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instead of trying to update our final view of the entity with every incoming delta,
|
abstract initializeView(): void;
|
||||||
// let's try this:
|
|
||||||
// - keep a lossless view (of everything)
|
abstract resolve(id: string): ResolvedViewOne | undefined;
|
||||||
// - build a lossy view when needed
|
|
||||||
// This approach is simplistic, but can then be optimized and enhanced.
|
|
||||||
|
|
||||||
rhizomeConnect(rhizomeNode: RhizomeNode) {
|
rhizomeConnect(rhizomeNode: RhizomeNode) {
|
||||||
this.rhizomeNode = rhizomeNode;
|
this.rhizomeNode = rhizomeNode;
|
||||||
|
|
||||||
this.lossy = new LastWriteWins(this.rhizomeNode.lossless);
|
this.initializeView();
|
||||||
|
|
||||||
// Listen for completed transactions, and emit updates to event stream
|
// Listen for completed transactions, and emit updates to event stream
|
||||||
this.rhizomeNode.lossless.eventStream.on("updated", (id) => {
|
this.rhizomeNode.lossless.eventStream.on("updated", (id) => {
|
||||||
|
@ -43,13 +36,12 @@ export class Collection {
|
||||||
this.eventStream.emit("update", res);
|
this.eventStream.emit("update", res);
|
||||||
});
|
});
|
||||||
|
|
||||||
rhizomeNode.httpServer.httpApi.serveCollection(this);
|
// TODO: Fix this
|
||||||
|
rhizomeNode.httpServer.httpApi.serveCollection<T>(this);
|
||||||
|
|
||||||
debug(`[${this.rhizomeNode.config.peerId}]`, `Connected ${this.name} to rhizome`);
|
debug(`[${this.rhizomeNode.config.peerId}]`, `Connected ${this.name} to rhizome`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Applies the javascript rules for updating object values,
|
|
||||||
// e.g. set to `undefined` to delete a property.
|
|
||||||
// This function is here instead of Entity so that it can:
|
// This function is here instead of Entity so that it can:
|
||||||
// - read the current state in order to build its delta
|
// - read the current state in order to build its delta
|
||||||
// - include the collection name in the delta it produces
|
// - include the collection name in the delta it produces
|
||||||
|
@ -206,14 +198,4 @@ export class Collection {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(
|
|
||||||
id: string
|
|
||||||
): ResolvedViewOne | undefined {
|
|
||||||
if (!this.rhizomeNode) throw new Error('collection not connected to rhizome');
|
|
||||||
if (!this.lossy) throw new Error('lossy view not initialized');
|
|
||||||
|
|
||||||
const res = this.lossy.resolve([id]) || {};
|
|
||||||
|
|
||||||
return res[id];
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
// A basic collection of entities
|
||||||
|
// This may be extended to house a collection of objects that all follow a common schema.
|
||||||
|
// It should enable operations like removing a property removes the value from the entities in the collection
|
||||||
|
// It could then be further extended with e.g. table semantics like filter, sort, join
|
||||||
|
|
||||||
|
import {Collection} from './collection-abstract';
|
||||||
|
import {LastWriteWins, ResolvedViewOne} from './last-write-wins';
|
||||||
|
|
||||||
|
export class BasicCollection extends Collection<LastWriteWins> {
|
||||||
|
lossy?: LastWriteWins;
|
||||||
|
|
||||||
|
initializeView() {
|
||||||
|
if (!this.rhizomeNode) throw new Error('not connected to rhizome');
|
||||||
|
this.lossy = new LastWriteWins(this.rhizomeNode.lossless);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(
|
||||||
|
id: string
|
||||||
|
): ResolvedViewOne | undefined {
|
||||||
|
if (!this.rhizomeNode) throw new Error('collection not connected to rhizome');
|
||||||
|
if (!this.lossy) throw new Error('lossy view not initialized');
|
||||||
|
|
||||||
|
const res = this.lossy.resolve([id]) || {};
|
||||||
|
|
||||||
|
return res[id];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
import {Collection} from "./collection-abstract";
|
||||||
|
import {LastWriteWins, ResolvedViewOne} from "./last-write-wins";
|
||||||
|
|
||||||
|
class RelationalView extends LastWriteWins {
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RelationalCollection extends Collection<RelationalView> {
|
||||||
|
lossy?: RelationalView;
|
||||||
|
|
||||||
|
initializeView() {
|
||||||
|
if (!this.rhizomeNode) throw new Error('not connected to rhizome');
|
||||||
|
this.lossy = new RelationalView(this.rhizomeNode.lossless);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(
|
||||||
|
id: string
|
||||||
|
): ResolvedViewOne | undefined {
|
||||||
|
if (!this.rhizomeNode) throw new Error('collection not connected to rhizome');
|
||||||
|
if (!this.lossy) throw new Error('lossy view not initialized');
|
||||||
|
|
||||||
|
const res = this.lossy.resolve([id]) || {};
|
||||||
|
|
||||||
|
return res[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -7,7 +7,6 @@
|
||||||
// - As typescript interfaces?
|
// - As typescript interfaces?
|
||||||
// - As typescript classes?
|
// - As typescript classes?
|
||||||
|
|
||||||
import {Collection} from "./collection";
|
|
||||||
import {PropertyTypes} from "./types";
|
import {PropertyTypes} from "./types";
|
||||||
|
|
||||||
export type EntityProperties = {
|
export type EntityProperties = {
|
||||||
|
@ -20,11 +19,5 @@ export class Entity {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly id: string,
|
readonly id: string,
|
||||||
readonly collection?: Collection
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async save() {
|
|
||||||
if (!this.collection) throw new Error('to save this entity you must specify the collection');
|
|
||||||
return this.collection.put(this.id, this.properties);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import express, {Router} from "express";
|
import express, {Router} from "express";
|
||||||
import {Collection} from "../collection";
|
import {Collection} from "../collection-abstract";
|
||||||
import {Delta} from "../delta";
|
import {Delta} from "../delta";
|
||||||
import {RhizomeNode} from "../node";
|
import {RhizomeNode} from "../node";
|
||||||
|
|
||||||
|
@ -57,7 +57,8 @@ export class HttpApi {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
serveCollection(collection: Collection) {
|
// serveCollection<T extends Collection>(collection: T) {
|
||||||
|
serveCollection<View>(collection: Collection<View>) {
|
||||||
const {name} = collection;
|
const {name} = collection;
|
||||||
|
|
||||||
// Get the ID of all domain entities
|
// Get the ID of all domain entities
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
import {Collection} from "./collection";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export class RelationalCollection extends Collection {
|
|
||||||
// lossy?:
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {Collection} from "../src/collection";
|
import {BasicCollection} from "../src/collection-basic";
|
||||||
import {RhizomeNode, RhizomeNodeConfig} from "../src/node";
|
import {RhizomeNode, RhizomeNodeConfig} from "../src/node";
|
||||||
|
|
||||||
const start = 5000;
|
const start = 5000;
|
||||||
|
@ -17,7 +17,7 @@ export class App extends RhizomeNode {
|
||||||
...config,
|
...config,
|
||||||
});
|
});
|
||||||
|
|
||||||
const users = new Collection("user");
|
const users = new BasicCollection("user");
|
||||||
users.rhizomeConnect(this);
|
users.rhizomeConnect(this);
|
||||||
|
|
||||||
const {httpAddr, httpPort} = this.config;
|
const {httpAddr, httpPort} = this.config;
|
||||||
|
|
Loading…
Reference in New Issue