Skip to content

Commit

Permalink
feat!: restoreSnapshot
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 committed Mar 13, 2021
1 parent 3eccf84 commit 6e75a61
Show file tree
Hide file tree
Showing 5 changed files with 24 additions and 22 deletions.
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
- [`storage.hasItem(key)`](#storagehasitemkey)
- [`storage.getItem(key)`](#storagegetitemkey)
- [`storage.setItem(key, value)`](#storagesetitemkey-value)
- [`storage.setItems(base, items)`](#storagesetitemsbase-items)
- [`storage.removeItem(key)`](#storageremoveitemkey)
- [`storage.getKeys(base?)`](#storagegetkeysbase)
- [`storage.clear(base?)`](#storageclearbase)
Expand All @@ -40,6 +39,7 @@
- [`storage.watch(callback)`](#storagewatchcallback)
- [Utils](#utils)
- [`snapshot(storage, base?)`](#snapshotstorage-base)
- [`restoreSnapshot(storage, data, base?)`](#restoresnapshotstorage-data-base)
- [Storage Server](#storage-server)
- [Drivers](#drivers)
- [`fs` (node)](#fs-node)
Expand Down Expand Up @@ -103,14 +103,6 @@ If value is `undefined`, it is same as calling `removeItem(key)`.
await storage.setItem('foo:bar', 'baz')
```

### `storage.setItems(base, items)`

Batch update items. Internally calls one-by-one to the driver. Useful to restore/hydrate state.

```js
await storage.setItems('/', { 'foo:bar': 'baz' })
```

### `storage.removeItem(key)`

Remove a value from storage.
Expand Down Expand Up @@ -201,6 +193,14 @@ import { snapshot } from 'unstorage'
const data = await snapshot(storage, '/etc')
```

### `restoreSnapshot(storage, data, base?)`

Restore snapshot created by `snapshot()`.

```js
await restoreSnapshot(storage, { 'foo:bar': 'baz' }, '/etc2')
```

## Storage Server

We can easily expose unstorage instance to a http server to allow remote connections.
Expand Down
15 changes: 9 additions & 6 deletions src/storage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import destr from 'destr'
import type { Storage, Driver, WatchCallback } from './types'
import type { Storage, Driver, WatchCallback, StorageValue } from './types'
import memory from './drivers/memory'
import { normalizeKey, normalizeBase, asyncCall, stringify } from './utils'

Expand Down Expand Up @@ -80,10 +80,6 @@ export function createStorage (): Storage {
onChange('update', key)
}
},
async setItems (base, items) {
base = normalizeBase(base)
await Promise.all(Object.entries(items).map(e => storage.setItem(base + e[0], e[1])))
},
async removeItem (key) {
key = normalizeKey(key)
const { relativeKey, driver } = getMount(key)
Expand Down Expand Up @@ -143,7 +139,9 @@ export function createStorage (): Storage {
return storage
}

export async function snapshot (storage: Storage, base: string) {
export type Snapshot<T=string> = Record<string, T>

export async function snapshot (storage: Storage, base: string): Promise<Snapshot<string>> {
base = normalizeBase(base)
const keys = await storage.getKeys(base)
const snapshot: any = {}
Expand All @@ -153,6 +151,11 @@ export async function snapshot (storage: Storage, base: string) {
return snapshot
}

export async function restoreSnapshot (storage: Storage, snapshot: Snapshot<StorageValue>, base: string = '') {
base = normalizeBase(base)
await Promise.all(Object.entries(snapshot).map(e => storage.setItem(base + e[0], e[1])))
}

function watch (storage: Driver, onChange: WatchCallback, base: string) {
if (storage.watch) {
return storage.watch((event, key) => onChange(event, base + key))
Expand Down
1 change: 0 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export interface Storage {
hasItem: (key: string) => Promise<boolean>
getItem: (key: string) => Promise<StorageValue>
setItem: (key: string, value: StorageValue) => Promise<void>
setItems: (base: string, items: Record<string, StorageValue>) => Promise<void>
removeItem: (key: string) => Promise<void>
getKeys: (base?: string) => Promise<string[]>
clear: (base?: string) => Promise<void>
Expand Down
4 changes: 2 additions & 2 deletions test/drivers/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Storage, Driver, createStorage } from '../../src'
import { Storage, Driver, createStorage, restoreSnapshot } from '../../src'

export interface TestContext {
storage: Storage
Expand All @@ -18,7 +18,7 @@ export function testDriver (opts: TestOptions) {

it('init', async () => {
await ctx.storage.mount('/', ctx.driver)
await ctx.storage.setItems('', { initial: 'works' })
await restoreSnapshot(ctx.storage, { initial: 'works' })
expect(await ctx.storage.getItem('initial')).toBe('works')
await ctx.storage.clear()
})
Expand Down
8 changes: 4 additions & 4 deletions test/storage.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createStorage, snapshot } from '../src'
import { createStorage, snapshot, restoreSnapshot } from '../src'
import memory from '../src/drivers/memory'

const data = {
Expand All @@ -9,21 +9,21 @@ const data = {
describe('storage', () => {
it('mount/unmount', async () => {
const storage = createStorage().mount('/mnt', memory())
await storage.setItems('/mnt', data)
await restoreSnapshot(storage, data, 'mnt')
expect(await snapshot(storage, '/mnt')).toMatchObject(data)
})

it('snapshot', async () => {
const storage = createStorage()
await storage.setItems('', data)
await restoreSnapshot(storage, data)
expect(await snapshot(storage, '')).toMatchObject(data)
})

it('watch', async () => {
const onChange = jest.fn()
const storage = createStorage().mount('/mnt', memory())
await storage.watch(onChange)
await storage.setItems('/mnt', data)
await restoreSnapshot(storage, data, 'mnt')
expect(onChange).toHaveBeenCalledWith('update', 'mnt:data:foo')
})
})

0 comments on commit 6e75a61

Please sign in to comment.