Skip to content

Commit

Permalink
fix(releases): when reverting, published versions are used to derive …
Browse files Browse the repository at this point in the history
…translog (#8112)
  • Loading branch information
jordanl17 authored Dec 20, 2024
1 parent 12c37ca commit d6c6663
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ vi.mock('../../../../../../store/translog/getTransactionLogs', () => ({

describe('useDocumentRevertStates', () => {
const mockDocuments = [
{document: {_id: 'doc1', _rev: 'rev1'}},
{document: {_id: 'doc2', _rev: 'rev2'}},
{document: {_id: 'versions.r1.doc1', _rev: 'rev1'}},
{document: {_id: 'versions.r1.doc2', _rev: 'rev2'}},
] as DocumentInRelease[]

/** @todo improve the useClient mock */
Expand All @@ -45,8 +45,10 @@ describe('useDocumentRevertStates', () => {
mockUseClient.mockReturnValue(mockClient)

mockGetTransactionsLogs.mockResolvedValue([
{id: 'trans1', documentIDs: ['doc1'], timestamp: new Date().toISOString()},
{id: 'trans2', documentIDs: ['doc2'], timestamp: new Date().toISOString()},
{id: 'trans0_doc1', documentIDs: ['doc1'], timestamp: new Date().toISOString()},
{id: 'trans1_doc1', documentIDs: ['doc1'], timestamp: new Date().toISOString()},
{id: 'trans0_doc2', documentIDs: ['doc2'], timestamp: new Date().toISOString()},
{id: 'trans1_doc2', documentIDs: ['doc2'], timestamp: new Date().toISOString()},
] as TransactionLogEventWithEffects[])

mockClient.observable.request.mockImplementation(({url}) => {
Expand Down Expand Up @@ -89,8 +91,8 @@ describe('useDocumentRevertStates', () => {
expect(resolvedResult).toEqual([
{
_id: 'doc1',
_rev: 'rev1',
_system: {delete: true},
_rev: 'observable-rev-1',
title: 'Reverted Document 1',
},
{
_id: 'doc2',
Expand All @@ -102,18 +104,28 @@ describe('useDocumentRevertStates', () => {

expect(mockGetTransactionsLogs).toHaveBeenCalledWith(mockClient, ['doc1', 'doc2'], {
toTransaction: 'rev1',
limit: 3,
reverse: true,
})

expect(mockClient.observable.request).toHaveBeenCalledWith({
url: '/data/history/test-dataset/documents/doc2?revision=trans2',
expect(mockClient.observable.request).toHaveBeenNthCalledWith(1, {
url: '/data/history/test-dataset/documents/doc1?revision=trans1_doc1',
})
expect(mockClient.observable.request).toHaveBeenNthCalledWith(2, {
url: '/data/history/test-dataset/documents/doc2?revision=trans1_doc2',
})
})

it('should handle missing revisions and mark for deletion', async () => {
mockClient.observable.request.mockReturnValueOnce(of({documents: []})) // No revert document found

mockGetTransactionsLogs.mockResolvedValue([
// publish transaction for release
{
id: 'trans0_doc1',
effects: {},
author: 'author',
documentIDs: ['doc1'],
timestamp: new Date().toISOString(),
},
])
const {result} = renderHook(() => useDocumentRevertStates(mockDocuments))

await waitFor(async () => {
Expand All @@ -124,12 +136,16 @@ describe('useDocumentRevertStates', () => {
_rev: 'rev1',
_system: {delete: true},
},
{
_id: 'doc2',
_rev: 'rev2',
_system: {delete: true},
},
])
})

expect(mockGetTransactionsLogs).toHaveBeenCalledWith(mockClient, ['doc1', 'doc2'], {
toTransaction: 'rev1',
limit: 3,
reverse: true,
})
})
Expand All @@ -146,7 +162,6 @@ describe('useDocumentRevertStates', () => {

expect(mockGetTransactionsLogs).toHaveBeenCalledWith(mockClient, ['doc1', 'doc2'], {
toTransaction: 'rev1',
limit: 3,
reverse: true,
})

Expand All @@ -165,27 +180,44 @@ describe('useDocumentRevertStates', () => {

expect(mockGetTransactionsLogs).toHaveBeenCalledWith(mockClient, ['doc1', 'doc2'], {
toTransaction: 'rev1',
limit: 3,
reverse: true,
})

expect(mockClient.observable.request).not.toHaveBeenCalled() // No API calls on failure
})

it('should handle a mix of existing and missing revisions', async () => {
mockClient.observable.request.mockImplementation(({url}) => {
if (url!.includes('doc1')) {
return of({
documents: [
{
_id: 'doc1',
title: 'Reverted Document 1',
},
],
})
}
return of({documents: []}) // No revert document for doc2
})
mockGetTransactionsLogs.mockResolvedValue([
// publish transaction for release
{
id: 'trans0_doc1',
effects: {},
author: 'author',
documentIDs: ['doc1'],
timestamp: new Date().toISOString(),
},
{
id: 'trans0_doc2',
effects: {},
author: 'author',
documentIDs: ['doc2'],
timestamp: new Date().toISOString(),
},
{
id: 'trans1_doc1',
effects: {},
author: 'author',
documentIDs: ['doc1'],
timestamp: new Date().toISOString(),
},
{
id: 'trans2_doc1',
effects: {},
author: 'author',
documentIDs: ['doc1'],
timestamp: new Date().toISOString(),
},
])

const {result} = renderHook(() => useDocumentRevertStates(mockDocuments))

Expand All @@ -194,16 +226,23 @@ describe('useDocumentRevertStates', () => {
expect(resolvedResult).toEqual([
{
_id: 'doc1',
_rev: 'rev1',
_rev: 'observable-rev-1',
title: 'Reverted Document 1',
},
{
_id: 'doc2',
_rev: 'rev2',
_system: {delete: true},
},
])
})

expect(mockGetTransactionsLogs).toHaveBeenCalledWith(mockClient, ['doc1', 'doc2'], {
toTransaction: 'rev1',
limit: 3,
reverse: true,
})
expect(mockClient.observable.request).toHaveBeenNthCalledWith(1, {
url: '/data/history/test-dataset/documents/doc1?revision=trans1_doc1',
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {type SanityDocument} from '@sanity/types'
import {useCallback, useEffect, useMemo, useRef} from 'react'
import {useObservable} from 'react-rx'
import {catchError, forkJoin, from, map, type Observable, of, switchMap} from 'rxjs'
import {getPublishedId} from 'sanity'

import {useClient} from '../../../../../hooks/useClient'
import {getTransactionsLogs} from '../../../../../store/translog/getTransactionLogs'
Expand All @@ -18,10 +19,10 @@ type RevertDocuments = RevertDocument[]

type DocumentRevertStates = RevertDocuments | null | undefined

export const useDocumentRevertStates = (documents: DocumentInRelease[]) => {
export const useDocumentRevertStates = (releaseDocuments: DocumentInRelease[]) => {
const client = useClient({apiVersion: API_VERSION})
const observableClient = client.observable
const transactionId = documents[0]?.document._rev
const transactionId = releaseDocuments[0]?.document._rev
const {dataset} = client.config()

const resultPromiseRef = useRef<Promise<DocumentRevertStates> | null>(null)
Expand All @@ -40,30 +41,33 @@ export const useDocumentRevertStates = (documents: DocumentInRelease[]) => {
}, [])

const memoDocumentRevertStates = useMemo(() => {
if (!documents.length) return of(undefined)
if (!releaseDocuments.length) return of(undefined)

const publishedDocuments = releaseDocuments.map(({document}) => ({
...document,
_id: getPublishedId(document._id),
}))

const documentRevertStates$: Observable<RevertDocuments | null | undefined> = from(
getTransactionsLogs(
client,
documents.map(({document}) => document._id),
publishedDocuments.map((document) => document._id),
{
toTransaction: transactionId,
// one transaction for every document plus the publish transaction
limit: documents.length + 1,
// reverse to find the transactions immediately before publish
// reverse order so most recent publish before release is second element
// (first is the release publish itself)
reverse: true,
},
),
).pipe(
map((transactions) => {
if (transactions.length === 0) throw new Error('No transactions found.')

const [publishTransaction, ...otherTransactions] = transactions

const getDocumentTransaction = (docId: string) =>
otherTransactions.find(({documentIDs}) => documentIDs.includes(docId))?.id
// second element is the transaction before the release
transactions.filter(({documentIDs}) => documentIDs.includes(docId))[1]?.id

return documents.map(({document}) => ({
return publishedDocuments.map((document) => ({
docId: document._id,
revisionId: getDocumentTransaction(document._id),
}))
Expand All @@ -75,7 +79,7 @@ export const useDocumentRevertStates = (documents: DocumentInRelease[]) => {
docRevisionPairs.map(({docId, revisionId}) => {
if (!revisionId) {
const {publishedDocumentExists, ...unpublishDocument} =
documents.find(({document}) => document._id === docId)?.document || {}
publishedDocuments.find((document) => document._id === docId) || {}

return of({
...unpublishDocument,
Expand Down Expand Up @@ -107,7 +111,7 @@ export const useDocumentRevertStates = (documents: DocumentInRelease[]) => {
)

return documentRevertStates$
}, [client, documents, transactionId, observableClient, dataset])
}, [client, releaseDocuments, transactionId, observableClient, dataset])

const documentRevertStatesResult = useObservable(memoDocumentRevertStates, null)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {useMemo} from 'react'
import {useObservable} from 'react-rx'
import {catchError, from, map, of} from 'rxjs'
import {getPublishedId} from 'sanity'

import {useClient} from '../../../../../hooks/useClient'
import {getTransactionsLogs} from '../../../../../store/translog/getTransactionLogs'
Expand All @@ -17,7 +18,7 @@ export const usePostPublishTransactions = (documents: DocumentInRelease[]) => {
return from(
getTransactionsLogs(
client,
documents.map(({document}) => document._id),
documents.map(({document}) => getPublishedId(document._id)),
{
fromTransaction: transactionId,
// publish transaction + at least one post publish transaction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
getVersionFromId,
getVersionId,
removeDupes,
} from './draftUtils'
} from '../draftUtils'

test('collate()', () => {
const foo = {_type: 'foo', _id: 'foo'}
Expand Down

0 comments on commit d6c6663

Please sign in to comment.