Skip to content
This repository has been archived by the owner on Aug 9, 2021. It is now read-only.

Commit

Permalink
Merge pull request #428 from 3box/release/1.8.0
Browse files Browse the repository at this point in the history
Release/1.8.0
  • Loading branch information
oed authored May 9, 2019
2 parents 6acc347 + 2793273 commit b194e01
Show file tree
Hide file tree
Showing 14 changed files with 196 additions and 14 deletions.
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,19 @@ await box.private.set('email', '[email protected]')
await box.private.remove('email')
```

##### Set multiple fields at once:
```js
const fields = ['name', 'website', 'employer']
const values = ['Jon Schwartz', 'openworklabs.com', 'Open Work Labs']

await box.public.setMultiple(fields, values)

const privateFields = ['age', 'coinBalance']
const privateValues = ['xxx', 'yyy']

await box.private.setMultiple(privateFields, privateValues)
```

<!-- commenting this out for now, not really needed when we're not using the iframe
#### IPFS Configs
Expand Down Expand Up @@ -143,7 +156,7 @@ For the fully detailed spec, view the [documentation](https://github.com/3box/3b
**WARNING: this is an experimental feature, the API will likely change in the future!**

#### Viewing a Thread
You can get all posts made in a thread without opening a space. This is great for allowing visitors of your site view comments made by other users. This is achieved by calling the `getThread` method on the Box object.
You can get all posts made in a thread without opening a space. This is great for allowing visitors of your site view comments made by other users. This is achieved by calling the `getThread` method on the Box object.
```js
const posts = await Box.getThread(spaceName, threadName)
console.log(posts)
Expand Down Expand Up @@ -518,6 +531,7 @@ Check if the given address is logged in
* [.get(key)](#KeyValueStore+get) ⇒ <code>String</code>
* [.getMetadata(key)](#KeyValueStore+getMetadata) ⇒ <code>Metadata</code>
* [.set(key, value)](#KeyValueStore+set) ⇒ <code>Boolean</code>
* [.setMultiple(keys, values)](#KeyValueStore+setMultiple) ⇒ <code>Boolean</code>
* [.remove(key)](#KeyValueStore+remove) ⇒ <code>Boolean</code>
<a name="new_KeyValueStore_new"></a>
Expand Down Expand Up @@ -577,6 +591,19 @@ Set a value for the given key
| key | <code>String</code> | the key |
| value | <code>String</code> | the value |
<a name="KeyValueStore+setMultiple"></a>
#### keyValueStore.setMultiple(keys, values) ⇒ <code>Boolean</code>
Set multiple values for multiple keys
**Kind**: instance method of [<code>KeyValueStore</code>](#KeyValueStore)
**Returns**: <code>Boolean</code> - true if successful, throw error if not
| Param | Type | Description |
| --- | --- | --- |
| keys | <code>Array.&lt;String&gt;</code> | the keys |
| values | <code>Array.&lt;String&gt;</code> | the values |
<a name="KeyValueStore+remove"></a>
#### keyValueStore.remove(key) ⇒ <code>Boolean</code>
Expand Down
5 changes: 5 additions & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Release Notes

## v1.8.0 - 2019-05-09
* Feature: Add `setMultiple` method, enables multiple fields to be set at once.

Special thanks to @Schwartz10 for contributing this feature!

## v1.7.2 - 2019-04-30
* Fix: Don't allow setting values wihtout a 'key'
* Fix: Ensure that linkProfile only happens once
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "3box",
"version": "1.7.2",
"version": "1.8.0",
"description": "Interact with user data",
"main": "lib/3box.js",
"directories": {
Expand Down Expand Up @@ -34,14 +34,14 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/uport-project/3box-js.git"
"url": "git+https://github.com/3box/3box-js.git"
},
"author": "3box <[email protected]>",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/uport-project/3box-js/issues"
"url": "https://github.com/3box/3box-js/issues"
},
"homepage": "https://github.com/uport-project/3box-js#readme",
"homepage": "https://github.com/3box/3box-js#readme",
"dependencies": {
"@babel/runtime": "^7.1.2",
"did-jwt": "^0.1.1",
Expand Down
15 changes: 14 additions & 1 deletion readme-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,19 @@ await box.private.set('email', '[email protected]')
await box.private.remove('email')
```

##### Set multiple fields at once:
```js
const fields = ['name', 'website', 'employer']
const values = ['Jon Schwartz', 'openworklabs.com', 'Open Work Labs']

await box.public.setMultiple(fields, values)

const privateFields = ['age', 'coinBalance']
const privateValues = ['xxx', 'yyy']

await box.private.setMultiple(privateFields, privateValues)
```

<!-- commenting this out for now, not really needed when we're not using the iframe
#### IPFS Configs
Expand Down Expand Up @@ -143,7 +156,7 @@ For the fully detailed spec, view the [documentation](https://github.com/3box/3b
**WARNING: this is an experimental feature, the API will likely change in the future!**

#### Viewing a Thread
You can get all posts made in a thread without opening a space. This is great for allowing visitors of your site view comments made by other users. This is achieved by calling the `getThread` method on the Box object.
You can get all posts made in a thread without opening a space. This is great for allowing visitors of your site view comments made by other users. This is achieved by calling the `getThread` method on the Box object.
```js
const posts = await Box.getThread(spaceName, threadName)
console.log(posts)
Expand Down
12 changes: 12 additions & 0 deletions src/__mocks__/keyValueStore.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { throwIfNotEqualLenArrays } from '../utils'

class KeyValueStore {
constructor (orbitdb, name, ensureConnected, threeId) {
this._orbitdb = orbitdb
Expand All @@ -17,6 +19,16 @@ class KeyValueStore {
return true
}

async setMultiple(keys, values) {
this._requireLoad()
throwIfNotEqualLenArrays(keys, values)
try {
keys.map((key, i) => this._db.set(key, values[i]))
} catch (error) {
throw new Error(error)
}
}

async remove (key) {
this._requireLoad()
this._db.remove(key)
Expand Down
8 changes: 8 additions & 0 deletions src/__tests__/keyValueStore.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ describe('KeyValueStore', () => {

it('should throw if not synced', async () => {
expect(keyValueStore.set('key', 'value')).rejects.toThrow(/_load must/)
expect(keyValueStore.setMultiple(['keys'], ['values'])).rejects.toThrow(/_load must/)
expect(keyValueStore.get('key')).rejects.toThrow(/_load must/)
expect(keyValueStore.remove('key')).rejects.toThrow(/_load must/)
})
Expand All @@ -58,6 +59,13 @@ describe('KeyValueStore', () => {
expect(ensureConnected).toHaveBeenCalledTimes(3)
})

it('should set and get multiple values correctly', async () => {
await keyValueStore.setMultiple(['key4', 'key5'], ['yoyo', 'ma'])
expect(await keyValueStore.get('key4')).toEqual('yoyo')
expect(await keyValueStore.get('key5')).toEqual('ma')
expect(ensureConnected).toHaveBeenCalledTimes(1)
})

it('should remove values correctly', async () => {
await keyValueStore.remove('key3')
expect(await keyValueStore.get('key3')).toBeUndefined()
Expand Down
17 changes: 17 additions & 0 deletions src/__tests__/privateStore.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,17 @@ describe('PrivateStore', () => {
expect(privateStore._decryptEntry(retreived)).toEqual('12345')
})

it('should set multiple values correctly', async () => {
await privateStore._load()
await privateStore.setMultiple(['key4', 'key5'], ['yoyo', 'ma'])
let dbkey = privateStore._genDbKey('key4')
let retreived = privateStore._db.get(dbkey)
expect(privateStore._decryptEntry(retreived)).toEqual('yoyo')
dbkey = privateStore._genDbKey('key5')
retreived = privateStore._db.get(dbkey)
expect(privateStore._decryptEntry(retreived)).toEqual('ma')
})

it('should get values correctly', async () => {
let value = await privateStore.get('key1')
expect(value).toEqual('value1')
Expand All @@ -97,6 +108,12 @@ describe('PrivateStore', () => {

value = await privateStore.get('key3')
expect(value).toEqual('12345')

value = await privateStore.get('key4')
expect(value).toEqual('yoyo')

value = await privateStore.get('key5')
expect(value).toEqual('ma')
})

it('should remove values correctly', async () => {
Expand Down
16 changes: 15 additions & 1 deletion src/__tests__/publicStore.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,21 @@ describe('PublicStore', () => {
expect(publicStore.set()).rejects.toEqual(new Error('key is a required argument'))
})

it('should set multiple keys', async () => {
await publicStore._load()
await publicStore.setMultiple(['key2', 'key3'], ['value2', 'value3'])
const value2 = await publicStore.get('key2')
const value3 = await publicStore.get('key3')
expect(value2).toEqual('value2')
expect(value3).toEqual('value3')
})

it('should throw an error if multiple keys are not arrays of equal length arrays', async () => {
expect(publicStore.setMultiple(['key'], ['value', 'value1'])).rejects.toEqual(new Error('Arrays must be of the same length'))
expect(publicStore.setMultiple('key', ['value', 'value1'])).rejects.toEqual(new Error('One or more arguments are not an array'))
})

it('should return profile correctly', async () => {
expect(await publicStore.all()).toEqual({ key1: 'value1' })
expect(await publicStore.all()).toEqual({ key1: 'value1', key2: 'value2', key3: 'value3' })
})
})
36 changes: 32 additions & 4 deletions src/__tests__/space.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,20 @@ describe('Space', () => {
expect(await space.public.get('k2')).toBeUndefined()
})

it('setMultiple works correctly', async () => {
await space.public.setMultiple(['k4', 'k5', 'k6'], ['v4', 'v5', 'v6'])
expect(await space.public.get('k4')).toEqual('v4')
expect(await space.public.get('k5')).toEqual('v5')
expect(await space.public.get('k6')).toEqual('v6')
await space.public.remove('k6')
expect(await space.public.get('k6')).toBeUndefined()

expect(space.public.setMultiple(['key'], ['value', 'value1'])).rejects.toEqual(new Error('Arrays must be of the same length'))
expect(space.public.setMultiple('key', ['value', 'value1'])).rejects.toEqual(new Error('One or more arguments are not an array'))
})

it('log should only return public values', async () => {
const refLog = [ { key: 'k1', op: 'PUT', timeStamp: 123, value: 'v1' }, { key: 'k3', op: 'PUT', timeStamp: 123, value: 'v3' } ]
const refLog = [{ key: 'k1', op: 'PUT', timeStamp: 123, value: 'v1' }, { key: 'k3', op: 'PUT', timeStamp: 123, value: 'v3' }, { key: 'k4', op: 'PUT', timeStamp: 123, value: 'v4' }, { key: 'k5', op: 'PUT', timeStamp: 123, value: 'v5' } ]
const log1 = space.public.log
expect(log1).toEqual(refLog)
space._store.set('key', 'value')
Expand All @@ -110,7 +122,9 @@ describe('Space', () => {
it('all should return all values from public store', async () => {
expect(await space.public.all()).toEqual({
k1: 'v1',
k3: 'v3'
k3: 'v3',
k4: 'v4',
k5: 'v5',
})
})

Expand All @@ -132,8 +146,20 @@ describe('Space', () => {
expect(await space.private.get('k2')).toEqual(null)
})

it('setMultiple works correctly', async () => {
await space.private.setMultiple(['k4', 'k5', 'k6'], ['sv4', 'sv5', 'sv6'])
expect(await space.private.get('k4')).toEqual('sv4')
expect(await space.private.get('k5')).toEqual('sv5')
expect(await space.private.get('k6')).toEqual('sv6')
await space.private.remove('k6')
expect(await space.private.get('k6')).toEqual(null)

expect(space.private.setMultiple(['key'], ['value', 'value1'])).rejects.toEqual(new Error('Arrays must be of the same length'))
expect(space.private.setMultiple('key', ['value', 'value1'])).rejects.toEqual(new Error('One or more arguments are not an array'))
})

it('log should only return private values', async () => {
const refLog = [ { key: 'k1', op: 'PUT', timeStamp: 123, value: 'sv1' }, { key: 'k3', op: 'PUT', timeStamp: 123, value: 'sv3' } ]
const refLog = [{ key: 'k1', op: 'PUT', timeStamp: 123, value: 'sv1' }, { key: 'k3', op: 'PUT', timeStamp: 123, value: 'sv3' }, { key: 'k4', op: 'PUT', timeStamp: 123, value: 'sv4' }, { key: 'k5', op: 'PUT', timeStamp: 123, value: 'sv5' } ]
const log1 = space.private.log
expect(log1).toEqual(refLog)
space._store.set('key', 'value')
Expand All @@ -144,7 +170,9 @@ describe('Space', () => {
it('all should return all values from private store', async () => {
expect(await space.private.all()).toEqual({
k1: 'sv1',
k3: 'sv3'
k3: 'sv3',
k4: 'sv4',
k5: 'sv5'
})
})

Expand Down
26 changes: 25 additions & 1 deletion src/keyValueStore.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { throwIfUndefined } = require('./utils/index')
const { throwIfUndefined, throwIfNotEqualLenArrays } = require('./utils/index')

class KeyValueStore {
/**
Expand Down Expand Up @@ -57,6 +57,30 @@ class KeyValueStore {
return true
}

/**
* Set multiple values for multiple keys
*
* @param {Array<String>} keys the keys
* @param {Array<String>} values the values
* @return {Boolean} true if successful, throw error if not
*/
async setMultiple (keys, values) {
throwIfNotEqualLenArrays(keys, values)
this._requireLoad()
this._ensureConnected()
try {
await keys.reduce(async (previousPromise, nextKey, index) => {
await previousPromise
throwIfUndefined(nextKey, 'key')
const timeStamp = new Date().getTime()
return this._db.put(nextKey, { value: values[index], timeStamp })
}, Promise.resolve())
return true
} catch (error) {
throw new Error(error)
}
}

/**
* Remove the value for the given key
*
Expand Down
7 changes: 7 additions & 0 deletions src/privateStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ class PrivateStore extends KeyValueStore {
return super.set(key, value)
}

async setMultiple (keys, values) {
utils.throwIfNotEqualLenArrays(keys, values)
const dbKeys = keys.map(this._genDbKey, this)
const encryptedValues = values.map(this._encryptEntry, this)
return super.setMultiple(dbKeys, encryptedValues)
}

async remove (key) {
key = this._genDbKey(key)
return super.remove(key)
Expand Down
8 changes: 7 additions & 1 deletion src/publicStore.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const KeyValueStore = require('./keyValueStore')
const { throwIfUndefined } = require('./utils/index')
const { throwIfUndefined, throwIfNotEqualLenArrays } = require('./utils/index')

class ProfileStore extends KeyValueStore {
constructor (orbitdb, name, linkProfile, ensureConnected, _3id) {
Expand All @@ -13,6 +13,12 @@ class ProfileStore extends KeyValueStore {
if (!opts.noLink) this._linkProfile()
return super.set(key, value)
}

async setMultiple (keys, values) {
throwIfNotEqualLenArrays(keys, values)
this._linkProfile()
return super.setMultiple(keys, values)
}
}

module.exports = ProfileStore
13 changes: 12 additions & 1 deletion src/space.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const KeyValueStore = require('./keyValueStore')
const Thread = require('./thread')
const { sha256Multihash, throwIfUndefined } = require('./utils')
const { sha256Multihash, throwIfUndefined, throwIfNotEqualLenArrays } = require('./utils')

const ENC_BLOCK_SIZE = 24
const nameToSpaceName = name => `3box.space.${name}.keyvalue`
Expand Down Expand Up @@ -122,6 +122,11 @@ const publicStoreReducer = (store) => {
throwIfUndefined(key, 'key')
store.set(PREFIX + key, value)
},
setMultiple: async (keys, values) => {
throwIfNotEqualLenArrays(keys, values)
const prefixedKeys = keys.map(key => PREFIX + key)
return store.setMultiple(prefixedKeys, values)
},
remove: async key => {
throwIfUndefined(key, 'key')
store.remove(PREFIX + key)
Expand Down Expand Up @@ -170,6 +175,12 @@ const privateStoreReducer = (store, keyring) => {
},
getMetadata: async key => store.getMetadata(dbKey(key)),
set: async (key, value) => store.set(dbKey(key), encryptEntry({ key, value })),
setMultiple: async (keys, values) => {
throwIfNotEqualLenArrays(keys, values)
const dbKeys = keys.map(dbKey)
const encryptedEntries = values.map((value, index) => encryptEntry({ key: keys[index], value }))
return store.setMultiple(dbKeys, encryptedEntries)
},
remove: async key => store.remove(dbKey(key)),
get log () {
return store.log.reduce((newLog, entry) => {
Expand Down
Loading

0 comments on commit b194e01

Please sign in to comment.