From 67e26134a85e0238c0fe11c8bc25886b4e08102c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Sans=C3=B3n?= Date: Tue, 23 Jul 2024 11:56:05 +0200 Subject: [PATCH] Changed recompute on demand --- .../getDocumentsAtCommit.test.ts | 2 +- .../documentVersions/getDocumentsAtCommit.ts | 17 +- packages/core/src/services/commits/merge.ts | 11 ++ .../src/services/documents/create.test.ts | 28 ++-- .../core/src/services/documents/create.ts | 57 +++---- packages/core/src/services/documents/index.ts | 1 + .../services/documents/recomputeChanges.ts | 64 ++++++++ .../src/services/documents/update.test.ts | 145 +++++++++++------- .../core/src/services/documents/update.ts | 101 ++++++------ .../documents/{shared.ts => utils.ts} | 106 ++++++++----- packages/core/src/services/projects/create.ts | 11 +- packages/core/src/tests/factories/projects.ts | 15 +- 12 files changed, 337 insertions(+), 221 deletions(-) create mode 100644 packages/core/src/services/documents/recomputeChanges.ts rename packages/core/src/services/documents/{shared.ts => utils.ts} (55%) diff --git a/packages/core/src/data-access/documentVersions/getDocumentsAtCommit.test.ts b/packages/core/src/data-access/documentVersions/getDocumentsAtCommit.test.ts index 2e7e3d91a..1e7a03837 100644 --- a/packages/core/src/data-access/documentVersions/getDocumentsAtCommit.test.ts +++ b/packages/core/src/data-access/documentVersions/getDocumentsAtCommit.test.ts @@ -21,7 +21,7 @@ describe('getDocumentsAtCommit', () => { const documents = result.unwrap() expect(documents.length).toBe(1) - expect(documents[0]!.id).toBe(doc.id) + expect(documents[0]!.documentUuid).toBe(doc.documentUuid) }) it('returns the right document version for each commit', async (ctx) => { diff --git a/packages/core/src/data-access/documentVersions/getDocumentsAtCommit.ts b/packages/core/src/data-access/documentVersions/getDocumentsAtCommit.ts index eb9d9ff2b..0f3c68fc1 100644 --- a/packages/core/src/data-access/documentVersions/getDocumentsAtCommit.ts +++ b/packages/core/src/data-access/documentVersions/getDocumentsAtCommit.ts @@ -7,7 +7,7 @@ import { Result, TypedResult, } from '@latitude-data/core' -import { LatitudeError } from '$core/lib/errors' +import { LatitudeError, NotFoundError } from '$core/lib/errors' import { and, eq, getTableColumns, isNotNull, lte, max } from 'drizzle-orm' async function fetchDocumentsFromMergedCommits( @@ -79,14 +79,17 @@ export async function getDocumentsAtCommit( { commitId }: { commitId: number }, tx = database, ): Promise> { - const commitResult = await findCommitById({ id: commitId }) + const commitResult = await findCommitById({ id: commitId }, tx) if (commitResult.error) return commitResult const commit = commitResult.value! - const documentsFromMergedCommits = await fetchDocumentsFromMergedCommits({ - projectId: commit.projectId, - maxMergedAt: commit.mergedAt, - }) + const documentsFromMergedCommits = await fetchDocumentsFromMergedCommits( + { + projectId: commit.projectId, + maxMergedAt: commit.mergedAt, + }, + tx, + ) if (commit.mergedAt !== null) { // Referenced commit is merged. No additional documents to return. @@ -130,7 +133,7 @@ export async function getDocumentAtCommit( (d) => d.documentUuid === documentUuid, ) - if (!document) return Result.error(new LatitudeError('Document not found')) + if (!document) return Result.error(new NotFoundError('Document not found')) return Result.ok(document) } diff --git a/packages/core/src/services/commits/merge.ts b/packages/core/src/services/commits/merge.ts index f0e049659..6af48ecac 100644 --- a/packages/core/src/services/commits/merge.ts +++ b/packages/core/src/services/commits/merge.ts @@ -2,6 +2,7 @@ import { Commit, commits, database, + recomputeChanges, Result, Transaction, } from '@latitude-data/core' @@ -35,6 +36,16 @@ export async function mergeCommit( ) } + const recomputedResults = await recomputeChanges({ commitId }, tx) + if (recomputedResults.error) return recomputedResults + if (Object.keys(recomputedResults.value.errors).length > 0) { + return Result.error( + new LatitudeError( + 'There are errors in the updated documents in this commit', + ), + ) + } + const result = await tx .update(commits) .set({ mergedAt }) diff --git a/packages/core/src/services/documents/create.test.ts b/packages/core/src/services/documents/create.test.ts index 75d6126f5..128cf5eaf 100644 --- a/packages/core/src/services/documents/create.test.ts +++ b/packages/core/src/services/documents/create.test.ts @@ -54,30 +54,20 @@ describe('createNewDocument', () => { }) expect(result.ok).toBe(false) - expect(result.error!.message).toBe( - 'Cannot create a document version in a merged commit', - ) + expect(result.error!.message).toBe('Cannot modify a merged commit') }) - it('modifies other documents if it is referenced by another document', async (ctx) => { - const { project } = await ctx.factories.createProject({ - documents: { - main: '', - }, - }) - + it('fails when trying to create a document in a merged commit', async (ctx) => { + const { project } = await ctx.factories.createProject() const { commit } = await ctx.factories.createDraft({ project }) - await createNewDocument({ + await mergeCommit({ commitId: commit.id }) + + const result = await createNewDocument({ commitId: commit.id, - path: 'referenced/doc', + path: 'foo', }) - const changes = await listCommitChanges({ commitId: commit.id }).then((r) => - r.unwrap(), - ) - expect(changes.length).toBe(2) - const changedDocsPahts = changes.map((c) => c.path) - expect(changedDocsPahts).toContain('main') - expect(changedDocsPahts).toContain('referenced/doc') + expect(result.ok).toBe(false) + expect(result.error!.message).toBe('Cannot modify a merged commit') }) }) diff --git a/packages/core/src/services/documents/create.ts b/packages/core/src/services/documents/create.ts index eba50cf93..709ab0c4e 100644 --- a/packages/core/src/services/documents/create.ts +++ b/packages/core/src/services/documents/create.ts @@ -1,54 +1,39 @@ -import { DocumentVersion, Result } from '@latitude-data/core' +import { findCommitById, getDocumentsAtCommit } from '$core/data-access' +import { Result, Transaction, TypedResult } from '$core/lib' import { BadRequestError } from '$core/lib/errors' - -import { - getDraft, - getMergedAndDraftDocuments, - replaceCommitChanges, - resolveDocumentChanges, -} from './shared' +import { DocumentVersion, documentVersions } from '$core/schema' export async function createNewDocument({ commitId, path, + content, }: { commitId: number path: string -}) { - try { - const draft = (await getDraft(commitId)).unwrap() - - const [mergedDocuments, draftDocuments] = ( - await getMergedAndDraftDocuments({ - draft, - }) - ).unwrap() + content?: string +}): Promise> { + return await Transaction.call(async (tx) => { + const commit = (await findCommitById({ id: commitId }, tx)).unwrap() + if (commit.mergedAt !== null) { + return Result.error(new BadRequestError('Cannot modify a merged commit')) + } - if (path && draftDocuments.find((d) => d.path === path)) { + const currentDocs = (await getDocumentsAtCommit({ commitId }, tx)).unwrap() + if (currentDocs.find((d) => d.path === path)) { return Result.error( new BadRequestError('A document with the same path already exists'), ) } - draftDocuments.push({ - path, - content: '', - } as DocumentVersion) - - const documentsToUpdate = await resolveDocumentChanges({ - originalDocuments: mergedDocuments, - newDocuments: draftDocuments, - }) - - const newDraftDocuments = ( - await replaceCommitChanges({ + const newDoc = await tx + .insert(documentVersions) + .values({ commitId, - documentChanges: documentsToUpdate, + path, + content: content ?? '', }) - ).unwrap() + .returning() - return Result.ok(newDraftDocuments.find((d) => d.path === path)!) - } catch (error) { - return Result.error(error as Error) - } + return Result.ok(newDoc[0]!) + }) } diff --git a/packages/core/src/services/documents/index.ts b/packages/core/src/services/documents/index.ts index c40e132f3..93b37a4e6 100644 --- a/packages/core/src/services/documents/index.ts +++ b/packages/core/src/services/documents/index.ts @@ -1,2 +1,3 @@ export * from './create' export * from './update' +export * from './recomputeChanges' diff --git a/packages/core/src/services/documents/recomputeChanges.ts b/packages/core/src/services/documents/recomputeChanges.ts new file mode 100644 index 000000000..e935db2a9 --- /dev/null +++ b/packages/core/src/services/documents/recomputeChanges.ts @@ -0,0 +1,64 @@ +import type { CompileError } from '@latitude-data/compiler' +import { database } from '$core/client' +import { findCommitById } from '$core/data-access' +import { Result, TypedResult } from '$core/lib' +import { BadRequestError } from '$core/lib/errors' +import { DocumentVersion } from '$core/schema' + +import { + getMergedAndDraftDocuments, + replaceCommitChanges, + resolveDocumentChanges, +} from './utils' + +type RecomputedChanges = { + documents: DocumentVersion[] + errors: { [documentUuid: string]: CompileError[] } +} + +export async function recomputeChanges( + { + commitId, + }: { + commitId: number + }, + tx = database, +): Promise> { + try { + const draft = (await findCommitById({ id: commitId }, tx)).unwrap() + if (draft.mergedAt !== null) { + return Result.error( + new BadRequestError('Cannot recompute changes in a merged commit'), + ) + } + + const [mergedDocuments, draftDocuments] = ( + await getMergedAndDraftDocuments( + { + draft, + }, + tx, + ) + ).unwrap() + + const { documents: documentsToUpdate, errors } = + await resolveDocumentChanges({ + originalDocuments: mergedDocuments, + newDocuments: draftDocuments, + }) + + const newDraftDocuments = ( + await replaceCommitChanges( + { + commitId, + documentChanges: documentsToUpdate, + }, + tx, + ) + ).unwrap() + + return Result.ok({ documents: newDraftDocuments, errors }) + } catch (error) { + return Result.error(error as Error) + } +} diff --git a/packages/core/src/services/documents/update.test.ts b/packages/core/src/services/documents/update.test.ts index 53cea4f02..55ec41b9f 100644 --- a/packages/core/src/services/documents/update.test.ts +++ b/packages/core/src/services/documents/update.test.ts @@ -1,30 +1,29 @@ -import { getDocumentsAtCommit, listCommitChanges } from '$core/data-access' +import { findHeadCommit, listCommitChanges } from '$core/data-access' import { describe, expect, it } from 'vitest' -import { mergeCommit } from '../commits/merge' +import { recomputeChanges } from './recomputeChanges' import { updateDocument } from './update' describe('updateDocument', () => { it('modifies a document that was created in a previous commit', async (ctx) => { - const { project } = await ctx.factories.createProject() - const { commit: commit1 } = await ctx.factories.createDraft({ project }) - const { documentVersion: doc } = await ctx.factories.createDocumentVersion({ - commit: commit1, - path: 'doc1', - content: 'Doc 1 commit 1', + const { project, documents } = await ctx.factories.createProject({ + documents: { + doc1: 'Doc 1 commit 1', + }, }) - await mergeCommit({ commitId: commit1.id }) - const { commit: commit2 } = await ctx.factories.createDraft({ project }) + const { commit } = await ctx.factories.createDraft({ project }) await updateDocument({ - commitId: commit2.id, - documentUuid: doc.documentUuid, + commitId: commit.id, + documentUuid: documents[0]!.documentUuid, content: 'Doc 1 commit 2', }).then((r) => r.unwrap()) + await recomputeChanges({ commitId: commit.id }) + const changedDocuments = await listCommitChanges({ - commitId: commit2.id, + commitId: commit.id, }).then((r) => r.unwrap()) expect(changedDocuments.length).toBe(1) @@ -47,6 +46,8 @@ describe('updateDocument', () => { content: 'Doc 1 v2', }).then((r) => r.unwrap()) + await recomputeChanges({ commitId: commit.id }) + const changedDocuments = await listCommitChanges({ commitId: commit.id, }).then((r) => r.unwrap()) @@ -57,31 +58,28 @@ describe('updateDocument', () => { }) it('modifying a document creates a change to all other documents that reference it', async (ctx) => { - const { project } = await ctx.factories.createProject() - const { commit: commit1 } = await ctx.factories.createDraft({ project }) - const { documentVersion: referencedDoc } = - await ctx.factories.createDocumentVersion({ - commit: commit1, - path: 'referenced/doc', - content: 'The document that is being referenced', - }) - await ctx.factories.createDocumentVersion({ - commit: commit1, - path: 'unmodified', - content: '', + const { project, documents } = await ctx.factories.createProject({ + documents: { + referenced: { + doc: 'The document that is being referenced', + }, + unmodified: '', + }, }) - await mergeCommit({ commitId: commit1.id }) - const { commit: commit2 } = await ctx.factories.createDraft({ project }) + const referencedDoc = documents.find((d) => d.path === 'referenced/doc')! + const { commit } = await ctx.factories.createDraft({ project }) await updateDocument({ - commitId: commit2.id, + commitId: commit.id, documentUuid: referencedDoc.documentUuid, content: 'The document that is being referenced v2', }).then((r) => r.unwrap()) + await recomputeChanges({ commitId: commit.id }) + const changedDocuments = await listCommitChanges({ - commitId: commit2.id, + commitId: commit.id, }).then((r) => r.unwrap()) expect(changedDocuments.length).toBe(2) @@ -92,7 +90,7 @@ describe('updateDocument', () => { }) it('renaming a document creates a change to all other documents that reference it', async (ctx) => { - const { project } = await ctx.factories.createProject({ + const { project, documents } = await ctx.factories.createProject({ documents: { referenced: { doc: 'The document that is being referenced', @@ -100,12 +98,9 @@ describe('updateDocument', () => { main: '', }, }) + const refDoc = documents.find((d) => d.path === 'referenced/doc')! const { commit } = await ctx.factories.createDraft({ project }) - const documents = await getDocumentsAtCommit({ commitId: commit.id }).then( - (r) => r.unwrap(), - ) - const refDoc = documents.find((d) => d.path === 'referenced/doc')! await updateDocument({ commitId: commit.id, @@ -113,6 +108,8 @@ describe('updateDocument', () => { path: 'referenced/doc2', }).then((r) => r.unwrap()) + await recomputeChanges({ commitId: commit.id }) + const changedDocuments = await listCommitChanges({ commitId: commit.id, }).then((r) => r.unwrap()) @@ -125,31 +122,28 @@ describe('updateDocument', () => { }) it('undoing a change to a document removes it from the list of changed documents', async (ctx) => { - const { project } = await ctx.factories.createProject() - const { commit: commit1 } = await ctx.factories.createDraft({ project }) - const { documentVersion: referencedDoc } = - await ctx.factories.createDocumentVersion({ - commit: commit1, - path: 'referenced/doc', - content: 'The document that is being referenced', - }) - await ctx.factories.createDocumentVersion({ - commit: commit1, - path: 'unmodified', - content: '', + const { project, documents } = await ctx.factories.createProject({ + documents: { + referenced: { + doc: 'The document that is being referenced', + }, + unmodified: '', + }, }) - await mergeCommit({ commitId: commit1.id }) + const referencedDoc = documents.find((d) => d.path === 'referenced/doc')! - const { commit: commit2 } = await ctx.factories.createDraft({ project }) + const { commit } = await ctx.factories.createDraft({ project }) await updateDocument({ - commitId: commit2.id, + commitId: commit.id, documentUuid: referencedDoc.documentUuid, content: 'The document that is being referenced v2', }).then((r) => r.unwrap()) + await recomputeChanges({ commitId: commit.id }) + const changedDocuments = await listCommitChanges({ - commitId: commit2.id, + commitId: commit.id, }).then((r) => r.unwrap()) expect(changedDocuments.length).toBe(2) @@ -159,15 +153,62 @@ describe('updateDocument', () => { expect(changedDocuments.find((d) => d.path === 'unmodified')).toBeDefined() await updateDocument({ - commitId: commit2.id, + commitId: commit.id, documentUuid: referencedDoc.documentUuid, content: referencedDoc.content, // Undo the change }).then((r) => r.unwrap()) + await recomputeChanges({ commitId: commit.id }) + const changedDocuments2 = await listCommitChanges({ - commitId: commit2.id, + commitId: commit.id, }).then((r) => r.unwrap()) expect(changedDocuments2.length).toBe(0) }) + + it('fails when renaming a document with a path that already exists', async (ctx) => { + const { project, documents } = await ctx.factories.createProject({ + documents: { + doc1: 'Doc 1', + doc2: 'Doc 2', + }, + }) + + const { commit } = await ctx.factories.createDraft({ project }) + const doc1 = documents.find((d) => d.path === 'doc1')! + + const updateResult = await updateDocument({ + commitId: commit.id, + documentUuid: doc1.documentUuid, + path: 'doc2', + }) + + expect(updateResult.ok).toBe(false) + expect(updateResult.error!.message).toBe( + 'A document with the same path already exists', + ) + }) + + it('fails when trying to create a document in a merged commit', async (ctx) => { + const { project, documents } = await ctx.factories.createProject({ + documents: { + foo: 'foo', + }, + }) + + const commit = await findHeadCommit({ projectId: project.id }).then((r) => + r.unwrap(), + ) + const fooDoc = documents.find((d) => d.path === 'foo')! + + const result = await updateDocument({ + commitId: commit.id, + documentUuid: fooDoc.documentUuid, + content: 'bar', + }) + + expect(result.ok).toBe(false) + expect(result.error!.message).toBe('Cannot modify a merged commit') + }) }) diff --git a/packages/core/src/services/documents/update.ts b/packages/core/src/services/documents/update.ts index 3ad21eafc..f105f7196 100644 --- a/packages/core/src/services/documents/update.ts +++ b/packages/core/src/services/documents/update.ts @@ -1,13 +1,9 @@ -import { Result, TypedResult } from '$core/lib' -import { BadRequestError, NotFoundError } from '$core/lib/errors' -import { DocumentVersion } from '$core/schema' +import { omit } from 'lodash-es' -import { - getDraft, - getMergedAndDraftDocuments, - replaceCommitChanges, - resolveDocumentChanges, -} from './shared' +import { findCommitById, getDocumentsAtCommit } from '$core/data-access' +import { Result, Transaction, TypedResult } from '$core/lib' +import { BadRequestError, NotFoundError } from '$core/lib/errors' +import { DocumentVersion, documentVersions } from '$core/schema' export async function updateDocument({ commitId, @@ -22,58 +18,57 @@ export async function updateDocument({ content?: string | null deletedAt?: Date | null }): Promise> { - try { - const draft = (await getDraft(commitId)).unwrap() - - const [mergedDocuments, draftDocuments] = ( - await getMergedAndDraftDocuments({ - draft, - }) - ).unwrap() - - const updatedDocData = Object.fromEntries( - Object.entries({ documentUuid, path, content, deletedAt }).filter( - ([_, v]) => v !== undefined, - ), - ) + const updatedDocData = Object.fromEntries( + Object.entries({ path, content, deletedAt }).filter( + ([_, v]) => v !== undefined, + ), + ) - const originalDoc = draftDocuments.find( - (d) => d.documentUuid === documentUuid!, - ) + return await Transaction.call(async (tx) => { + const commit = (await findCommitById({ id: commitId }, tx)).unwrap() + if (commit.mergedAt !== null) { + return Result.error(new BadRequestError('Cannot modify a merged commit')) + } + const currentDocs = (await getDocumentsAtCommit({ commitId }, tx)).unwrap() + const currentDoc = currentDocs.find((d) => d.documentUuid === documentUuid) - if (!originalDoc) { + if (!currentDoc) { return Result.error(new NotFoundError('Document does not exist')) } - Object.assign(originalDoc, updatedDocData) - - if ( - path && - draftDocuments.find( - (d) => d.documentUuid !== documentUuid && d.path === path, - ) - ) { - return Result.error( - new BadRequestError('A document with the same path already exists'), - ) + if (path !== undefined) { + if ( + currentDocs.find( + (d) => d.path === path && d.documentUuid !== documentUuid, + ) + ) { + return Result.error( + new BadRequestError('A document with the same path already exists'), + ) + } } - const documentsToUpdate = await resolveDocumentChanges({ - originalDocuments: mergedDocuments, - newDocuments: draftDocuments, - }) + const oldVersion = omit(currentDoc, ['id', 'commitId', 'updatedAt']) + + const newVersion = { + ...oldVersion, + ...updatedDocData, + commitId, + } - const newDraftDocuments = ( - await replaceCommitChanges({ - commitId, - documentChanges: documentsToUpdate, + const updatedDocs = await tx + .insert(documentVersions) + .values(newVersion) + .onConflictDoUpdate({ + target: [documentVersions.documentUuid, documentVersions.commitId], + set: newVersion, }) - ).unwrap() + .returning() + + if (updatedDocs.length === 0) { + return Result.error(new NotFoundError('Document does not exist')) + } - return Result.ok( - newDraftDocuments.find((d) => d.documentUuid === documentUuid)!, - ) - } catch (error) { - return Result.error(error as Error) - } + return Result.ok(updatedDocs[0]!) + }) } diff --git a/packages/core/src/services/documents/shared.ts b/packages/core/src/services/documents/utils.ts similarity index 55% rename from packages/core/src/services/documents/shared.ts rename to packages/core/src/services/documents/utils.ts index f7fc533b8..ff2dd82e9 100644 --- a/packages/core/src/services/documents/shared.ts +++ b/packages/core/src/services/documents/utils.ts @@ -1,19 +1,16 @@ import { omit } from 'lodash-es' -import { readMetadata } from '@latitude-data/compiler' +import { readMetadata, type CompileError } from '@latitude-data/compiler' +import { database } from '$core/client' import { - Commit, - DocumentVersion, - documentVersions, findCommitById, findHeadCommit, getDocumentsAtCommit, listCommitChanges, - Result, - Transaction, - TypedResult, -} from '@latitude-data/core' +} from '$core/data-access' +import { Result, Transaction, TypedResult } from '$core/lib' import { ForbiddenError, LatitudeError } from '$core/lib/errors' +import { Commit, DocumentVersion, documentVersions } from '$core/schema' import { eq } from 'drizzle-orm' export async function getDraft( @@ -30,29 +27,39 @@ export async function getDraft( return Result.ok(commit.value!) } -export async function getMergedAndDraftDocuments({ - draft, -}: { - draft: Commit -}): Promise> { - const headCommit = await findHeadCommit({ projectId: draft.projectId }) - if (headCommit.error) return headCommit - - const mergedDocuments = await getDocumentsAtCommit({ - commitId: headCommit.value.id, - }) - if (mergedDocuments.error) return mergedDocuments +export async function getMergedAndDraftDocuments( + { + draft, + }: { + draft: Commit + }, + tx = database, +): Promise> { + const mergedDocuments: DocumentVersion[] = [] + + const headCommit = await findHeadCommit({ projectId: draft.projectId }, tx) + if (headCommit.ok) { + // "Head commit" may not exist if the project is empty + const headDocuments = await getDocumentsAtCommit( + { + commitId: headCommit.value!.id, + }, + tx, + ) + if (headDocuments.error) return headDocuments + mergedDocuments.push(...headDocuments.value) + } - const draftChanges = await listCommitChanges({ commitId: draft.id }) + const draftChanges = await listCommitChanges({ commitId: draft.id }, tx) if (draftChanges.error) return Result.error(draftChanges.error) - const draftDocuments = mergedDocuments.value + const draftDocuments = mergedDocuments .filter( (d) => !draftChanges.value.find((c) => c.documentUuid === d.documentUuid), ) .concat(draftChanges.value) - return Result.ok([mergedDocuments.value, structuredClone(draftDocuments)]) + return Result.ok([mergedDocuments, structuredClone(draftDocuments)]) } export function existsAnotherDocumentWithSamePath({ @@ -71,7 +78,12 @@ export async function resolveDocumentChanges({ }: { originalDocuments: DocumentVersion[] newDocuments: DocumentVersion[] -}): Promise { +}): Promise<{ + documents: DocumentVersion[] + errors: Record +}> { + const errors: Record = {} + const getDocumentContent = async (path: string): Promise => { const document = newDocuments.find((d) => d.path === path) if (!document) { @@ -81,16 +93,23 @@ export async function resolveDocumentChanges({ } const newDocumentsWithUpdatedHash = await Promise.all( - newDocuments.map(async (d) => ({ - ...d, - hash: await readMetadata({ + newDocuments.map(async (d) => { + const metadata = await readMetadata({ prompt: d.content ?? '', referenceFn: getDocumentContent, - }).then((m) => m.hash), - })), + }) + if (metadata.errors.length > 0) { + errors[d.documentUuid] = metadata.errors + } + + return { + ...d, + hash: metadata.hash, + } + }), ) - return newDocumentsWithUpdatedHash.filter( + const changedDocuments = newDocumentsWithUpdatedHash.filter( (newDoc) => !originalDocuments.find( (oldDoc) => @@ -99,23 +118,28 @@ export async function resolveDocumentChanges({ oldDoc.path === newDoc.path, ), ) + + return { documents: changedDocuments, errors } } -export async function replaceCommitChanges({ - commitId, - documentChanges, -}: { - commitId: number - documentChanges: DocumentVersion[] -}): Promise> { - return Transaction.call(async (tx) => { - await tx +export async function replaceCommitChanges( + { + commitId, + documentChanges, + }: { + commitId: number + documentChanges: DocumentVersion[] + }, + tx = database, +): Promise> { + return Transaction.call(async (trx) => { + await trx .delete(documentVersions) .where(eq(documentVersions.commitId, commitId)) if (documentChanges.length === 0) return Result.ok([]) - const insertedDocuments = await tx + const insertedDocuments = await trx .insert(documentVersions) .values( documentChanges.map((d) => ({ @@ -126,5 +150,5 @@ export async function replaceCommitChanges({ .returning() return Result.ok(insertedDocuments) - }) + }, tx) } diff --git a/packages/core/src/services/projects/create.ts b/packages/core/src/services/projects/create.ts index 22dda57c0..b4dd17f5a 100644 --- a/packages/core/src/services/projects/create.ts +++ b/packages/core/src/services/projects/create.ts @@ -6,7 +6,6 @@ import { type Project, } from '@latitude-data/core' import { createCommit } from '$core/services/commits/create' -import { mergeCommit } from '$core/services/commits/merge' export async function createProject( { @@ -23,16 +22,16 @@ export async function createProject( await tx.insert(projects).values({ workspaceId, name }).returning() )[0]! const commit = await createCommit({ - commit: { projectId: project.id, title: 'Initial version' }, + commit: { + projectId: project.id, + title: 'Initial version', + mergedAt: new Date(), + }, db: tx, }) if (commit.error) return commit - const resultMerge = await mergeCommit({ commitId: commit.value.id }, tx) - - if (resultMerge.error) return resultMerge - return Result.ok(project) }, db) } diff --git a/packages/core/src/tests/factories/projects.ts b/packages/core/src/tests/factories/projects.ts index 449274bd5..03b71c28d 100644 --- a/packages/core/src/tests/factories/projects.ts +++ b/packages/core/src/tests/factories/projects.ts @@ -1,6 +1,6 @@ import { faker } from '@faker-js/faker' import { getUser } from '$core/data-access' -import { Workspace, type SafeUser } from '$core/schema' +import { DocumentVersion, Workspace, type SafeUser } from '$core/schema' import { createNewDocument, mergeCommit, updateDocument } from '$core/services' import { createProject as createProjectFn } from '$core/services/projects' @@ -62,23 +62,26 @@ export async function createProject(projectData: Partial = {}) { }) const project = result.unwrap() + const documents: DocumentVersion[] = [] + if (projectData.documents) { - const documents = await flattenDocumentStructure({ + const documentsToCreate = await flattenDocumentStructure({ documents: projectData.documents, }) const { commit: draft } = await createDraft({ project }) - for await (const { path, content } of documents) { + for await (const { path, content } of documentsToCreate) { const newDoc = await createNewDocument({ commitId: draft.id, path }).then( (r) => r.unwrap(), ) - await updateDocument({ + const updatedDoc = await updateDocument({ commitId: draft.id, documentUuid: newDoc.documentUuid, content, }) + documents.push(updatedDoc.unwrap()) } - await mergeCommit({ commitId: draft.id }) + await mergeCommit({ commitId: draft.id }).then((r) => r.unwrap()) } - return { project, user, workspace } + return { project, user, workspace, documents } }