Skip to content

Commit

Permalink
#244 - add a createLevelDatabase to allow customizing the Level u…
Browse files Browse the repository at this point in the history
…sed by `BlockstoreLevel`

this will also be passed down when creating a `MessageStoreLevel` and `DataStoreLevel`
  • Loading branch information
dcrousso committed Mar 8, 2023
1 parent 25f5823 commit d38fc19
Show file tree
Hide file tree
Showing 15 changed files with 85 additions and 16 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Decentralized Web Node (DWN) SDK

Code Coverage
![Statements](https://img.shields.io/badge/statements-94.77%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-94.04%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-92.68%25-brightgreen.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-94.77%25-brightgreen.svg?style=flat)
![Statements](https://img.shields.io/badge/statements-94.83%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-94.05%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-92.71%25-brightgreen.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-94.83%25-brightgreen.svg?style=flat)

## Introduction

Expand Down
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"@types/node": "^18.13.0",
"@types/readable-stream": "^2.3.15",
"@types/search-index": "3.2.0",
"abstract-level": "^1.0.3",
"ajv": "8.11.0",
"blockstore-core": "3.0.0",
"cross-fetch": "3.1.5",
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export type { RecordsDeleteMessage, RecordsQueryMessage, RecordsWriteMessage } f
export { AllowAllTenantGate, TenantGate } from './core/tenant-gate.js';
export { Cid } from './utils/cid.js';
export { DataStore } from './store/data-store.js';
export { DataStoreLevel } from './store/data-store-level.js';
export { DateSort } from './interfaces/records/messages/records-query.js';
export { DataStream } from './utils/data-stream.js';
export { DidKeyResolver } from './did/did-key-resolver.js';
Expand Down
20 changes: 16 additions & 4 deletions src/store/blockstore-level.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,34 @@
import type { LevelDatabase } from './create-level.js';
import type { AwaitIterable, Batch, KeyQuery, Pair, Query } from 'interface-store';
import type { Blockstore, Options } from 'interface-blockstore';

import { abortOr } from '../utils/abort.js';
import { CID } from 'multiformats';
import { Level } from 'level';
import { createLevelDatabase } from './create-level.js';
import { sleep } from '../utils/time.js';

// `level` works in Node.js 12+ and Electron 5+ on Linux, Mac OS, Windows and
// FreeBSD, including any future Node.js and Electron release thanks to Node-API, including ARM
// platforms like Raspberry Pi and Android, as well as in Chrome, Firefox, Edge, Safari, iOS Safari
// and Chrome for Android.
export class BlockstoreLevel implements Blockstore {
db: Level<string, Uint8Array>;
config: BlockstoreLevelConfig;

db: LevelDatabase<string, Uint8Array>;

/**
* @param location - must be a directory path (relative or absolute) where LevelDB will store its
* files, or in browsers, the name of
* the {@link https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase IDBDatabase}
* to be opened.
*/
constructor(location: string) {
this.db = new Level(location, { keyEncoding: 'utf8', valueEncoding: 'binary' });
constructor(location: string, config: BlockstoreLevelConfig = {}) {
this.config = {
createLevelDatabase,
...config
};

this.db = this.config.createLevelDatabase<string, Uint8Array>(location, { keyEncoding: 'utf8', valueEncoding: 'binary' })
}

async open(): Promise<void> {
Expand Down Expand Up @@ -130,3 +138,7 @@ export class BlockstoreLevel implements Blockstore {
throw new Error('not implemented');
}
}

type BlockstoreLevelConfig = {
createLevelDatabase?: typeof createLevelDatabase,
};
20 changes: 18 additions & 2 deletions src/store/data-store-level.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { BlockstoreLevel } from './blockstore-level.js';
import { CID } from 'multiformats/cid';
import { createLevelDatabase } from './create-level.js';
import { DataStore } from './data-store.js';
import { exporter } from 'ipfs-unixfs-exporter';
import { importer } from 'ipfs-unixfs-importer';
Expand All @@ -10,10 +11,20 @@ import { Readable } from 'readable-stream';
* Leverages LevelDB under the hood.
*/
export class DataStoreLevel implements DataStore {
config: DataStoreLevelConfig;

blockstore: BlockstoreLevel;

constructor(blockstoreLocation: string = 'DATASTORE') {
this.blockstore = new BlockstoreLevel(blockstoreLocation);
constructor(config: DataStoreLevelConfig = {}) {
this.config = {
blockstoreLocation: 'DATASTORE',
createLevelDatabase,
...config
};

this.blockstore = new BlockstoreLevel(this.config.blockstoreLocation, {
createLevelDatabase: this.config.createLevelDatabase,
});
}

public async open(): Promise<void> {
Expand Down Expand Up @@ -82,3 +93,8 @@ export class DataStoreLevel implements DataStore {
await this.blockstore.clear();
}
}

type DataStoreLevelConfig = {
blockstoreLocation?: string,
createLevelDatabase?: typeof createLevelDatabase,
};
9 changes: 7 additions & 2 deletions src/store/message-store-level.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import searchIndex from 'search-index';
import { abortOr } from '../utils/abort.js';
import { BlockstoreLevel } from './blockstore-level.js';
import { CID } from 'multiformats/cid';
import { createLevelDatabase } from './create-level.js';
import { RangeCriterion } from '../interfaces/records/types.js';
import { sha256 } from 'multiformats/hashes/sha2';

Expand All @@ -19,7 +20,7 @@ import { sha256 } from 'multiformats/hashes/sha2';
export class MessageStoreLevel implements MessageStore {
config: MessageStoreLevelConfig;

public readonly blockstore: BlockstoreLevel;
blockstore: BlockstoreLevel;

// levelDB doesn't natively provide the querying capabilities needed for DWN,
// to accommodate, we're leveraging a level-backed inverted index.
Expand All @@ -36,10 +37,13 @@ export class MessageStoreLevel implements MessageStore {
this.config = {
blockstoreLocation : 'BLOCKSTORE',
indexLocation : 'INDEX',
createLevelDatabase,
...config
};

this.blockstore = new BlockstoreLevel(this.config.blockstoreLocation);
this.blockstore = new BlockstoreLevel(this.config.blockstoreLocation, {
createLevelDatabase: this.config.createLevelDatabase,
});
}

async open(): Promise<void> {
Expand Down Expand Up @@ -242,4 +246,5 @@ type RangeSearchIndexTerm = {
type MessageStoreLevelConfig = {
blockstoreLocation?: string,
indexLocation?: string,
createLevelDatabase?: typeof createLevelDatabase,
};
4 changes: 3 additions & 1 deletion tests/dwn.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ describe('DWN', () => {
indexLocation : 'TEST-INDEX'
});

dataStore = new DataStoreLevel('TEST-DATASTORE');
dataStore = new DataStoreLevel({
blockstoreLocation: 'TEST-DATASTORE'
});

dwn = await Dwn.create({ messageStore, dataStore });
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ describe('ProtocolsConfigureHandler.handle()', () => {
indexLocation : 'TEST-INDEX'
});

dataStore = new DataStoreLevel('TEST-DATASTORE');
dataStore = new DataStoreLevel({
blockstoreLocation: 'TEST-DATASTORE'
});

dwn = await Dwn.create({ didResolver, messageStore, dataStore });
});
Expand Down
4 changes: 3 additions & 1 deletion tests/interfaces/protocols/handlers/protocols-query.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ describe('ProtocolsQueryHandler.handle()', () => {
indexLocation : 'TEST-INDEX'
});

dataStore = new DataStoreLevel('TEST-DATASTORE');
dataStore = new DataStoreLevel({
blockstoreLocation: 'TEST-DATASTORE'
});

dwn = await Dwn.create({ didResolver, messageStore, dataStore });
});
Expand Down
4 changes: 3 additions & 1 deletion tests/interfaces/records/handlers/records-delete.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ describe('RecordsDeleteHandler.handle()', () => {
indexLocation : 'TEST-INDEX'
});

dataStore = new DataStoreLevel('TEST-DATASTORE');
dataStore = new DataStoreLevel({
blockstoreLocation: 'TEST-DATASTORE'
});

dwn = await Dwn.create({ didResolver, messageStore, dataStore });
});
Expand Down
4 changes: 3 additions & 1 deletion tests/interfaces/records/handlers/records-query.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ describe('RecordsQueryHandler.handle()', () => {
indexLocation : 'TEST-INDEX'
});

dataStore = new DataStoreLevel('TEST-DATASTORE');
dataStore = new DataStoreLevel({
blockstoreLocation: 'TEST-DATASTORE'
});

dwn = await Dwn.create({ didResolver, messageStore, dataStore });
});
Expand Down
4 changes: 3 additions & 1 deletion tests/interfaces/records/handlers/records-read.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ describe('RecordsReadHandler.handle()', () => {
indexLocation : 'TEST-INDEX'
});

dataStore = new DataStoreLevel('TEST-DATASTORE');
dataStore = new DataStoreLevel({
blockstoreLocation: 'TEST-DATASTORE'
});

dwn = await Dwn.create({ didResolver, messageStore, dataStore });
});
Expand Down
4 changes: 3 additions & 1 deletion tests/interfaces/records/handlers/records-write.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ describe('RecordsWriteHandler.handle()', () => {
indexLocation : 'TEST-INDEX'
});

dataStore = new DataStoreLevel('TEST-DATASTORE');
dataStore = new DataStoreLevel({
blockstoreLocation: 'TEST-DATASTORE'
});

dwn = await Dwn.create({ didResolver, messageStore, dataStore });
});
Expand Down
19 changes: 19 additions & 0 deletions tests/store/message-store.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Message } from '../../src/core/message.js';
import { MessageStoreLevel } from '../../src/store/message-store-level.js';
import { RecordsWriteMessage } from '../../src/interfaces/records/types.js';
import { TestDataGenerator } from '../utils/test-data-generator.js';
import { createLevelDatabase, LevelDatabase, LevelDatabaseOptions } from '../../src/store/create-level.js';

let messageStore: MessageStoreLevel;

Expand Down Expand Up @@ -161,4 +162,22 @@ describe('MessageStoreLevel Tests', () => {
expect(results.length).to.equal(0);
});
});

describe('createLevelDatabase', function () {
it('should be called if provided', async () => {
let called = 0;

new MessageStoreLevel({
blockstoreLocation : 'TEST-BLOCKSTORE',
indexLocation : 'TEST-INDEX',
createLevelDatabase<K, V>(location, options?: LevelDatabaseOptions<K, V>): LevelDatabase<K, V> {
++called;
expect(location).to.equal('TEST-BLOCKSTORE');
return createLevelDatabase(location, options);
}
});

expect(called).to.equal(1);
});
});
});

0 comments on commit d38fc19

Please sign in to comment.