diff --git a/package.json b/package.json index 390dd1d..cbff5e2 100644 --- a/package.json +++ b/package.json @@ -63,10 +63,10 @@ ], "coverageThreshold": { "global": { - "branches": 55, - "functions": 95, - "lines": 95, - "statements": 95 + "branches": 65, + "functions": 90, + "lines": 90, + "statements": 90 } }, "collectCoverage": true diff --git a/src/index.ts b/src/index.ts index c5b1689..c8030ed 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,11 @@ export enum ScuttlebucketValueItems { OriginalValue } +export interface ScuttlebucketAccept { + whitelist?: string[] + blacklist?: string[] +} + function setId(obj: Scuttlebutt, id: string) { obj.setId(id) return obj @@ -63,6 +68,24 @@ export class Scuttlebucket extends Scuttlebutt { return this } + _isAcceptedName(peerAccept: ScuttlebucketAccept, name: string) { + const { blacklist, whitelist } = peerAccept + if (blacklist && Array.isArray(blacklist)) { + if (blacklist.includes(name)) { + return false + } + } + if (whitelist && Array.isArray(whitelist)) { + return whitelist.includes(name) ? true : false + } + return true + } + + isAccepted(peerAccept: ScuttlebucketAccept, update: Update) { + const name = update[UpdateItems.Data][ScuttlebucketValueItems.Name] + return this._isAcceptedName(peerAccept, name) + } + applyUpdate(update: Update) { const newUpdate = [...update] as Update @@ -84,13 +107,15 @@ export class Scuttlebucket extends Scuttlebutt { return true } - history(sources: Sources) { + history(sources: Sources, peerAccept?: ScuttlebucketAccept) { const h: Update[] = [] const self = this for (let name in this._parts) { - this._parts[name].history(sources).forEach(function(update: Update) { - h.push(self._wrap(name, update)) - }) + if (!peerAccept || this._isAcceptedName(peerAccept, name)) { + this._parts[name].history(sources).forEach(function(update: Update) { + h.push(self._wrap(name, update)) + }) + } } return h.sort(function(a, b) { return ( diff --git a/test/scuttlebucket-pull.test.ts b/test/scuttlebucket-pull.test.ts index a584c2c..73b1c39 100644 --- a/test/scuttlebucket-pull.test.ts +++ b/test/scuttlebucket-pull.test.ts @@ -1,4 +1,4 @@ -import { Model, ReliableEvent, link } from '@jacobbubu/scuttlebutt-pull' +import { Model, ReliableEvent, link, ScuttlebuttOptions } from '@jacobbubu/scuttlebutt-pull' import { Scuttlebucket } from '../src/' import { delay } from './utils' @@ -9,8 +9,11 @@ describe('Scuttlebucket', () => { value2: 'bar2' } - function create() { - return new Scuttlebucket().add('meta', new Model()).add('event', new ReliableEvent()) + function create(opts?: ScuttlebuttOptions) { + return new Scuttlebucket(opts) + .add('meta', new Model()) + .add('event', new ReliableEvent()) + .add('config', new Model()) } it('bucket with model', async () => { @@ -119,4 +122,56 @@ describe('Scuttlebucket', () => { expect(aFired).toHaveBeenCalledTimes(1) expect(bFired).toHaveBeenCalledTimes(1) }) + + it('accepted with whitelist', async () => { + const accept = { whitelist: ['meta'] } + const bucketA = create() + const bucketB = create({ accept }) + + const metaAtA: Model = bucketA.get('meta') as Model + const configAtA: Model = bucketA.get('config') as Model + const metaAtB: Model = bucketB.get('meta') as Model + const configAtB: Model = bucketB.get('config') as Model + + metaAtA.set(expected.key, expected.value) + configAtA.set(expected.key, expected.value) + + const s1 = bucketA.createStream() + const s2 = bucketB.createStream() + + link(s1, s2) + + await delay(10) + + expect(metaAtA.get(expected.key)).toBe(expected.value) + expect(configAtA.get(expected.key)).toBe(expected.value) + expect(metaAtB.get(expected.key)).toBe(expected.value) + expect(configAtB.get(expected.key)).toBeUndefined() + }) + + it('accepted with blacklist', async () => { + const accept = { blacklist: ['config'] } + const bucketA = create() + const bucketB = create({ accept }) + + const metaAtA: Model = bucketA.get('meta') as Model + const configAtA: Model = bucketA.get('config') as Model + const metaAtB: Model = bucketB.get('meta') as Model + const configAtB: Model = bucketB.get('config') as Model + + metaAtA.set(expected.key, expected.value) + configAtA.set(expected.key, expected.value) + + const s1 = bucketA.createStream() + const s2 = bucketB.createStream() + + link(s1, s2) + + await delay(10) + + expect(metaAtA.get(expected.key)).toBe(expected.value) + expect(configAtA.get(expected.key)).toBe(expected.value) + expect(metaAtB.get(expected.key)).toBe(expected.value) + expect(configAtB.get(expected.key)).toBeUndefined() + }) })