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: dbMigrationPathOpt - add db migration path to MapeoProject and MapeoManager #384

Merged
merged 6 commits into from
Nov 22, 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
19 changes: 15 additions & 4 deletions src/mapeo-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ export class MapeoManager extends TypedEmitter {
/** @type {import('./types.js').CoreStorage} */
#coreStorage
#dbFolder
/** @type {string} */
#projectMigrationsFolder
#deviceId
#localPeers
#invite
Expand All @@ -78,24 +80,32 @@ export class MapeoManager extends TypedEmitter {
* @param {Object} opts
* @param {Buffer} opts.rootKey 16-bytes of random data that uniquely identify the device, used to derive a 32-byte master key, which is used to derive all the keypairs used for Mapeo
* @param {string} opts.dbFolder Folder for sqlite Dbs. Folder must exist. Use ':memory:' to store everything in-memory
* @param {string} opts.projectMigrationsFolder path for drizzle migrations folder for project database
* @param {string} opts.clientMigrationsFolder path for drizzle migrations folder for client database
* @param {string | import('./types.js').CoreStorage} opts.coreStorage Folder for hypercore storage or a function that returns a RandomAccessStorage instance
* @param {{ port?: number, logger: import('fastify').FastifyServerOptions['logger'] }} [opts.mediaServerOpts]
*/
constructor({ rootKey, dbFolder, coreStorage, mediaServerOpts }) {
constructor({
rootKey,
dbFolder,
projectMigrationsFolder,
clientMigrationsFolder,
coreStorage,
mediaServerOpts,
}) {
super()
this.#keyManager = new KeyManager(rootKey)
this.#deviceId = getDeviceId(this.#keyManager)
this.#l = new Logger({ deviceId: this.#deviceId, ns: 'manager' })
this.#dbFolder = dbFolder
this.#projectMigrationsFolder = projectMigrationsFolder
const sqlite = new Database(
dbFolder === ':memory:'
? ':memory:'
: path.join(dbFolder, CLIENT_SQLITE_FILE_NAME)
)
this.#db = drizzle(sqlite)
migrate(this.#db, {
migrationsFolder: new URL('../drizzle/client', import.meta.url).pathname,
})
migrate(this.#db, { migrationsFolder: clientMigrationsFolder })

this.#localPeers = new LocalPeers({ logger: this.#l })
this.#localPeers.on('peers', (peers) => {
Expand Down Expand Up @@ -352,6 +362,7 @@ export class MapeoManager extends TypedEmitter {
return new MapeoProject({
...this.#projectStorage(projectId),
...projectKeys,
projectMigrationsFolder: this.#projectMigrationsFolder,
keyManager: this.#keyManager,
sharedDb: this.#db,
sharedIndexWriter: this.#projectSettingsIndexWriter,
Expand Down
6 changes: 3 additions & 3 deletions src/mapeo-project.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export class MapeoProject {
/**
* @param {Object} opts
* @param {string} opts.dbPath Path to store project sqlite db. Use `:memory:` for memory storage
* @param {string} opts.projectMigrationsFolder path for drizzle migration folder for project
* @param {import('@mapeo/crypto').KeyManager} opts.keyManager mapeo/crypto KeyManager instance
* @param {Buffer} opts.projectKey 32-byte public key of the project creator core
* @param {Buffer} [opts.projectSecretKey] 32-byte secret key of the project creator core
Expand All @@ -83,6 +84,7 @@ export class MapeoProject {
*/
constructor({
dbPath,
projectMigrationsFolder,
sharedDb,
sharedIndexWriter,
coreStorage,
Expand All @@ -101,9 +103,7 @@ export class MapeoProject {
///////// 1. Setup database
const sqlite = new Database(dbPath)
const db = drizzle(sqlite)
migrate(db, {
migrationsFolder: new URL('../drizzle/project', import.meta.url).pathname,
})
migrate(db, { migrationsFolder: projectMigrationsFolder })

///////// 2. Setup random-access-storage functions

Expand Down
13 changes: 13 additions & 0 deletions test-e2e/capabilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,17 @@ import {
} from '../src/capabilities.js'
import { randomBytes } from 'crypto'

const projectMigrationsFolder = new URL('../drizzle/project', import.meta.url)
.pathname
const clientMigrationsFolder = new URL('../drizzle/client', import.meta.url)
.pathname

test('Creator capabilities and role assignment', async (t) => {
const rootKey = KeyManager.generateRootKey()
const manager = new MapeoManager({
rootKey,
projectMigrationsFolder,
clientMigrationsFolder,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand Down Expand Up @@ -44,6 +51,8 @@ test('Creator capabilities and role assignment', async (t) => {
test('New device without capabilities', async (t) => {
const rootKey = KeyManager.generateRootKey()
const manager = new MapeoManager({
projectMigrationsFolder,
clientMigrationsFolder,
rootKey,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
Expand Down Expand Up @@ -81,6 +90,8 @@ test('getMany() - on invitor device', async (t) => {
const creatorDeviceId = km.getIdentityKeypair().publicKey.toString('hex')
const manager = new MapeoManager({
rootKey,
projectMigrationsFolder,
clientMigrationsFolder,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand Down Expand Up @@ -118,6 +129,8 @@ test('getMany() - on newly invited device before sync', async (t) => {
const km = new KeyManager(rootKey)
const deviceId = km.getIdentityKeypair().publicKey.toString('hex')
const manager = new MapeoManager({
projectMigrationsFolder,
clientMigrationsFolder,
rootKey,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
Expand Down
4 changes: 4 additions & 0 deletions test-e2e/core-ownership.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ test('CoreOwnership', async (t) => {
const km = new KeyManager(rootKey)
const manager = new MapeoManager({
rootKey,
projectMigrationsFolder: new URL('../drizzle/project', import.meta.url)
.pathname,
clientMigrationsFolder: new URL('../drizzle/client', import.meta.url)
.pathname,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand Down
13 changes: 13 additions & 0 deletions test-e2e/device-info.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,17 @@ import RAM from 'random-access-memory'

import { MapeoManager } from '../src/mapeo-manager.js'

const projectMigrationsFolder = new URL('../drizzle/project', import.meta.url)
.pathname
const clientMigrationsFolder = new URL('../drizzle/client', import.meta.url)
.pathname

test('write and read deviceInfo', async (t) => {
const rootKey = KeyManager.generateRootKey()
const manager = new MapeoManager({
rootKey,
projectMigrationsFolder,
clientMigrationsFolder,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand All @@ -27,6 +34,8 @@ test('device info written to projects', (t) => {
t.test('when creating project', async (st) => {
const manager = new MapeoManager({
rootKey: KeyManager.generateRootKey(),
projectMigrationsFolder,
clientMigrationsFolder,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand All @@ -47,6 +56,8 @@ test('device info written to projects', (t) => {
t.test('when adding project', async (st) => {
const manager = new MapeoManager({
rootKey: KeyManager.generateRootKey(),
projectMigrationsFolder,
clientMigrationsFolder,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand All @@ -70,6 +81,8 @@ test('device info written to projects', (t) => {
t.test('after updating global device info', async (st) => {
const manager = new MapeoManager({
rootKey: KeyManager.generateRootKey(),
projectMigrationsFolder,
clientMigrationsFolder,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand Down
17 changes: 17 additions & 0 deletions test-e2e/manager-basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@ import { KeyManager } from '@mapeo/crypto'
import RAM from 'random-access-memory'
import { MapeoManager } from '../src/mapeo-manager.js'

const projectMigrationsFolder = new URL('../drizzle/project', import.meta.url)
.pathname
const clientMigrationsFolder = new URL('../drizzle/client', import.meta.url)
.pathname

test('Managing created projects', async (t) => {
const manager = new MapeoManager({
rootKey: KeyManager.generateRootKey(),
projectMigrationsFolder,
clientMigrationsFolder,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand Down Expand Up @@ -109,6 +116,8 @@ test('Managing created projects', async (t) => {
test('Managing added projects', async (t) => {
const manager = new MapeoManager({
rootKey: KeyManager.generateRootKey(),
projectMigrationsFolder,
clientMigrationsFolder,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand Down Expand Up @@ -175,6 +184,8 @@ test('Managing added projects', async (t) => {
test('Managing both created and added projects', async (t) => {
const manager = new MapeoManager({
rootKey: KeyManager.generateRootKey(),
projectMigrationsFolder,
clientMigrationsFolder,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand Down Expand Up @@ -213,6 +224,8 @@ test('Managing both created and added projects', async (t) => {
test('Manager cannot add project that already exists', async (t) => {
const manager = new MapeoManager({
rootKey: KeyManager.generateRootKey(),
projectMigrationsFolder,
clientMigrationsFolder,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand Down Expand Up @@ -240,6 +253,8 @@ test('Consistent storage folders', async (t) => {
const storageNames = []
const manager = new MapeoManager({
rootKey: randomBytesSeed('root_key').subarray(0, 16),
projectMigrationsFolder,
clientMigrationsFolder,
dbFolder: ':memory:',
coreStorage: (name) => {
storageNames.push(name)
Expand All @@ -263,6 +278,8 @@ test('Consistent storage folders', async (t) => {
test('manager.start() and manager.stop()', async (t) => {
const manager = new MapeoManager({
rootKey: KeyManager.generateRootKey(),
projectMigrationsFolder,
clientMigrationsFolder,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand Down
13 changes: 13 additions & 0 deletions test-e2e/manager-invite.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@ import { InviteResponse_Decision } from '../src/generated/rpc.js'
import { MapeoManager, kRPC } from '../src/mapeo-manager.js'
import { replicate } from '../tests/helpers/local-peers.js'

const projectMigrationsFolder = new URL('../drizzle/project', import.meta.url)
.pathname
const clientMigrationsFolder = new URL('../drizzle/client', import.meta.url)
.pathname

test('member invite accepted', async (t) => {
t.plan(10)

const deferred = pDefer()

const creator = new MapeoManager({
rootKey: KeyManager.generateRootKey(),
projectMigrationsFolder,
clientMigrationsFolder,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand All @@ -39,6 +46,8 @@ test('member invite accepted', async (t) => {

const joiner = new MapeoManager({
rootKey: KeyManager.generateRootKey(),
projectMigrationsFolder,
clientMigrationsFolder,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand Down Expand Up @@ -107,6 +116,8 @@ test('member invite rejected', async (t) => {

const creator = new MapeoManager({
rootKey: KeyManager.generateRootKey(),
projectMigrationsFolder,
clientMigrationsFolder,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand All @@ -133,6 +144,8 @@ test('member invite rejected', async (t) => {

const joiner = new MapeoManager({
rootKey: KeyManager.generateRootKey(),
projectMigrationsFolder,
clientMigrationsFolder,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand Down
9 changes: 9 additions & 0 deletions test-e2e/media-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,19 @@ const BLOB_FIXTURES_DIR = fileURLToPath(
new URL('../tests/fixtures/blob-api/', import.meta.url)
)

const projectMigrationsFolder = new URL('../drizzle/project', import.meta.url)
.pathname
const clientMigrationsFolder = new URL('../drizzle/client', import.meta.url)
.pathname

test('retrieving blobs using url', async (t) => {
const clock = FakeTimers.install({ shouldAdvanceTime: true })
t.teardown(() => clock.uninstall())

const manager = new MapeoManager({
rootKey: KeyManager.generateRootKey(),
projectMigrationsFolder,
clientMigrationsFolder,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand Down Expand Up @@ -111,6 +118,8 @@ test('retrieving icons using url', async (t) => {

const manager = new MapeoManager({
rootKey: KeyManager.generateRootKey(),
projectMigrationsFolder,
clientMigrationsFolder,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand Down
9 changes: 9 additions & 0 deletions test-e2e/members.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ import {
} from '../src/capabilities.js'
import { replicate } from '../tests/helpers/local-peers.js'

const projectMigrationsFolder = new URL('../drizzle/project', import.meta.url)
.pathname
const clientMigrationsFolder = new URL('../drizzle/client', import.meta.url)
.pathname

test('getting yourself after creating project', async (t) => {
const { manager } = setup()

Expand Down Expand Up @@ -159,6 +164,8 @@ test('getting invited member after invite accepted', async (t) => {
function setup() {
const manager = new MapeoManager({
rootKey: KeyManager.generateRootKey(),
projectMigrationsFolder,
clientMigrationsFolder,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand All @@ -180,6 +187,8 @@ function setup() {

const otherManager = new MapeoManager({
rootKey: KeyManager.generateRootKey(),
projectMigrationsFolder,
clientMigrationsFolder,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand Down
4 changes: 4 additions & 0 deletions test-e2e/project-crud.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ function getUpdateFixture(value) {
test('CRUD operations', async (t) => {
const manager = new MapeoManager({
rootKey: KeyManager.generateRootKey(),
projectMigrationsFolder: new URL('../drizzle/project', import.meta.url)
.pathname,
clientMigrationsFolder: new URL('../drizzle/client', import.meta.url)
.pathname,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand Down
4 changes: 4 additions & 0 deletions test-e2e/project-settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import RAM from 'random-access-memory'
test('Project settings create, read, and update operations', async (t) => {
const manager = new MapeoManager({
rootKey: KeyManager.generateRootKey(),
projectMigrationsFolder: new URL('../drizzle/project', import.meta.url)
.pathname,
clientMigrationsFolder: new URL('../drizzle/client', import.meta.url)
.pathname,
dbFolder: ':memory:',
coreStorage: () => new RAM(),
})
Expand Down
2 changes: 2 additions & 0 deletions test-types/data-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ const sqlite = new Database(':memory:')

const mapeoProject = new MapeoProject({
dbPath: ':memory:',
projectMigrationsFolder: new URL('../drizzle/project', import.meta.url)
.pathname,
coreStorage: () => new RAM(),
keyManager: new KeyManager(randomBytes(32)),
projectKey: randomBytes(32),
Expand Down
Loading