Skip to content

Commit

Permalink
Recompute changes on commit to find all indirect changes and syntax e…
Browse files Browse the repository at this point in the history
…rrors (#53)

* Recompute all draft changes when updating any document

* Changed recompute on demand
  • Loading branch information
csansoon authored Jul 24, 2024
1 parent ba7494a commit e28a430
Show file tree
Hide file tree
Showing 12 changed files with 488 additions and 228 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -79,14 +79,17 @@ export async function getDocumentsAtCommit(
{ commitId }: { commitId: number },
tx = database,
): Promise<TypedResult<DocumentVersion[], LatitudeError>> {
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.
Expand Down Expand Up @@ -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)
}
Expand Down
11 changes: 11 additions & 0 deletions packages/core/src/services/commits/merge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
Commit,
commits,
database,
recomputeChanges,
Result,
Transaction,
} from '@latitude-data/core'
Expand Down Expand Up @@ -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 })
Expand Down
18 changes: 15 additions & 3 deletions packages/core/src/services/documents/create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +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('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 mergeCommit({ commitId: commit.id })

const result = await createNewDocument({
commitId: commit.id,
path: 'foo',
})

expect(result.ok).toBe(false)
expect(result.error!.message).toBe('Cannot modify a merged commit')
})
})
55 changes: 21 additions & 34 deletions packages/core/src/services/documents/create.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,39 @@
import {
DocumentVersion,
documentVersions,
getDocumentsAtCommit,
Result,
Transaction,
} 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 {
assertCommitIsEditable,
existsAnotherDocumentWithSamePath,
} from './utils'
import { DocumentVersion, documentVersions } from '$core/schema'

export async function createNewDocument({
commitId,
path,
content,
}: {
commitId: number
path: string
}) {
const commitResult = await assertCommitIsEditable(commitId)
if (commitResult.error) return commitResult

const currentDocuments = await getDocumentsAtCommit({
commitId,
})
if (currentDocuments.error) return currentDocuments
content?: string
}): Promise<TypedResult<DocumentVersion, Error>> {
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 (
existsAnotherDocumentWithSamePath({
documents: currentDocuments.value,
path,
})
) {
return Result.error(
new BadRequestError('A document with the same path already exists'),
)
}
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'),
)
}

return Transaction.call<DocumentVersion>(async (tx) => {
const result = await tx
const newDoc = await tx
.insert(documentVersions)
.values({
commitId,
path,
content: content ?? '',
})
.returning()
const documentVersion = result[0]
return Result.ok(documentVersion!)

return Result.ok(newDoc[0]!)
})
}
1 change: 1 addition & 0 deletions packages/core/src/services/documents/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './create'
export * from './update'
export * from './recomputeChanges'
64 changes: 64 additions & 0 deletions packages/core/src/services/documents/recomputeChanges.ts
Original file line number Diff line number Diff line change
@@ -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<TypedResult<RecomputedChanges, Error>> {
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)
}
}
Loading

0 comments on commit e28a430

Please sign in to comment.