fix: properly store timestamps in versions (#8646)

This PR makes a more clear gap between `version_createdAt` /
`version_updatedAt` and `createdAt` / `updatedAt` columns / fields in
mongodb.

- `createdAt` - This should be a new value in a new version. Before this
change it was the same all the time. Should remain the same on autosave.
- The same for `updatedAt`, but it should be updated on every change
(including autosave)
- `version_createdAt` - Should remain equal to `createdAt` from the
parent collection / table
- `version_updatedAt` - On a latest version it makes sense this be the
same as `updatedAt` from the parent collection / table, as all the
`version_*` fields should be just synced with it
This commit is contained in:
Sasha
2024-10-11 17:01:21 +03:00
committed by GitHub
parent 067d353cdd
commit 8daac4e670
15 changed files with 128 additions and 48 deletions

View File

@@ -99,8 +99,6 @@ export const queryDrafts: QueryDrafts = async function queryDrafts(
_id: doc.parent,
id: doc.parent,
...doc.version,
createdAt: doc.createdAt,
updatedAt: doc.updatedAt,
}
return sanitizeInternalFields(doc)

View File

@@ -12,10 +12,12 @@ export async function createGlobalVersion<T extends TypeWithID>(
this: DrizzleAdapter,
{
autosave,
createdAt,
globalSlug,
publishedLocale,
req = {} as PayloadRequest,
snapshot,
updatedAt,
versionData,
}: CreateGlobalVersionArgs,
) {
@@ -28,9 +30,11 @@ export async function createGlobalVersion<T extends TypeWithID>(
adapter: this,
data: {
autosave,
createdAt,
latest: true,
publishedLocale,
snapshot,
updatedAt,
version: versionData,
},
db,

View File

@@ -13,10 +13,12 @@ export async function createVersion<T extends TypeWithID>(
{
autosave,
collectionSlug,
createdAt,
parent,
publishedLocale,
req = {} as PayloadRequest,
snapshot,
updatedAt,
versionData,
}: CreateVersionArgs<T>,
) {
@@ -33,17 +35,15 @@ export async function createVersion<T extends TypeWithID>(
const data: Record<string, unknown> = {
autosave,
createdAt,
latest: true,
parent,
publishedLocale,
snapshot,
updatedAt,
version,
}
if ('createdAt' in version) {
data.createdAt = version.createdAt
}
const result = await upsertRow<TypeWithVersion<T>>({
adapter: this,
data,

View File

@@ -38,8 +38,6 @@ export const queryDrafts: QueryDrafts = async function queryDrafts(
doc = {
id: doc.parent,
...doc.version,
createdAt: doc.createdAt,
updatedAt: doc.updatedAt,
}
return doc

View File

@@ -267,10 +267,7 @@ export const duplicateOperation = async <TSlug extends CollectionSlug>(
result = await saveVersion({
id: versionDoc.id,
collection: collectionConfig,
docWithLocales: {
...versionDoc,
createdAt: result.createdAt,
},
docWithLocales: versionDoc,
draft: shouldSaveDraft,
payload,
req,

View File

@@ -131,7 +131,7 @@ export const findOperation = async <TSlug extends CollectionSlug>(
page: sanitizedPage,
pagination: usePagination,
req,
sort: getQueryDraftsSort(sort),
sort: getQueryDraftsSort({ collectionConfig, sort }),
where: fullWhere,
})
} else {

View File

@@ -329,10 +329,7 @@ export const updateOperation = async <TSlug extends CollectionSlug>(
result = await saveVersion({
id,
collection: collectionConfig,
docWithLocales: {
...result,
createdAt: doc.createdAt,
},
docWithLocales: result,
payload,
req,
})

View File

@@ -356,10 +356,7 @@ export const updateByIDOperation = async <TSlug extends CollectionSlug>(
id,
autosave,
collection: collectionConfig,
docWithLocales: {
...result,
createdAt: docWithLocales.createdAt,
},
docWithLocales: result,
draft: shouldSaveDraft,
payload,
publishSpecificLocale,

View File

@@ -245,11 +245,7 @@ export const updateOperation = async <TSlug extends GlobalSlug>(
const { globalType } = result
result = await saveVersion({
autosave,
docWithLocales: {
...result,
createdAt: result.createdAt,
updatedAt: result.updatedAt,
},
docWithLocales: result,
draft: shouldSaveDraft,
global: globalConfig,
payload,

View File

@@ -1,10 +1,22 @@
import type { SanitizedCollectionConfig } from '../../collections/config/types.js'
/**
* Takes the incoming sort argument and prefixes it with `versions.` and preserves any `-` prefixes for descending order
* @param sort
*/
export const getQueryDraftsSort = (sort: string): string => {
export const getQueryDraftsSort = ({
collectionConfig,
sort,
}: {
collectionConfig: SanitizedCollectionConfig
sort: string
}): string => {
if (!sort) {
return sort
if (collectionConfig.defaultSort) {
sort = collectionConfig.defaultSort
} else {
sort = '-createdAt'
}
}
let direction = ''

View File

@@ -48,10 +48,7 @@ export const getLatestCollectionVersion = async <T extends TypeWithID = any>({
return undefined
}
return {
...latestVersion.version,
id,
createdAt: latestVersion.createdAt,
updatedAt: latestVersion.updatedAt,
}
latestVersion.version.id = id
return latestVersion.version
}

View File

@@ -56,12 +56,16 @@ export const getLatestGlobalVersion = async ({
}
}
if (!latestVersion.version.createdAt) {
latestVersion.version.createdAt = latestVersion.createdAt
}
if (!latestVersion.version.updatedAt) {
latestVersion.version.updatedAt = latestVersion.updatedAt
}
return {
global: {
...latestVersion.version,
createdAt: latestVersion.createdAt,
updatedAt: latestVersion.updatedAt,
},
global: latestVersion.version,
globalExists,
}
}

View File

@@ -82,7 +82,7 @@ export const saveVersion = async ({
const data: Record<string, unknown> = {
createdAt: new Date(latestVersion.createdAt).toISOString(),
updatedAt: draft ? now : new Date(doc.updatedAt).toISOString(),
updatedAt: now,
version: {
...versionData,
},
@@ -114,12 +114,12 @@ export const saveVersion = async ({
const createVersionArgs = {
autosave: Boolean(autosave),
collectionSlug: undefined,
createdAt: doc?.createdAt ? new Date(doc.createdAt).toISOString() : now,
createdAt: now,
globalSlug: undefined,
parent: collection ? id : undefined,
publishedLocale: publishSpecificLocale || undefined,
req,
updatedAt: draft ? now : new Date(doc.updatedAt).toISOString(),
updatedAt: now,
versionData,
}
@@ -194,8 +194,6 @@ export const saveVersion = async ({
}
let createdVersion = result.version
createdVersion.createdAt = result.createdAt
createdVersion.updatedAt = result.updatedAt
createdVersion = sanitizeInternalFields(createdVersion)
createdVersion.id = result.parent

View File

@@ -339,6 +339,11 @@ const DocumentInfo: React.FC<
...versionParams.where,
and: [
...versionParams.where.and,
{
'version._status': {
equals: 'draft',
},
},
{
updatedAt: {
greater_than: publishedJSON.updatedAt,

View File

@@ -14,6 +14,7 @@ import AutosavePosts from './collections/Autosave.js'
import AutosaveGlobal from './globals/Autosave.js'
import {
autosaveCollectionSlug,
autoSaveGlobalSlug,
draftCollectionSlug,
localizedCollectionSlug,
localizedGlobalSlug,
@@ -306,21 +307,55 @@ describe('Versions', () => {
)
})
it('should have the same createdAt on new version create', async () => {
it('should have different createdAt in a new version while the same version.createdAt', async () => {
const doc = await payload.create({
collection: autosaveCollectionSlug,
data: { description: 'descr', title: 'title' },
})
await wait(30)
await wait(10)
const updated = await payload.update({
const upd = await payload.update({
collection: autosaveCollectionSlug,
id: doc.id,
data: {},
})
expect(doc.createdAt).toBe(updated.createdAt)
expect(upd.createdAt).toBe(doc.createdAt)
const {
docs: [latestVersionData],
} = await payload.findVersions({
collection: autosaveCollectionSlug,
where: {
and: [
{
parent: {
equals: doc.id,
},
latest: {
equals: true,
},
},
],
},
})
// Version itself should have new createdAt
expect(new Date(latestVersionData.createdAt) > new Date(doc.createdAt)).toBe(true)
// But the same createdAt in version data!
expect(latestVersionData.version.createdAt).toBe(doc.createdAt)
const fromNonVersionsTable = await payload.findByID({
draft: false,
id: doc.id,
collection: autosaveCollectionSlug,
})
// createdAt from non-versions should be the same as version_createdAt in versions
expect(fromNonVersionsTable.createdAt).toBe(latestVersionData.version.createdAt)
// When creating new version - updatedAt should match version.updatedAt
expect(fromNonVersionsTable.updatedAt).toBe(latestVersionData.version.updatedAt)
})
})
@@ -1265,6 +1300,48 @@ describe('Versions', () => {
expect(updatedGlobal._status).toStrictEqual('draft')
expect(globalLocalVersionID).toBeDefined()
})
it('should have different createdAt in a new version while the same version.createdAt', async () => {
const doc = await payload.updateGlobal({
slug: autoSaveGlobalSlug,
data: { title: 'asd' },
})
await wait(10)
const upd = await payload.updateGlobal({
slug: autoSaveGlobalSlug,
data: { title: 'asd2' },
})
expect(upd.createdAt).toBe(doc.createdAt)
const {
docs: [latestVersionData],
} = await payload.findGlobalVersions({
slug: autoSaveGlobalSlug,
where: {
latest: {
equals: true,
},
},
})
// Version itself should have new createdAt
expect(new Date(latestVersionData.createdAt) > new Date(doc.createdAt)).toBe(true)
// But the same version.createdAt!
expect(latestVersionData.version.createdAt).toBe(doc.createdAt)
const fromNonVersionsTable = await payload.findGlobal({
draft: false,
slug: autoSaveGlobalSlug,
})
// createdAt from non-versions should be the same as version_createdAt in versions
expect(fromNonVersionsTable.createdAt).toBe(latestVersionData.version.createdAt)
// When creating a new version - updatedAt should match
expect(fromNonVersionsTable.updatedAt).toBe(latestVersionData.version.updatedAt)
})
})
describe('Read', () => {