Refactor Manifest

This commit is contained in:
haad 2023-04-13 07:59:30 +03:00
parent d979b98562
commit f331b1b458
3 changed files with 97 additions and 72 deletions

View File

@ -2,30 +2,59 @@ import * as Block from 'multiformats/block'
import * as dagCbor from '@ipld/dag-cbor'
import { sha256 } from 'multiformats/hashes/sha2'
import { base58btc } from 'multiformats/bases/base58'
import { ComposedStorage, IPFSBlockStorage, LRUStorage } from './storage/index.js'
const codec = dagCbor
const hasher = sha256
const hashStringEncoding = base58btc
// Creates a DB manifest file and saves it in IPFS
export default async ({ storage, name, type, accessController, meta }) => {
if (!storage) throw new Error('storage is required')
if (!name) throw new Error('name is required')
if (!type) throw new Error('type is required')
if (!accessController) throw new Error('accessController is required')
const manifest = Object.assign(
{
name,
type,
accessController
},
// meta field is only added to manifest if meta parameter is defined
meta !== undefined ? { meta } : {}
const Manifest = async ({ ipfs, storage } = {}) => {
storage = storage || await ComposedStorage(
await LRUStorage({ size: 1000 }),
await IPFSBlockStorage({ ipfs, pin: true })
)
const { cid, bytes } = await Block.encode({ value: manifest, codec, hasher })
const hash = cid.toString(hashStringEncoding)
await storage.put(hash, bytes)
return { hash, manifest }
const get = async (address) => {
const bytes = await storage.get(address)
const { value } = await Block.decode({ bytes, codec, hasher })
return value
}
const create = async ({ name, type, accessController, meta }) => {
if (!name) throw new Error('name is required')
if (!type) throw new Error('type is required')
if (!accessController) throw new Error('accessController is required')
const manifest = Object.assign(
{
name,
type,
accessController
},
// meta field is only added to manifest if meta parameter is defined
meta !== undefined ? { meta } : {}
)
const { cid, bytes } = await Block.encode({ value: manifest, codec, hasher })
const hash = cid.toString(hashStringEncoding)
await storage.put(hash, bytes)
return {
hash,
manifest
}
}
const close = async () => {
await storage.close()
}
return {
get,
create,
close
}
}
export default Manifest

View File

@ -1,18 +1,12 @@
import { Events, KeyValue, Documents } from './db/index.js'
import { ComposedStorage, IPFSBlockStorage, LRUStorage } from './storage/index.js'
import KeyStore from './key-store.js'
import { Identities } from './identities/index.js'
import OrbitDBAddress, { isValidAddress } from './address.js'
import DBManifest from './manifest.js'
import Manifests from './manifest.js'
import { createId } from './utils/index.js'
import pathJoin from './utils/path-join.js'
import * as Block from 'multiformats/block'
import * as dagCbor from '@ipld/dag-cbor'
import { sha256 } from 'multiformats/hashes/sha2'
import * as AccessControllers from './access-controllers/index.js'
const codec = dagCbor
const hasher = sha256
import IPFSAccessController from './access-controllers/ipfs.js'
// Mapping for database types
const databaseTypes = {
@ -28,6 +22,8 @@ const addDatabaseType = (type, store) => {
databaseTypes[type] = store
}
const DefaultAccessController = IPFSAccessController
const OrbitDB = async ({ ipfs, id, identity, keystore, directory } = {}) => {
if (ipfs == null) {
throw new Error('IPFS instance is a required argument. See https://github.com/orbitdb/orbit-db/blob/master/API.md#createinstance')
@ -40,10 +36,7 @@ const OrbitDB = async ({ ipfs, id, identity, keystore, directory } = {}) => {
const identities = await Identities({ ipfs, keystore })
identity = identity || await identities.createIdentity({ id })
const manifestStorage = await ComposedStorage(
await LRUStorage({ size: 1000 }),
await IPFSBlockStorage({ ipfs, pin: true })
)
const manifests = await Manifests({ ipfs })
let databases = {}
@ -61,23 +54,20 @@ const OrbitDB = async ({ ipfs, id, identity, keystore, directory } = {}) => {
if (isValidAddress(address)) {
// If the address given was a valid OrbitDB address, eg. '/orbitdb/zdpuAuK3BHpS7NvMBivynypqciYCuy2UW77XYBPUYRnLjnw13'
const addr = OrbitDBAddress(address)
const bytes = await manifestStorage.get(addr.path)
const { value } = await Block.decode({ bytes, codec, hasher })
manifest = value
manifest = await manifests.get(addr.path)
const acType = manifest.accessController.split('/', 2).pop()
const acAddress = manifest.accessController.replaceAll(`/${acType}/`, '')
accessController = await AccessControllers.get(acType)()({ orbitdb: { open, identity, ipfs }, identities, address: acAddress })
AccessController = AccessControllers.get(acType)()
accessController = await AccessController({ orbitdb: { open, identity, ipfs }, identities, address: acAddress })
name = manifest.name
type = type || manifest.type
meta = manifest.meta
} else {
// If the address given was not valid, eg. just the name of the database
type = type || 'events'
AccessController = AccessController || AccessControllers.get('ipfs')({ storage: manifestStorage })
AccessController = AccessController || DefaultAccessController()
accessController = await AccessController({ orbitdb: { open, identity, ipfs }, identities })
const m = await DBManifest({ storage: manifestStorage, name: address, type, accessController: accessController.address, meta })
const m = await manifests.create({ name: address, type, accessController: accessController.address, meta })
manifest = m.manifest
address = OrbitDBAddress(m.hash)
name = manifest.name
@ -89,11 +79,14 @@ const OrbitDB = async ({ ipfs, id, identity, keystore, directory } = {}) => {
if (!Database) {
throw new Error(`Unsupported database type: '${type}'`)
}
const db = await Database({ ipfs, identity, address: address.toString(), name, access: accessController, directory, meta, syncAutomatically: sync != null ? sync : true, headsStorage, entryStorage, indexStorage, referencesCount })
db.events.on('close', onDatabaseClosed(address.toString()))
address = address.toString()
databases[address.toString()] = db
const db = await Database({ ipfs, identity, address, name, access: accessController, directory, meta, syncAutomatically: sync, headsStorage, entryStorage, indexStorage, referencesCount })
db.events.on('close', onDatabaseClosed(address))
databases[address] = db
return db
}
@ -106,8 +99,8 @@ const OrbitDB = async ({ ipfs, id, identity, keystore, directory } = {}) => {
if (keystore) {
await keystore.close()
}
if (manifestStorage) {
await manifestStorage.close()
if (manifests) {
await manifests.close()
}
databases = {}
}

View File

@ -1,73 +1,76 @@
import { strictEqual, deepStrictEqual } from 'assert'
import rmrf from 'rimraf'
import * as IPFS from 'ipfs-core'
import Manifest from '../src/manifest.js'
import IPFSBlockStorage from '../src/storage/ipfs-block.js'
import Manifests from '../src/manifest.js'
import config from './config.js'
describe('Manifest', () => {
const repo = './ipfs'
let ipfs
let storage
let manifests
before(async () => {
ipfs = await IPFS.create({ ...config.daemon1, repo })
storage = await IPFSBlockStorage({ ipfs })
manifests = await Manifests({ ipfs })
})
after(async () => {
await storage.close()
await manifests.close()
await ipfs.stop()
await rmrf(repo)
})
it('creates a manifest', async () => {
const name = 'manifest'
const type = 'manifest'
const name = 'database'
const type = 'keyvalue'
const accessController = 'test/default-access-controller'
const expectedHash = 'zdpuAx3LaygjPHa2zsUmRoR4jQPm2WYrExsvz2gncfm62aRKv'
const expectedHash = 'zdpuAn26ookFToGNmpVHgEM71YMULiyS8mAs9UQtV1g6eEyRP'
const expectedManifest = {
name,
type,
accessController
}
const { hash, manifest } = await Manifest({ storage, name, type, accessController })
const { hash, manifest } = await manifests.create({ name, type, accessController })
strictEqual(hash, expectedHash)
deepStrictEqual(manifest, expectedManifest)
})
it('creates a manifest with metadata', async () => {
const name = 'manifest'
const type = 'manifest'
it('loads a manifest', async () => {
const name = 'database'
const type = 'keyvalue'
const accessController = 'test/default-access-controller'
const expectedHash = 'zdpuAmegd2PpDfTQRVhGiATCkWQDvp3JygT9WksWgJkG2u313'
const expectedHash = 'zdpuAn26ookFToGNmpVHgEM71YMULiyS8mAs9UQtV1g6eEyRP'
const expectedManifest = {
name,
type,
accessController
}
const manifest = await manifests.get(expectedHash)
deepStrictEqual(manifest, expectedManifest)
})
it('creates a manifest with metadata', async () => {
const name = 'database'
const type = 'keyvalue'
const accessController = 'test/default-access-controller'
const expectedHash = 'zdpuAyWPs4yAXS6W7CY4UM68pV2NCpzAJr98aMA4zS5XRq5ga'
const meta = { name, description: 'more information about the database' }
const { hash, manifest } = await Manifest({ storage, name, type, accessController, meta })
const { hash, manifest } = await manifests.create({ name, type, accessController, meta })
strictEqual(hash, expectedHash)
deepStrictEqual(manifest.meta, meta)
})
it('throws an error if storage is not specified', async () => {
let err
try {
await Manifest({})
} catch (e) {
err = e.toString()
}
strictEqual(err, 'Error: storage is required')
})
it('throws an error if name is not specified', async () => {
let err
try {
await Manifest({ storage })
await manifests.create({})
} catch (e) {
err = e.toString()
}
@ -79,7 +82,7 @@ describe('Manifest', () => {
let err
try {
await Manifest({ storage, name: 'manifest' })
await manifests.create({ name: 'database' })
} catch (e) {
err = e.toString()
}
@ -91,7 +94,7 @@ describe('Manifest', () => {
let err
try {
await Manifest({ storage, name: 'manifest', type: 'manifest' })
await manifests.create({ name: 'database', type: 'keyvalue' })
} catch (e) {
err = e.toString()
}