Skip to content

Commit

Permalink
fix: version restoration (#6039)
Browse files Browse the repository at this point in the history
  • Loading branch information
JarrodMFlesch committed Apr 26, 2024
1 parent 33f6edc commit 91bac9c
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 39 deletions.
Expand Up @@ -66,7 +66,9 @@ const Restore: React.FC<Props> = ({
if (res.status === 200) {
const json = await res.json()
toast.success(json.message)
history.push(redirectURL)
history.push(redirectURL, {
refetchDocumentData: true,
})
} else {
toast.error(t('problemRestoringVersion'))
}
Expand Down
Expand Up @@ -50,15 +50,16 @@ const EditView: React.FC<IndexProps> = (props) => {
} = config

const { params: { id } = {} } = useRouteMatch<Record<string, string>>()
const history = useHistory()
const history = useHistory<{ refetchDocumentData?: boolean }>()

const [internalState, setInternalState] = useState<Fields>()
const [updatedAt, setUpdatedAt] = useState<string>()
const { permissions, user } = useAuth()
const userRef = useRef(user)
const { docPermissions, getDocPermissions, getDocPreferences, getVersions } = useDocumentInfo()
const { t } = useTranslation('general')

const [{ data, isError, isLoading: isLoadingData }] = usePayloadAPI(
const [{ data, isError, isLoading: isLoadingData }, { refetchData }] = usePayloadAPI(
isEditing ? `${serverURL}${api}/${collectionSlug}/${id}` : '',
{ initialData: null, initialParams: { depth: 0, draft: 'true', 'fallback-locale': 'null' } },
)
Expand Down Expand Up @@ -128,10 +129,16 @@ const EditView: React.FC<IndexProps> = (props) => {
useEffect(() => {
setFormQueryParams((params) => ({
...params,
locale: locale,
locale,
}))
}, [locale])

useEffect(() => {
if (history.location.state?.refetchDocumentData) {
void refetchData()
}
}, [history.location.state?.refetchDocumentData, refetchData])

if (isError) {
return <NotFound marginTop="large" />
}
Expand Down
75 changes: 40 additions & 35 deletions packages/payload/src/admin/hooks/usePayloadAPI.tsx
@@ -1,5 +1,5 @@
import queryString from 'qs'
import { useEffect, useState } from 'react'
import { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'

import { requests } from '../api'
Expand All @@ -12,6 +12,7 @@ type Result = [
isLoading: boolean
},
{
refetchData: (abortController?: AbortController) => Promise<void>
setParams: React.Dispatch<unknown>
},
]
Expand Down Expand Up @@ -43,49 +44,53 @@ const usePayloadAPI: UsePayloadAPI = (url, options = {}) => {
},
)

useEffect(() => {
const abortController = new AbortController()

const fetchData = async () => {
setIsError(false)
setIsLoading(true)

try {
const response = await requests.get(`${url}${search}`, {
headers: {
'Accept-Language': i18n.language,
},
signal: abortController.signal,
})

if (response.status > 201) {
setIsError(true)
}

const json = await response.json()
setData(json)
setIsLoading(false)
} catch (error) {
if (!abortController.signal.aborted) {
setIsError(true)
const fetchData = useCallback(
async (abortController?: AbortController) => {
if (url) {
setIsError(false)
setIsLoading(true)
try {
const response = await requests.get(`${url}${search}`, {
headers: {
'Accept-Language': i18n.language,
},
signal: abortController ? abortController.signal : undefined,
})

if (response.status > 201) {
setIsError(true)
}

const json = await response.json()
setData(json)
setIsLoading(false)
} catch (error) {
if (!abortController || !abortController.signal.aborted) {
setIsError(true)
setIsLoading(false)
}
}
} else {
setIsError(false)
setIsLoading(false)
}
}
},
[url, search, i18n.language],
)

if (url) {
fetchData()
} else {
setIsError(false)
setIsLoading(false)
}
useEffect(() => {
const abortController = new AbortController()
void fetchData(abortController)

return () => {
abortController.abort()
}
}, [url, locale, search, i18n.language])
}, [url, search, fetchData])

return [{ data, isError, isLoading }, { setParams }]
return [
{ data, isError, isLoading },
{ refetchData: fetchData, setParams },
]
}

export default usePayloadAPI
3 changes: 3 additions & 0 deletions playwright.config.ts
@@ -1,5 +1,8 @@
import type { PlaywrightTestConfig } from '@playwright/test'

export const EXPECT_TIMEOUT = 45000
export const POLL_TOPASS_TIMEOUT = EXPECT_TIMEOUT * 4 // That way expect.poll() or expect().toPass can retry 4 times. 4x higher than default expect timeout => can retry 4 times if retryable expects are used inside

const config: PlaywrightTestConfig = {
// Look for test files in the "test" directory, relative to this configuration file
testDir: 'test',
Expand Down
34 changes: 34 additions & 0 deletions test/versions/e2e.spec.ts
Expand Up @@ -28,12 +28,14 @@ import { expect, test } from '@playwright/test'

import payload from '../../packages/payload/src'
import wait from '../../packages/payload/src/utilities/wait'
import { POLL_TOPASS_TIMEOUT } from '../../playwright.config'
import { globalSlug } from '../admin/slugs'
import {
changeLocale,
exactText,
findTableCell,
initPageConsoleErrorCatch,
saveDocAndAssert,
selectTableRow,
} from '../helpers'
import { AdminUrlUtil } from '../helpers/adminUrlUtil'
Expand Down Expand Up @@ -240,6 +242,38 @@ describe('versions', () => {
await expect(page.locator('.app-header .collection-versions-button')).toHaveCount(1)
})

test('should restore version with correct data', async () => {
await page.goto(url.create)
await page.waitForURL(url.create)

// publish a doc
await page.locator('#field-title').fill('v1')
await page.locator('#field-description').fill('hello')
await saveDocAndAssert(page)

// save a draft
await page.locator('#field-title').fill('v2')
await saveDocAndAssert(page, '#action-save-draft')

// go to versions list view
const savedDocURL = page.url()
await page.goto(`${savedDocURL}/versions`)
await page.waitForURL(`${savedDocURL}/versions`)

// select the first version (row 2)
const row2 = page.locator('tbody .row-2')
const versionID = await row2.locator('.cell-id').textContent()
await page.goto(`${savedDocURL}/versions/${versionID}`)
await page.waitForURL(`${savedDocURL}/versions/${versionID}`)

// restore doc
await page.locator('.pill.restore-version').click()
await page.locator('button:has-text("Confirm")').click()
await expect.poll(() => page.url(), { timeout: POLL_TOPASS_TIMEOUT }).not.toContain(versionID)

await expect(page.locator('#field-title')).toHaveValue('v1')
})

test('should show global versions view level action in globals versions view', async () => {
const global = new AdminUrlUtil(serverURL, draftGlobalSlug)
await page.goto(`${global.global(draftGlobalSlug)}/versions`)
Expand Down

0 comments on commit 91bac9c

Please sign in to comment.