Remove getItem as we should use getItems with an array (#4862)

* Remove getItem as we should use getItems with an array

* remove getItem tests

* Update to latest and test

* Test memory downloader and memory db
This commit is contained in:
Adam Hathcock
2025-06-03 08:07:56 +01:00
committed by GitHub
parent 4191071082
commit 51dab0f4ee
11 changed files with 160 additions and 88 deletions
@@ -1,31 +1,29 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`database cache > write single item to queue use getItem 1`] = `
{
"base": {
"id": "id",
"speckle_type": "type",
exports[`IndexedDatabase > should add and get an item 1`] = `
[
{
"baseId": "id1",
"item": {
"foo": "bar",
},
},
"baseId": "id",
}
]
`;
exports[`database cache > write two items to queue use getItem 1`] = `
{
"base": {
"id": "id",
"speckle_type": "type",
exports[`IndexedDatabase > should add and get multiple items 1`] = `
[
{
"baseId": "id1",
"item": {
"foo": "bar",
},
},
"baseId": "id1",
}
`;
exports[`database cache > write two items to queue use getItem 2`] = `
{
"base": {
"id": "id",
"speckle_type": "type",
{
"baseId": "id2",
"item": {
"foo": "bar",
},
},
"baseId": "id2",
}
]
`;
@@ -1,37 +1,33 @@
import { describe, expect, test } from 'vitest'
import IndexedDatabase from './indexedDatabase.js'
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { IDBFactory, IDBKeyRange } from 'fake-indexeddb'
import IndexedDatabase, { IndexedDatabaseOptions } from './indexedDatabase.js'
import { Item } from '../../types/types.js'
describe('database cache', () => {
test('write single item to queue use getItem', async () => {
const i: Item = { baseId: 'id', base: { id: 'id', speckle_type: 'type' } }
const database = new IndexedDatabase({
indexedDB: new IDBFactory(),
keyRange: IDBKeyRange
})
await database.add(i)
await database.disposeAsync()
// Mock Item
const defaultItem = (id: string): Item => ({ baseId: id, item: { foo: 'bar' } })
const x = await database.getItem({ id: 'id' })
expect(x).toMatchSnapshot()
describe('IndexedDatabase', () => {
let db: IndexedDatabase
let options: IndexedDatabaseOptions
beforeEach(() => {
options = { indexedDB: new IDBFactory(), keyRange: IDBKeyRange }
db = new IndexedDatabase(options)
})
test('write two items to queue use getItem', async () => {
const i1: Item = { baseId: 'id1', base: { id: 'id', speckle_type: 'type' } }
const i2: Item = { baseId: 'id2', base: { id: 'id', speckle_type: 'type' } }
const database = new IndexedDatabase({
indexedDB: new IDBFactory(),
keyRange: IDBKeyRange
})
await database.add(i1)
await database.add(i2)
await database.disposeAsync()
afterEach(async () => {
await db.disposeAsync()
})
const x1 = await database.getItem({ id: i1.baseId })
expect(x1).toMatchSnapshot()
it('should add and get multiple items', async () => {
const items = [defaultItem('id1'), defaultItem('id2')]
await db.cacheSaveBatch({ batch: items })
const result = await db.getAll(['id1', 'id2'])
expect(result).toMatchSnapshot()
expect(result).toEqual(items)
})
const x2 = await database.getItem({ id: i2.baseId })
expect(x2).toMatchSnapshot()
it('should dispose without error', async () => {
await expect(db.disposeAsync()).resolves.not.toThrow()
})
})
@@ -5,7 +5,7 @@ import { isSafari } from '@speckle/shared'
import { Dexie, DexieOptions, Table } from 'dexie'
import { Database } from '../interfaces.js'
class ObjectStore extends Dexie {
export class ObjectStore extends Dexie {
static #databaseName: string = 'speckle-cache'
objects!: Table<Item, string> // Table type: <entity, primaryKey>
@@ -83,30 +83,6 @@ export default class IndexedDatabase implements Database {
this.#cacheDB = await this.#openDatabase()
}
//this is for testing only - in the real world we will not use this
async add(item: Item): Promise<void> {
await this.#setupCacheDb()
await this.#cacheDB!.transaction('rw', this.#cacheDB!.objects, async () => {
return await this.#cacheDB?.objects.add(item)
})
}
async getItem(params: { id: string }): Promise<Item | undefined> {
const { id } = params
await this.#setupCacheDb()
//might not be in the real DB yet, so check the write queue first
if (this.#writeQueue) {
const item = this.#writeQueue.get(id)
if (item) {
return item
}
}
return this.#cacheDB!.transaction('r', this.#cacheDB!.objects, async () => {
return await this.#cacheDB?.objects.get(id)
})
}
async cacheSaveBatch(params: { batch: Item[] }): Promise<void> {
await this.#setupCacheDb()
const { batch } = params
@@ -0,0 +1,48 @@
import { describe, it, expect, beforeEach } from 'vitest'
import { MemoryDatabase } from './memoryDatabase.js'
import { Base, Item } from '../../types/types.js'
const makeItem = (id: string, foo = 'bar'): Item => ({
baseId: id,
base: { foo } as unknown as Base
})
describe('MemoryDatabase', () => {
let db: MemoryDatabase
beforeEach(() => {
db = new MemoryDatabase()
})
it('should return undefined for missing keys', async () => {
const result = await db.getAll(['missing'])
expect(result).toEqual([undefined])
})
it('should add and retrieve a single item', async () => {
const item = makeItem('id1')
await db.cacheSaveBatch({ batch: [item] })
const result = await db.getAll(['id1'])
expect(result).toEqual([item])
})
it('should add and retrieve multiple items', async () => {
const items = [makeItem('id1'), makeItem('id2', 'baz')]
await db.cacheSaveBatch({ batch: items })
const result = await db.getAll(['id1', 'id2'])
expect(result).toEqual(items)
})
it('should overwrite items with the same key', async () => {
const item1 = makeItem('id1', 'foo')
const item2 = makeItem('id1', 'bar')
await db.cacheSaveBatch({ batch: [item1] })
await db.cacheSaveBatch({ batch: [item2] })
const result = await db.getAll(['id1'])
expect(result).toEqual([item2])
})
it('disposeAsync should resolve', async () => {
await expect(db.disposeAsync()).resolves.not.toThrow()
})
})
@@ -29,14 +29,6 @@ export class MemoryDatabase implements Database {
return Promise.resolve()
}
getItem(params: { id: string }): Promise<Item | undefined> {
const item = this.items.get(params.id)
if (item) {
return Promise.resolve({ baseId: params.id, base: item })
}
return Promise.resolve(undefined)
}
disposeAsync(): Promise<void> {
return Promise.resolve()
}
@@ -0,0 +1,12 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`MemoryDownloader > should add found item to results queue 1`] = `
Map {
"id1" => {
"foo": "foo",
},
"id2" => {
"foo": "bar",
},
}
`;
@@ -0,0 +1,49 @@
import { describe, it, expect, beforeEach } from 'vitest'
import { MemoryDownloader } from './memoryDownloader.js'
import { Base, Item } from '../../types/types.js'
import Queue from '../../helpers/queue.js'
import BufferQueue from '../../helpers/bufferQueue.js'
const makeBase = (foo: string): Base => ({ foo } as unknown as Base)
describe('MemoryDownloader', () => {
let items: Map<string, Base>
let downloader: MemoryDownloader
let results: Queue<Item>
beforeEach(() => {
items = new Map([
['id1', makeBase('foo')],
['id2', makeBase('bar')]
])
downloader = new MemoryDownloader('id1', items)
results = new BufferQueue<Item>()
})
it('should download the root item', async () => {
const item = await downloader.downloadSingle()
expect(item).toEqual({ baseId: 'id1', base: { foo: 'foo' } })
})
it('should throw if root item is missing', async () => {
const missingDownloader = new MemoryDownloader('missing', items)
await expect(missingDownloader.downloadSingle()).rejects.toThrow(
'Method not implemented.'
)
})
it('should add found item to results queue', () => {
downloader.initializePool({ results, total: 2 })
downloader.add('id2')
expect(items).toMatchSnapshot()
})
it('should throw if added item is missing', () => {
downloader.initializePool({ results, total: 2 })
expect(() => downloader.add('missing')).toThrow()
})
it('disposeAsync should resolve', async () => {
await expect(downloader.disposeAsync()).resolves.not.toThrow()
})
})
@@ -23,7 +23,7 @@ export class MemoryDownloader implements Downloader {
if (root) {
return Promise.resolve({ baseId: this.#rootId, base: root })
}
throw new Error('Method not implemented.')
return Promise.reject(new Error('Method not implemented.'))
}
disposeAsync(): Promise<void> {
return Promise.resolve()
@@ -13,7 +13,6 @@ export interface Downloader extends Queue<string> {
export interface Database {
getAll(keys: string[]): Promise<(Item | undefined)[]>
getItem(params: { id: string }): Promise<Item | undefined>
cacheSaveBatch(params: { batch: Item[] }): Promise<void>
disposeAsync(): Promise<void>
}
@@ -65,7 +65,7 @@ export class ObjectLoader2 {
async getRootObject(): Promise<Item | undefined> {
if (!this.#root) {
this.#root = await this.#database.getItem({ id: this.#rootId })
this.#root = (await this.#database.getAll([this.#rootId]))[0]
if (!this.#root) {
this.#root = await this.#downloader.downloadSingle()
}
+4 -2
View File
@@ -14,8 +14,10 @@ describe('e2e', () => {
serverUrl: 'https://app.speckle.systems',
streamId: 'da9e320dad',
objectId: '31d10c0cea569a1e26809658ed27e281',
indexedDB: new IDBFactory(),
keyRange: IDBKeyRange
options: {
indexedDB: new IDBFactory(),
keyRange: IDBKeyRange
}
})
const getObjectPromise = loader.getObject({