Skip to content

Commit

Permalink
fix!: delete() by docId not versionId(s)
Browse files Browse the repository at this point in the history
Fixes "`dataType.delete()` should use `docId` not `versionId`" (#422)

The deletion document links to all forks, if forks exist.
  • Loading branch information
gmaclennan committed Dec 19, 2023
1 parent aee15c2 commit 1e4c4b2
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 14 deletions.
2 changes: 1 addition & 1 deletion src/datatype/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,5 +95,5 @@ export class DataType<
>
>(versionId: string | string[], value: T): Promise<TDoc & { forks: string[] }>

delete(versionId: string | string[]): Promise<TDoc & { forks: string[] }>
delete(docId: string): Promise<TDoc & { forks: string[] }>
}
22 changes: 10 additions & 12 deletions src/datatype/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { validate } from '@mapeo/schema'
import { getTableConfig } from 'drizzle-orm/sqlite-core'
import { eq, inArray, placeholder } from 'drizzle-orm'
import { randomBytes } from 'node:crypto'
import { deNullify, valueOf } from '../utils.js'
import { deNullify } from '../utils.js'
import crypto from 'hypercore-crypto'
import { TypedEmitter } from 'tiny-typed-emitter'

Expand Down Expand Up @@ -209,24 +209,22 @@ export class DataType extends TypedEmitter {

/**
* Not yet implemented
* @param {string | string[]} versionId
* @param {string} docId
*/
async delete(versionId) {
async delete(docId) {
await this.#dataStore.indexer.idle()
const links = Array.isArray(versionId) ? versionId : [versionId]
const { docId, createdAt, createdBy } = await this.#validateLinks(links)
const existingDoc = await this.getByDocId(docId)

const existingDoc = await this.getByVersionId(links[links.length - 1])
if ('deleted' in existingDoc && existingDoc.deleted) {
throw new Error('Doc already deleted')
}

/** @type {any} */
const doc = {
...valueOf(existingDoc),
docId,
createdAt,
...existingDoc,
updatedAt: new Date().toISOString(),
createdBy,
links,
schemaName: this.#schemaName,
// @ts-expect-error - TS just doesn't work in this class
links: [existingDoc.versionId, ...existingDoc.forks],
deleted: true,
}
await this.#dataStore.write(doc)
Expand Down
61 changes: 61 additions & 0 deletions test-e2e/project-crud.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,5 +207,66 @@ test('CRUD operations', async (t) => {
'expected values returned before closing and after re-opening'
)
})

await t.test(`create and delete ${schemaName}`, async (st) => {
const projectId = await manager.createProject()
const project = await manager.getProject(projectId)
const writePromises = []
let i = 0
while (i++ < CREATE_COUNT) {
const value = valueOf(generate(schemaName)[0])
writePromises.push(
// @ts-ignore
project[schemaName].create(value)
)
}
const written = await Promise.all(writePromises)
const deleted = await Promise.all(
written.map((doc) => project[schemaName].delete(doc.docId))
)
const read = await Promise.all(
written.map((doc) => project[schemaName].getByDocId(doc.docId))
)
st.ok(
deleted.every((doc) => doc.deleted),
'all docs are deleted'
)
st.alike(
sortById(deleted),
sortById(read),
'return create() matches return of getByDocId()'
)
})

await t.test('delete forks', async (st) => {
const projectId = await manager.createProject()
const project = await manager.getProject(projectId)
// @ts-ignore
const written = await project[schemaName].create(value)
const updateValue = getUpdateFixture(value)
// @ts-ignore
const updatedFork1 = await project[schemaName].update(
written.versionId,
updateValue
)
// @ts-ignore
const updatedFork2 = await project[schemaName].update(
written.versionId,
updateValue
)
const updatedReRead = await project[schemaName].getByDocId(written.docId)
st.alike(
updatedFork2,
updatedReRead,
'return of update() matched return of getByDocId()'
)
st.alike(updatedReRead.forks, [updatedFork1.versionId], 'doc is forked')
const deleted = await project[schemaName].delete(written.docId)
st.ok(deleted.deleted, 'doc is deleted')
st.is(deleted.forks.length, 0, 'forks are deleted')
const deletedReRead = await project[schemaName].getByDocId(written.docId)
st.ok(deletedReRead.deleted, 'doc is deleted')
st.is(deletedReRead.forks.length, 0, 'forks are deleted')
})
}
})
3 changes: 3 additions & 0 deletions test-types/data-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ mapeoProject.observation.on('updated-docs', (docs) => {
Expect<Equal<Observation[], typeof docs>>
})

const deletedObservation = await mapeoProject.observation.delete('abc')
Expect<Equal<Observation & { forks: string[] }, typeof deletedObservation>>

///// Presets

const createdPreset = await mapeoProject.preset.create({} as PresetValue)
Expand Down
2 changes: 1 addition & 1 deletion tests/data-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ test('delete()', async (t) => {
const { dataType } = await testenv({ projectKey })
const doc = await dataType.create(obsFixture)
t.is(doc.deleted, false, `'deleted' field is false before deletion`)
const deletedDoc = await dataType.delete(doc.versionId)
const deletedDoc = await dataType.delete(doc.docId)
t.is(deletedDoc.deleted, true, `'deleted' field is true after deletion`)
t.alike(
deletedDoc.links,
Expand Down

0 comments on commit 1e4c4b2

Please sign in to comment.