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

feat: adds .replace and .getAll methods to config #229

Merged
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
65 changes: 49 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# IPFS Repo JavaScript Implementation
# IPFS Repo JavaScript Implementation <!-- omit in toc -->

[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/)
Expand All @@ -14,11 +14,11 @@

This is the implementation of the [IPFS repo spec](https://github.com/ipfs/specs/blob/master/REPO.md) in JavaScript.

## Lead Maintainer
## Lead Maintainer <!-- omit in toc -->

[Alex Potsides](https://github.com/achingbrain)

## Table of Contents
## Table of Contents <!-- omit in toc -->

- [Background](#background)
- [Install](#install)
Expand All @@ -28,7 +28,40 @@ This is the implementation of the [IPFS repo spec](https://github.com/ipfs/specs
- [Use in a browser Using a script tag](#use-in-a-browser-using-a-script-tag)
- [Usage](#usage)
- [API](#api)
- [Setup](#setup)
- [`new Repo(path[, options])`](#new-repopath-options)
- [`Promise repo.init ()`](#promise-repoinit-)
- [`Promise repo.open ()`](#promise-repoopen-)
- [`Promise repo.close ()`](#promise-repoclose-)
- [`Promise<boolean> repo.exists ()`](#promiseboolean-repoexists-)
- [Repos](#repos)
- [`Promise repo.put (key, value:Buffer)`](#promise-repoput-key-valuebuffer)
- [`Promise<Buffer> repo.get (key)`](#promisebuffer-repoget-key)
- [`Promise<Boolean> repo.isInitialized ()`](#promiseboolean-repoisinitialized-)
- [`Promise repo.blocks.put (block:Block)`](#promise-repoblocksput-blockblock)
- [`Promise repo.blocks.putMany (blocks)`](#promise-repoblocksputmany-blocks)
- [`Promise<Buffer> repo.blocks.get (cid)`](#promisebuffer-repoblocksget-cid)
- [`repo.datastore`](#repodatastore)
- [Config](#config)
- [`Promise repo.config.set(key:string, value)`](#promise-repoconfigsetkeystring-value)
- [`Promise repo.config.replace(value)`](#promise-repoconfigreplacevalue)
- [`Promise<?> repo.config.get(key:string)`](#promise-repoconfiggetkeystring)
- [`Promise<Object> repo.config.getAll()`](#promiseobject-repoconfiggetall)
- [`Promise<boolean> repo.config.exists()`](#promiseboolean-repoconfigexists)
- [Version](#version)
- [`Promise<Number> repo.version.get ()`](#promisenumber-repoversionget-)
- [`Promise repo.version.set (version:Number)`](#promise-repoversionset-versionnumber)
- [API Addr](#api-addr)
- [`Promise<String> repo.apiAddr.get ()`](#promisestring-repoapiaddrget-)
- [`Promise repo.apiAddr.set (value)`](#promise-repoapiaddrset-value)
- [Status](#status)
- [`Promise<Object> repo.stat ()`](#promiseobject-repostat-)
- [Lock](#lock)
- [`Promise lock.lock (dir)`](#promise-locklock-dir)
- [`Promise closer.close ()`](#promise-closerclose-)
- [`Promise<boolean> lock.locked (dir)`](#promiseboolean-locklocked-dir)
- [Notes](#notes)
- [Migrations](#migrations)
- [Contribute](#contribute)
- [License](#license)

Expand Down Expand Up @@ -210,13 +243,11 @@ Datastore:
This contains a full implementation of [the `interface-datastore` API](https://github.com/ipfs/interface-datastore#api).


### Utils

#### `repo.config`
### Config

Instead of using `repo.set('config')` this exposes an API that allows you to set and get a decoded config object, as well as, in a safe manner, change any of the config values individually.

##### `Promise repo.config.set(key:string, value)`
#### `Promise repo.config.set(key:string, value)`

Set a config value. `value` can be any object that is serializable to JSON.

Expand All @@ -228,11 +259,11 @@ const config = await repo.config.get()
assert.equal(config.a.b.c, 'c value')
```

##### `Promise repo.config.set(value)`
#### `Promise repo.config.replace(value)`

Set the whole config value. `value` can be any object that is serializable to JSON.

##### `Promise<?> repo.config.get(key:string)`
#### `Promise<?> repo.config.get(key:string)`

Get a config value. Returned promise resolves to the same type that was set before.

Expand All @@ -243,25 +274,25 @@ const value = await repo.config.get('a.b.c')
console.log('config.a.b.c = ', value)
```

##### `Promise<Object> repo.config.get()`
#### `Promise<Object> repo.config.getAll()`

Get the entire config value.

#### `Promise<boolean> repo.config.exists()`

Whether the config sub-repo exists.

#### `repo.version`
### Version

##### `Promise<Number> repo.version.get ()`
#### `Promise<Number> repo.version.get ()`

Gets the repo version (an integer).

##### `Promise repo.version.set (version:Number)`
#### `Promise repo.version.set (version:Number)`

Sets the repo version

#### `repo.apiAddr`
### API Addr

#### `Promise<String> repo.apiAddr.get ()`

Expand All @@ -273,7 +304,9 @@ Sets the API address.

* `value` should be a [Multiaddr](https://github.com/multiformats/js-multiaddr) or a String representing a valid one.

### `Promise<Object> repo.stat ()`
### Status

#### `Promise<Object> repo.stat ()`

Gets the repo status.

Expand Down Expand Up @@ -304,7 +337,7 @@ Sets the lock if one does not already exist. If a lock already exists, should th

Returns `closer`, where `closer` has a `close` method for removing the lock.

##### `Promise closer.close ()`
#### `Promise closer.close ()`

Closes the lock created by `lock.open`

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"devDependencies": {
"aegir": "^21.4.5",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"dirty-chai": "^2.0.1",
"lodash": "^4.17.11",
"memdown": "^5.1.0",
Expand Down
53 changes: 48 additions & 5 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,33 @@ module.exports = (store) => {
/**
* Get the current configuration from the repo.
*
* @param {Object} options - options
* @param {AbortSignal} options.signal - abort this config read
* @returns {Promise<Object>}
*/
async getAll (options = {}) { // eslint-disable-line require-await
return configStore.get(undefined, options)
},

/**
* Get the value for the passed configuration key from the repo.
*
* @param {String} key - the config key to get
* @param {Object} options - options
* @param {AbortSignal} options.signal - abort this config read
* @returns {Promise<Object>}
*/
async get (key) {
async get (key, options = {}) {
if (!key) {
key = undefined
}

const encodedValue = await store.get(configKey)

if (options.signal && options.signal.aborted) {
return
}

const config = JSON.parse(encodedValue.toString())
if (key !== undefined && !_has(config, key)) {
throw new errors.NotFoundError(`Key ${key} does not exist in config`)
Expand All @@ -40,9 +58,11 @@ module.exports = (store) => {
*
* @param {String} key - the config key to be written
* @param {Object} value - the config value to be written
* @param {Object} options - options
* @param {AbortSignal} options.signal - abort this config write
* @returns {void}
*/
async set (key, value) { // eslint-disable-line require-await
async set (key, value, options = {}) { // eslint-disable-line require-await
if (arguments.length === 1) {
value = key
key = undefined
Expand All @@ -54,10 +74,29 @@ module.exports = (store) => {
throw errcode(new Error('Invalid value type: ' + typeof value), 'ERR_INVALID_VALUE')
}

return setQueue.add(() => _doSet({
return setQueue.add(() => _maybeDoSet({
key: key,
value: value
}))
}, options.signal))
},

/**
* Set the current configuration for this repo.
*
* @param {Object} value - the config value to be written
* @param {Object} options - options
* @param {AbortSignal} options.signal - abort this config write
* @returns {void}
*/
async replace (value, options = {}) { // eslint-disable-line require-await
if (!value || Buffer.isBuffer(value)) {
throw errcode(new Error('Invalid value type: ' + typeof value), 'ERR_INVALID_VALUE')
}

return setQueue.add(() => _maybeDoSet({
key: undefined,
value: value
}, options.signal))
},

/**
Expand All @@ -72,7 +111,11 @@ module.exports = (store) => {

return configStore

async function _doSet (m) {
async function _maybeDoSet (m, signal) {
if (signal && signal.aborted) {
return
}

const key = m.key
const value = m.value
if (key) {
Expand Down
53 changes: 11 additions & 42 deletions test/blockstore-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
const chai = require('chai')
chai.use(require('dirty-chai'))
const expect = chai.expect
const assert = chai.assert
const Block = require('ipfs-block')
const CID = require('cids')
const _ = require('lodash')
Expand Down Expand Up @@ -121,13 +120,8 @@ module.exports = (repo) => {
expect(commitInvoked).to.be.true()
})

it('returns an error on invalid block', async () => {
try {
await repo.blocks.put('hello')
assert.fail()
} catch (err) {
expect(err).to.exist()
}
it('returns an error on invalid block', () => {
return expect(repo.blocks.put('hello')).to.eventually.be.rejected()
})
})

Expand Down Expand Up @@ -155,14 +149,8 @@ module.exports = (repo) => {
}))
})

it('returns an error on invalid block', async () => {
try {
await repo.blocks.get('woot')
} catch (err) {
expect(err).to.exist()
return
}
assert.fail()
it('returns an error on invalid block', () => {
return expect(repo.blocks.get('woot')).to.eventually.be.rejected()
})

it('should get block stored under v0 CID with a v1 CID', async () => {
Expand All @@ -184,25 +172,16 @@ module.exports = (repo) => {
expect(block.data).to.eql(data)
})

it('throws when passed an invalid cid', async () => {
try {
await repo.blocks.get('foo')
throw new Error('Should have thrown')
} catch (err) {
expect(err.code).to.equal('ERR_INVALID_CID')
}
it('throws when passed an invalid cid', () => {
return expect(repo.blocks.get('foo')).to.eventually.be.rejected().with.property('code', 'ERR_INVALID_CID')
})

it('throws ERR_NOT_FOUND when requesting non-dag-pb CID that is not in the store', async () => {
const data = Buffer.from(`TEST${Date.now()}`)
const hash = await multihashing(data, 'sha2-256')
const cid = new CID(1, 'dag-cbor', hash)

try {
await repo.blocks.get(cid)
} catch (err) {
expect(err.code).to.equal('ERR_NOT_FOUND')
}
await expect(repo.blocks.get(cid)).to.eventually.be.rejected().with.property('code', 'ERR_NOT_FOUND')
})

it('throws unknown error encountered when getting a block', async () => {
Expand Down Expand Up @@ -276,13 +255,8 @@ module.exports = (repo) => {
expect(exists).to.eql(true)
})

it('throws when passed an invalid cid', async () => {
try {
await repo.blocks.has('foo')
throw new Error('Should have thrown')
} catch (err) {
expect(err.code).to.equal('ERR_INVALID_CID')
}
it('throws when passed an invalid cid', () => {
return expect(repo.blocks.has('foo')).to.eventually.be.rejected().with.property('code', 'ERR_INVALID_CID')
})

it('returns false when requesting non-dag-pb CID that is not in the store', async () => {
Expand All @@ -302,13 +276,8 @@ module.exports = (repo) => {
expect(exists).to.equal(false)
})

it('throws when passed an invalid cid', async () => {
try {
await repo.blocks.delete('foo')
throw new Error('Should have thrown')
} catch (err) {
expect(err.code).to.equal('ERR_INVALID_CID')
}
it('throws when passed an invalid cid', () => {
return expect(repo.blocks.delete('foo')).to.eventually.be.rejected().with.property('code', 'ERR_INVALID_CID')
})
})
})
Expand Down
4 changes: 1 addition & 3 deletions test/blockstore-utils-test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
/* eslint-env mocha */
'use strict'

const chai = require('chai')
chai.use(require('dirty-chai'))
const { expect } = chai
const { expect } = require('./utils/chai')
const { Key } = require('interface-datastore')
const CID = require('cids')
const Repo = require('../src')
Expand Down
Loading