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 #390 from 3box/release/1.7.0
Browse files Browse the repository at this point in the history
Release/1.7.0
  • Loading branch information
oed authored Apr 12, 2019
2 parents ffa6895 + 97b382a commit 32ad00d
Show file tree
Hide file tree
Showing 13 changed files with 166 additions and 29 deletions.
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ Get the public data in a space of a given address with the given name
| name | <code>String</code> | A space name |
| opts | <code>Object</code> | Optional parameters |
| opts.profileServer | <code>String</code> | URL of Profile API server |
| opts.metadata | <code>String</code> | flag to retrieve metadata |

<a name="Box.getThread"></a>

Expand Down Expand Up @@ -431,6 +432,7 @@ Check if the given address is logged in
* [new KeyValueStore()](#new_KeyValueStore_new)
* [.log](#KeyValueStore+log) ⇒ <code>Array.&lt;Object&gt;</code>
* [.get(key)](#KeyValueStore+get) ⇒ <code>String</code>
* [.getMetadata(key)](#KeyValueStore+getMetadata) ⇒ <code>Metadata</code>
* [.set(key, value)](#KeyValueStore+set) ⇒ <code>Boolean</code>
* [.remove(key)](#KeyValueStore+remove) ⇒ <code>Boolean</code>

Expand Down Expand Up @@ -460,7 +462,19 @@ const log = store.log
Get the value of the given key

**Kind**: instance method of [<code>KeyValueStore</code>](#KeyValueStore)
**Returns**: <code>String</code> - the value associated with the key
**Returns**: <code>String</code> - the value associated with the key, undefined if there's no such key

| Param | Type | Description |
| --- | --- | --- |
| key | <code>String</code> | the key |

<a name="KeyValueStore+getMetadata"></a>

#### keyValueStore.getMetadata(key) ⇒ <code>Metadata</code>
Get metadata for for a given key

**Kind**: instance method of [<code>KeyValueStore</code>](#KeyValueStore)
**Returns**: <code>Metadata</code> - Metadata for the key, undefined if there's no such key

| Param | Type | Description |
| --- | --- | --- |
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.7.0 - 2019-04-12
* Feature: Add ability to get metadata for entries
* Feature: Add idUtils helper functions
* Feature: Send along DID when opening db with pinning node

## v1.6.2 - 2019-04-09
* Fix: Use correct key when subscribing to thread in a space.

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"name": "3box",
"version": "1.6.2",
"version": "1.7.0",
"description": "Interact with user data",
"main": "lib/3box.js",
"directories": {
"lib": "lib"
},
"scripts": {
"lint": "./node_modules/.bin/standard --verbose src/**",
"test": "rm -rf ./tmp ; jest --forceExit --detectOpenHandles --coverage --runInBand --testURL=\"http://localhost\"",
"test": "rm -rf ./tmp ; jest --forceExit --coverage --runInBand --testURL=\"http://localhost\"",
"build:es5": "rm -rf ./lib; ./node_modules/.bin/babel src --out-dir lib --ignore=src/__tests__/,src/__mocks__/",
"build:dist": "./node_modules/.bin/webpack --config webpack.config.js --mode=development",
"build:dist:dev": "./node_modules/.bin/webpack --config webpack.dev.config.js --mode=development",
Expand Down
18 changes: 14 additions & 4 deletions src/3box.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const PrivateStore = require('./privateStore')
const Verified = require('./verified')
const Space = require('./space')
const utils = require('./utils/index')
const idUtils = require('./utils/id')
const config = require('./config.js')
const API = require('./api')

Expand Down Expand Up @@ -92,7 +93,7 @@ class Box {

const onNewPeer = async (topic, peer) => {
if (peer === this.pinningNode.split('/').pop()) {
this._pubsub.publish(PINNING_ROOM, { type: 'PIN_DB', odbAddress: rootStoreAddress })
this._pubsub.publish(PINNING_ROOM, { type: 'PIN_DB', odbAddress: rootStoreAddress, did: this._3id.getDid() })
}
}

Expand Down Expand Up @@ -168,11 +169,17 @@ class Box {
* @return {Object} a json object with the profile for the given address
*/
static async getProfile (address, opts = {}) {
const metadata = opts.metadata
opts = Object.assign({ useCacheService: true }, opts)

let profile
if (opts.useCacheService) {
profile = await API.getProfile(address, opts.profileServer)
profile = await API.getProfile(address, opts.profileServer, { metadata })
} else {
if (metadata) {
throw new Error('getting metadata is not yet supported outside of the API')
}

const normalizedAddress = address.toLowerCase()
profile = await this._getProfileOrbit(normalizedAddress, opts)
}
Expand All @@ -198,10 +205,11 @@ class Box {
* @param {String} name A space name
* @param {Object} opts Optional parameters
* @param {String} opts.profileServer URL of Profile API server
* @param {String} opts.metadata flag to retrieve metadata
* @return {Object} a json object with the public space data
*/
static async getSpace (address, name, opts = {}) {
return API.getSpace(address, name, opts.profileServer)
return API.getSpace(address, name, opts.profileServer, opts)
}

/**
Expand Down Expand Up @@ -230,7 +238,7 @@ class Box {
}

static async _getProfileOrbit (address, opts = {}) {
if (utils.isMuportDID(address)) {
if (idUtils.isMuportDID(address)) {
throw new Error('DID are supported in the cached version only')
}

Expand Down Expand Up @@ -489,4 +497,6 @@ async function initIPFS (ipfs, iframeStore, ipfsOptions) {
}
}

Box.idUtils = idUtils

module.exports = Box
1 change: 0 additions & 1 deletion src/__tests__/3box.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ jest.mock('../utils/index', () => {
let linkmap = {}
let linkNum = 0
return {
isMuportDID: actualUtils.isMuportDID,
getMessageConsent: actualUtils.getMessageConsent,

openBoxConsent: jest.fn(async () => '0x8726348762348723487238476238746827364872634876234876234'),
Expand Down
26 changes: 26 additions & 0 deletions src/__tests__/idUtils.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const { isMuportDID, isClaim, verifyClaim } = require('../utils/id')

const CLAIM_1 = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJpYXQiOjE1NTQ5NzI2MDksImV4cCI6MTk1NzQ2MzQyMSwibmFtZSI6InVQb3J0IERldmVsb3BlciIsImlzcyI6ImRpZDp1cG9ydDoyb3NuZko0V3k3TEJBbTJuUEJYaXJlMVdmUW43NVJyVjZUcyJ9.e9H1ngK7Kto_Am3N9NAJWm8kj7NetGPbOoQtKw8y-C21ytj1zjDr99w63AtlFCytYkLRcHnTHSl0eByaZww5dg'
const INVALID_CLAIM_FORMAT = '%eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJpYXQiOjE1NTQ5NzI2MDksImV4cCI6MTk1NzQ2MzQyMSwibmFtZSI6InVQb3J0IERldmVsb3BlciIsImlzcyI6ImRpZDp1cG9ydDoyb3NuZko0V3k3TEJBbTJuUEJYaXJlMVdmUW43NVJyVjZUcyJ9.e9H1ngK7Kto_Am3N9NAJWm8kj7NetGPbOoQtKw8y-C21ytj1zjDr99w63AtlFCytYkLRcHnTHSl0eByaZww5dg'
const EXPIRED_CLAIM = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJpYXQiOjE1NTQ5NzM5NDgsImV4cCI6MTk1LCJuYW1lIjoidVBvcnQgRGV2ZWxvcGVyIiwiaXNzIjoiZGlkOnVwb3J0OjJvc25mSjRXeTdMQkFtMm5QQlhpcmUxV2ZRbjc1UnJWNlRzIn0.M_HupDVb7N4TFOUg4B_PU6XQm9TTx7S0klhMLT1U3zfpThA4DAT2L8HGeBDTMuGS3-nXVo8oDYORASEX_ecGsQ'

describe('basic utils tests', () => {
test('is muport did', () => {
expect(isMuportDID('abc')).toEqual(false)
expect(isMuportDID('did:example')).toEqual(false)
expect(isMuportDID('did:muport')).toEqual(false)
expect(isMuportDID('did:muport:Qmb9E8wLqjfAqfKhideoApU5g26Yz2Q2bSp6MSZmc5WrNr')).toEqual(true)
})

test('isClaim', async () => {
expect(await isClaim(CLAIM_1)).toEqual(true)
expect(await isClaim(INVALID_CLAIM_FORMAT)).toEqual(false)
expect(await isClaim(EXPIRED_CLAIM)).toEqual(true) // invalid claim will throw during verify
})

test('verifyClaim', async () => {
expect(verifyClaim(CLAIM_1)).resolves.toBeTruthy()
expect(verifyClaim(INVALID_CLAIM_FORMAT)).rejects.toBeTruthy()
expect(verifyClaim(EXPIRED_CLAIM)).rejects.toBeTruthy()
}, 100000)
})
18 changes: 18 additions & 0 deletions src/__tests__/keyValueStore.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,24 @@ describe('KeyValueStore', () => {
// await ipfs2.stop()
})

describe('metdata', () => {
it('should contain the metadata method', async () => {
await keyValueStore.set('some-key', 'some-value')

const v = await keyValueStore.get('some-key')
const m = await keyValueStore.getMetadata('some-key')

expect(v).toEqual('some-value')
expect(m).toBeDefined()
expect(m.timestamp).toBeDefined()
})

it('should return an undefined value for unknown key', async () => {
const m = await keyValueStore.getMetadata('a key so complex no one would set it')
expect(m).toBeUndefined()
})
})

describe('log', () => {

let storeNum = 0
Expand Down
52 changes: 38 additions & 14 deletions src/api.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const graphQLRequest = require('graphql-request').request
const utils = require('./utils/index')
const verifier = require('./utils/verifier')
const { isMuportDID } = require('./utils/id')
const config = require('./config.js')

const GRAPHQL_SERVER_URL = config.graphql_server_url
Expand All @@ -16,7 +17,7 @@ async function getRootStoreAddress (identifier, serverUrl = ADDRESS_SERVER_URL)
async function listSpaces (address, serverUrl = PROFILE_SERVER_URL) {
try {
// we await explicitly here to make sure the error is catch'd in the correct scope
if (utils.isMuportDID(address)) {
if (isMuportDID(address)) {
return await utils.fetchJson(serverUrl + '/list-spaces?did=' + encodeURIComponent(address))
} else {
return await utils.fetchJson(serverUrl + '/list-spaces?address=' + encodeURIComponent(address))
Expand All @@ -26,14 +27,28 @@ async function listSpaces (address, serverUrl = PROFILE_SERVER_URL) {
}
}

async function getSpace (address, name, serverUrl = PROFILE_SERVER_URL) {
async function getSpace (address, name, serverUrl = PROFILE_SERVER_URL, { metadata }) {
let url = `${serverUrl}/space`

try {
// we await explicitly here to make sure the error is catch'd in the correct scope
if (utils.isMuportDID(address)) {
return await utils.fetchJson(serverUrl + `/space?did=${encodeURIComponent(address)}&name=${encodeURIComponent(name)}`)
// Add first parameter: address or did
if (isMuportDID(address)) {
url = `${url}?did=${encodeURIComponent(address)}`
} else {
return await utils.fetchJson(serverUrl + `/space?address=${encodeURIComponent(address)}&name=${encodeURIComponent(name)}`)
url = `${url}?address=${encodeURIComponent(address.toLowerCase())}`
}

// Add name:
url = `${url}&name=${encodeURIComponent(name)}`

// Add metadata:
if (metadata) {
url = `${url}&metadata=${encodeURIComponent(metadata)}`
}

// Query:
// we await explicitly to make sure the error is catch'd in the correct scope
return await utils.fetchJson(url)
} catch (err) {
return {}
}
Expand All @@ -50,16 +65,25 @@ async function getThread (space, name, serverUrl = PROFILE_SERVER_URL) {
})
}

async function getProfile (address, serverUrl = PROFILE_SERVER_URL) {
async function getProfile (address, serverUrl = PROFILE_SERVER_URL, { metadata }) {
let url = `${serverUrl}/profile`

try {
// Note: we await explicitly to make sure the error is catch'd in the correct scope
if (utils.isMuportDID(address)) {
const normalized = encodeURIComponent(address) // uppercase is significant in did:muport
return await utils.fetchJson(serverUrl + '/profile?did=' + normalized)
// Add first parameter: address or did
if (isMuportDID(address)) {
url = `${url}?did=${encodeURIComponent(address)}`
} else {
const normalized = encodeURIComponent(address.toLowerCase())
return await utils.fetchJson(serverUrl + '/profile?address=' + normalized)
url = `${url}?address=${encodeURIComponent(address.toLowerCase())}`
}

// Add metadata:
if (metadata) {
url = `${url}&metadata=${encodeURIComponent(metadata)}`
}

// Query:
// we await explicitly to make sure the error is catch'd in the correct scope
return await utils.fetchJson(url)
} catch (err) {
return {} // empty profile
}
Expand All @@ -71,7 +95,7 @@ async function getProfiles (addressArray, opts = {}) {

// Split addresses on ethereum / dids
addressArray.forEach(address => {
if (utils.isMuportDID(address)) {
if (isMuportDID(address)) {
req.didList.push(address)
} else {
req.addressList.push(address)
Expand Down
30 changes: 26 additions & 4 deletions src/keyValueStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,22 @@ class KeyValueStore {
* Get the value of the given key
*
* @param {String} key the key
* @return {String} the value associated with the key
* @return {String} the value associated with the key, undefined if there's no such key
*/
async get (key) {
this._requireLoad()
const dbGetRes = await this._db.get(key)
return dbGetRes ? dbGetRes.value : dbGetRes
const x = await this._get(key)
return x ? x.value : x
}

/**
* Get metadata for for a given key
*
* @param {String} key the key
* @return {Metadata} Metadata for the key, undefined if there's no such key
*/
async getMetadata (key) {
const x = await this._get(key)
return x ? { timestamp: x.timeStamp } : x
}

/**
Expand Down Expand Up @@ -49,6 +59,18 @@ class KeyValueStore {
return true
}

/**
* Get the raw value of the given key
* @private
*
* @param {String} key the key
* @return {String} the value associated with the key
*/
async _get (key) {
this._requireLoad()
return this._db.get(key)
}

async _sync (numRemoteEntries) {
this._requireLoad()
// let toid = null
Expand Down
5 changes: 5 additions & 0 deletions src/privateStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ class PrivateStore extends KeyValueStore {
return encryptedEntry ? this._decryptEntry(encryptedEntry) : null
}

async getMetadata (key) {
// Note: assumes metadata is not encrypted.
return super.getMetadata(this._genDbKey(key))
}

async set (key, value) {
value = this._encryptEntry(value)
key = this._genDbKey(key)
Expand Down
2 changes: 2 additions & 0 deletions src/space.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ const publicStoreReducer = (store) => {
const PREFIX = 'pub_'
return {
get: async key => store.get(PREFIX + key),
getMetadata: async key => store.getMetadata(PREFIX + key),
set: async (key, value) => store.set(PREFIX + key, value),
remove: async key => store.remove(PREFIX + key),
get log () {
Expand Down Expand Up @@ -158,6 +159,7 @@ const privateStoreReducer = (store, keyring) => {
const entry = await store.get(dbKey(key))
return entry ? decryptEntry(entry).value : null
},
getMetadata: async key => store.getMetadata(dbKey(key)),
set: async (key, value) => store.set(dbKey(key), encryptEntry({ key, value })),
remove: async key => store.remove(dbKey(key)),
get log () {
Expand Down
15 changes: 15 additions & 0 deletions src/utils/id.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const didJWT = require('did-jwt')
const DID_MUPORT_PREFIX = 'did:muport:'

module.exports = {
isMuportDID: (address) => address.startsWith(DID_MUPORT_PREFIX),
isClaim: async (claim, opts = {}) => {
try {
await didJWT.decodeJWT(claim, opts)
return true
} catch (e) {
return false
}
},
verifyClaim: didJWT.verifyJWT
}
3 changes: 0 additions & 3 deletions src/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ const fetch = typeof window !== 'undefined' ? window.fetch : require('node-fetch
const Multihash = require('multihashes')
const sha256 = require('js-sha256').sha256

const DID_MUPORT_PREFIX = 'did:muport:'

const HTTPError = (status, message) => {
const e = new Error(message)
e.statusCode = status
Expand All @@ -16,7 +14,6 @@ const getMessageConsent = (did) => (

module.exports = {
getMessageConsent,
isMuportDID: (address) => address.startsWith(DID_MUPORT_PREFIX),

openBoxConsent: (fromAddress, ethereum) => {
const text = 'This app wants to view and update your 3Box profile.'
Expand Down

0 comments on commit 32ad00d

Please sign in to comment.