### What? Upgrades mongoose from 6 to latest `v8.8.1` Fixes https://github.com/payloadcms/payload/issues/9171 ### Why? Compatibilty with Mongodb Atlas ### How? - Updates deps - Changed ObjectId from bson-objectid to use `new Type.ObjectId` from mongoose for compatibility (only inside of db-mongodb) - Internal type adjustments https://github.com/payloadcms/payload/discussions/9088 BREAKING CHANGES: All projects with existing data having versions enabled, or relationship or upload fields will want to create the predefined migration that converts all strings to ObjectIDs where needed. This can be created using `payload migrate:create --file @payloadcms/mongodb/relationships-v2-v3`. For projects making use of the exposed Models from mongoose, review the upgrade guides from [v6 to v7](https://mongoosejs.com/docs/7.x/docs/migrating_to_7.html) and [v7 to v8](https://mongoosejs.com/docs/migrating_to_8.html) and make adjustments as needed. --------- Co-authored-by: Sasha <64744993+r1tsuu@users.noreply.github.com>
345 lines
7.3 KiB
TypeScript
345 lines
7.3 KiB
TypeScript
import type { Field, SanitizedConfig } from 'payload'
|
|
|
|
import { Types } from 'mongoose'
|
|
|
|
import { sanitizeRelationshipIDs } from './sanitizeRelationshipIDs.js'
|
|
|
|
const flattenRelationshipValues = (obj: Record<string, any>, prefix = ''): Record<string, any> => {
|
|
return Object.keys(obj).reduce(
|
|
(acc, key) => {
|
|
const fullKey = prefix ? `${prefix}.${key}` : key
|
|
const value = obj[key]
|
|
|
|
if (value && typeof value === 'object' && !(value instanceof Types.ObjectId)) {
|
|
Object.assign(acc, flattenRelationshipValues(value, fullKey))
|
|
// skip relationTo and blockType
|
|
} else if (!fullKey.endsWith('relationTo') && !fullKey.endsWith('blockType')) {
|
|
acc[fullKey] = value
|
|
}
|
|
|
|
return acc
|
|
},
|
|
{} as Record<string, any>,
|
|
)
|
|
}
|
|
|
|
const relsFields: Field[] = [
|
|
{
|
|
name: 'rel_1',
|
|
type: 'relationship',
|
|
relationTo: 'rels',
|
|
},
|
|
{
|
|
name: 'rel_1_l',
|
|
type: 'relationship',
|
|
localized: true,
|
|
relationTo: 'rels',
|
|
},
|
|
{
|
|
name: 'rel_2',
|
|
type: 'relationship',
|
|
hasMany: true,
|
|
relationTo: 'rels',
|
|
},
|
|
{
|
|
name: 'rel_2_l',
|
|
type: 'relationship',
|
|
hasMany: true,
|
|
localized: true,
|
|
relationTo: 'rels',
|
|
},
|
|
{
|
|
name: 'rel_3',
|
|
type: 'relationship',
|
|
relationTo: ['rels'],
|
|
},
|
|
{
|
|
name: 'rel_3_l',
|
|
type: 'relationship',
|
|
localized: true,
|
|
relationTo: ['rels'],
|
|
},
|
|
{
|
|
name: 'rel_4',
|
|
type: 'relationship',
|
|
hasMany: true,
|
|
relationTo: ['rels'],
|
|
},
|
|
{
|
|
name: 'rel_4_l',
|
|
type: 'relationship',
|
|
hasMany: true,
|
|
localized: true,
|
|
relationTo: ['rels'],
|
|
},
|
|
]
|
|
|
|
const config = {
|
|
collections: [
|
|
{
|
|
slug: 'docs',
|
|
fields: [
|
|
...relsFields,
|
|
{
|
|
name: 'array',
|
|
type: 'array',
|
|
fields: [
|
|
{
|
|
name: 'array',
|
|
type: 'array',
|
|
fields: relsFields,
|
|
},
|
|
{
|
|
name: 'blocks',
|
|
type: 'blocks',
|
|
blocks: [{ slug: 'block', fields: relsFields }],
|
|
},
|
|
...relsFields,
|
|
],
|
|
},
|
|
{
|
|
name: 'arrayLocalized',
|
|
type: 'array',
|
|
fields: [
|
|
{
|
|
name: 'array',
|
|
type: 'array',
|
|
fields: relsFields,
|
|
},
|
|
{
|
|
name: 'blocks',
|
|
type: 'blocks',
|
|
blocks: [{ slug: 'block', fields: relsFields }],
|
|
},
|
|
...relsFields,
|
|
],
|
|
localized: true,
|
|
},
|
|
{
|
|
name: 'blocks',
|
|
type: 'blocks',
|
|
blocks: [
|
|
{
|
|
slug: 'block',
|
|
fields: [
|
|
...relsFields,
|
|
{
|
|
name: 'group',
|
|
type: 'group',
|
|
fields: relsFields,
|
|
},
|
|
{
|
|
name: 'array',
|
|
type: 'array',
|
|
fields: relsFields,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: 'group',
|
|
type: 'group',
|
|
fields: [
|
|
...relsFields,
|
|
{
|
|
name: 'array',
|
|
type: 'array',
|
|
fields: relsFields,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: 'groupLocalized',
|
|
type: 'group',
|
|
fields: [
|
|
...relsFields,
|
|
{
|
|
name: 'array',
|
|
type: 'array',
|
|
fields: relsFields,
|
|
},
|
|
],
|
|
localized: true,
|
|
},
|
|
{
|
|
name: 'groupAndRow',
|
|
type: 'group',
|
|
fields: [
|
|
{
|
|
type: 'row',
|
|
fields: [
|
|
...relsFields,
|
|
{
|
|
type: 'array',
|
|
name: 'array',
|
|
fields: relsFields,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
type: 'tabs',
|
|
tabs: [
|
|
{
|
|
name: 'tab',
|
|
fields: relsFields,
|
|
},
|
|
{
|
|
name: 'tabLocalized',
|
|
fields: relsFields,
|
|
localized: true,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
slug: 'rels',
|
|
fields: [],
|
|
},
|
|
],
|
|
localization: {
|
|
defaultLocale: 'en',
|
|
localeCodes: ['en', 'es'],
|
|
locales: [
|
|
{ code: 'en', label: 'EN' },
|
|
{ code: 'es', label: 'ES' },
|
|
],
|
|
},
|
|
} as SanitizedConfig
|
|
|
|
const relsData = {
|
|
rel_1: new Types.ObjectId().toHexString(),
|
|
rel_1_l: {
|
|
en: new Types.ObjectId().toHexString(),
|
|
es: new Types.ObjectId().toHexString(),
|
|
},
|
|
rel_2: [new Types.ObjectId().toHexString()],
|
|
rel_2_l: {
|
|
en: [new Types.ObjectId().toHexString()],
|
|
es: [new Types.ObjectId().toHexString()],
|
|
},
|
|
rel_3: {
|
|
relationTo: 'rels',
|
|
value: new Types.ObjectId().toHexString(),
|
|
},
|
|
rel_3_l: {
|
|
en: {
|
|
relationTo: 'rels',
|
|
value: new Types.ObjectId().toHexString(),
|
|
},
|
|
es: {
|
|
relationTo: 'rels',
|
|
value: new Types.ObjectId().toHexString(),
|
|
},
|
|
},
|
|
rel_4: [
|
|
{
|
|
relationTo: 'rels',
|
|
value: new Types.ObjectId().toHexString(),
|
|
},
|
|
],
|
|
rel_4_l: {
|
|
en: [
|
|
{
|
|
relationTo: 'rels',
|
|
value: new Types.ObjectId().toHexString(),
|
|
},
|
|
],
|
|
es: [
|
|
{
|
|
relationTo: 'rels',
|
|
value: new Types.ObjectId().toHexString(),
|
|
},
|
|
],
|
|
},
|
|
}
|
|
|
|
describe('sanitizeRelationshipIDs', () => {
|
|
it('should sanitize relationships', () => {
|
|
const data = {
|
|
...relsData,
|
|
array: [
|
|
{
|
|
...relsData,
|
|
array: [{ ...relsData }],
|
|
blocks: [
|
|
{
|
|
blockType: 'block',
|
|
...relsData,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
arrayLocalized: {
|
|
en: [
|
|
{
|
|
...relsData,
|
|
array: [{ ...relsData }],
|
|
blocks: [
|
|
{
|
|
blockType: 'block',
|
|
...relsData,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
es: [
|
|
{
|
|
...relsData,
|
|
array: [{ ...relsData }],
|
|
blocks: [
|
|
{
|
|
blockType: 'block',
|
|
...relsData,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
blocks: [
|
|
{
|
|
blockType: 'block',
|
|
...relsData,
|
|
array: [{ ...relsData }],
|
|
group: { ...relsData },
|
|
},
|
|
],
|
|
group: {
|
|
...relsData,
|
|
array: [{ ...relsData }],
|
|
},
|
|
groupAndRow: {
|
|
...relsData,
|
|
array: [{ ...relsData }],
|
|
},
|
|
groupLocalized: {
|
|
en: {
|
|
...relsData,
|
|
array: [{ ...relsData }],
|
|
},
|
|
es: {
|
|
...relsData,
|
|
array: [{ ...relsData }],
|
|
},
|
|
},
|
|
tab: { ...relsData },
|
|
tabLocalized: {
|
|
en: { ...relsData },
|
|
es: { ...relsData },
|
|
},
|
|
}
|
|
const flattenValuesBefore = Object.values(flattenRelationshipValues(data))
|
|
|
|
sanitizeRelationshipIDs({ config, data, fields: config.collections[0].fields })
|
|
const flattenValuesAfter = Object.values(flattenRelationshipValues(data))
|
|
|
|
flattenValuesAfter.forEach((value, i) => {
|
|
expect(value).toBeInstanceOf(Types.ObjectId)
|
|
expect(flattenValuesBefore[i]).toBe(value.toHexString())
|
|
})
|
|
})
|
|
})
|