Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#244 - add a createLevelDatabase to allow customizing the Level used by BlockstoreLevel #249

Merged
merged 1 commit into from
Mar 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
};
11 changes: 11 additions & 0 deletions src/store/create-level.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { AbstractDatabaseOptions, AbstractLevel } from 'abstract-level';

import { Level } from 'level';

export type LevelDatabase<K, V> = AbstractLevel<string | Buffer | Uint8Array, K, V>;

export type LevelDatabaseOptions<K, V> = AbstractDatabaseOptions<K, V>;

export function createLevelDatabase<K, V>(location: string, options?: LevelDatabaseOptions<K, V>): LevelDatabase<K, V> {
return new Level(location, options);
}
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
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);
});
});
});