fix: virtual relationship fields with select (#12266)
Continuation of https://github.com/payloadcms/payload/pull/12265. Currently, using `select` on new relationship virtual fields: ``` const doc = await payload.findByID({ collection: 'virtual-relations', depth: 0, id, select: { postTitle: true }, }) ``` doesn't work, because in order to calculate `post.title`, the `post` field must be selected as well. This PR adds logic that sanitizes the incoming `select` to include those relationships into `select` (that are related to selected virtual fields) --------- Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
This commit is contained in:
@@ -247,6 +247,7 @@ export const createOperation = async <
|
|||||||
let doc
|
let doc
|
||||||
|
|
||||||
const select = sanitizeSelect({
|
const select = sanitizeSelect({
|
||||||
|
fields: collectionConfig.flattenedFields,
|
||||||
forceSelect: collectionConfig.forceSelect,
|
forceSelect: collectionConfig.forceSelect,
|
||||||
select: incomingSelect,
|
select: incomingSelect,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ export const deleteOperation = async <
|
|||||||
const fullWhere = combineQueries(where, accessResult)
|
const fullWhere = combineQueries(where, accessResult)
|
||||||
|
|
||||||
const select = sanitizeSelect({
|
const select = sanitizeSelect({
|
||||||
|
fields: collectionConfig.flattenedFields,
|
||||||
forceSelect: collectionConfig.forceSelect,
|
forceSelect: collectionConfig.forceSelect,
|
||||||
select: incomingSelect,
|
select: incomingSelect,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ export const deleteByIDOperation = async <TSlug extends CollectionSlug, TSelect
|
|||||||
}
|
}
|
||||||
|
|
||||||
const select = sanitizeSelect({
|
const select = sanitizeSelect({
|
||||||
|
fields: collectionConfig.flattenedFields,
|
||||||
forceSelect: collectionConfig.forceSelect,
|
forceSelect: collectionConfig.forceSelect,
|
||||||
select: incomingSelect,
|
select: incomingSelect,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ export const findOperation = async <
|
|||||||
} = args
|
} = args
|
||||||
|
|
||||||
const select = sanitizeSelect({
|
const select = sanitizeSelect({
|
||||||
|
fields: collectionConfig.flattenedFields,
|
||||||
forceSelect: collectionConfig.forceSelect,
|
forceSelect: collectionConfig.forceSelect,
|
||||||
select: incomingSelect,
|
select: incomingSelect,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ export const findByIDOperation = async <
|
|||||||
} = args
|
} = args
|
||||||
|
|
||||||
const select = sanitizeSelect({
|
const select = sanitizeSelect({
|
||||||
|
fields: collectionConfig.flattenedFields,
|
||||||
forceSelect: collectionConfig.forceSelect,
|
forceSelect: collectionConfig.forceSelect,
|
||||||
select: incomingSelect,
|
select: incomingSelect,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { APIError, Forbidden, NotFound } from '../../errors/index.js'
|
|||||||
import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
||||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||||
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
|
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
|
||||||
|
import { buildVersionCollectionFields } from '../../versions/buildCollectionFields.js'
|
||||||
import { getQueryDraftsSelect } from '../../versions/drafts/getQueryDraftsSelect.js'
|
import { getQueryDraftsSelect } from '../../versions/drafts/getQueryDraftsSelect.js'
|
||||||
|
|
||||||
export type Arguments = {
|
export type Arguments = {
|
||||||
@@ -70,8 +71,10 @@ export const findVersionByIDOperation = async <TData extends TypeWithID = any>(
|
|||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|
||||||
const select = sanitizeSelect({
|
const select = sanitizeSelect({
|
||||||
|
fields: buildVersionCollectionFields(payload.config, collectionConfig, true),
|
||||||
forceSelect: getQueryDraftsSelect({ select: collectionConfig.forceSelect }),
|
forceSelect: getQueryDraftsSelect({ select: collectionConfig.forceSelect }),
|
||||||
select: incomingSelect,
|
select: incomingSelect,
|
||||||
|
versions: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const versionsQuery = await payload.db.findVersions<TData>({
|
const versionsQuery = await payload.db.findVersions<TData>({
|
||||||
|
|||||||
@@ -72,8 +72,10 @@ export const findVersionsOperation = async <TData extends TypeWithVersion<TData>
|
|||||||
const fullWhere = combineQueries(where, accessResults)
|
const fullWhere = combineQueries(where, accessResults)
|
||||||
|
|
||||||
const select = sanitizeSelect({
|
const select = sanitizeSelect({
|
||||||
|
fields: buildVersionCollectionFields(payload.config, collectionConfig, true),
|
||||||
forceSelect: getQueryDraftsSelect({ select: collectionConfig.forceSelect }),
|
forceSelect: getQueryDraftsSelect({ select: collectionConfig.forceSelect }),
|
||||||
select: incomingSelect,
|
select: incomingSelect,
|
||||||
|
versions: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ export const restoreVersionOperation = async <TData extends TypeWithID = any>(
|
|||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|
||||||
const select = sanitizeSelect({
|
const select = sanitizeSelect({
|
||||||
|
fields: collectionConfig.flattenedFields,
|
||||||
forceSelect: collectionConfig.forceSelect,
|
forceSelect: collectionConfig.forceSelect,
|
||||||
select: incomingSelect,
|
select: incomingSelect,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -201,6 +201,7 @@ export const updateOperation = async <
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const select = sanitizeSelect({
|
const select = sanitizeSelect({
|
||||||
|
fields: collectionConfig.flattenedFields,
|
||||||
forceSelect: collectionConfig.forceSelect,
|
forceSelect: collectionConfig.forceSelect,
|
||||||
select: incomingSelect,
|
select: incomingSelect,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ export const updateByIDOperation = async <
|
|||||||
})
|
})
|
||||||
|
|
||||||
const select = sanitizeSelect({
|
const select = sanitizeSelect({
|
||||||
|
fields: collectionConfig.flattenedFields,
|
||||||
forceSelect: collectionConfig.forceSelect,
|
forceSelect: collectionConfig.forceSelect,
|
||||||
select: incomingSelect,
|
select: incomingSelect,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ export const findOneOperation = async <T extends Record<string, unknown>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const select = sanitizeSelect({
|
const select = sanitizeSelect({
|
||||||
|
fields: globalConfig.flattenedFields,
|
||||||
forceSelect: globalConfig.forceSelect,
|
forceSelect: globalConfig.forceSelect,
|
||||||
select: incomingSelect,
|
select: incomingSelect,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
|||||||
import { deepCopyObjectSimple } from '../../utilities/deepCopyObject.js'
|
import { deepCopyObjectSimple } from '../../utilities/deepCopyObject.js'
|
||||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||||
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
|
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
|
||||||
|
import { buildVersionCollectionFields } from '../../versions/buildCollectionFields.js'
|
||||||
|
import { buildVersionGlobalFields } from '../../versions/buildGlobalFields.js'
|
||||||
import { getQueryDraftsSelect } from '../../versions/drafts/getQueryDraftsSelect.js'
|
import { getQueryDraftsSelect } from '../../versions/drafts/getQueryDraftsSelect.js'
|
||||||
|
|
||||||
export type Arguments = {
|
export type Arguments = {
|
||||||
@@ -60,8 +62,10 @@ export const findVersionByIDOperation = async <T extends TypeWithVersion<T> = an
|
|||||||
const hasWhereAccess = typeof accessResults === 'object'
|
const hasWhereAccess = typeof accessResults === 'object'
|
||||||
|
|
||||||
const select = sanitizeSelect({
|
const select = sanitizeSelect({
|
||||||
|
fields: buildVersionGlobalFields(payload.config, globalConfig, true),
|
||||||
forceSelect: getQueryDraftsSelect({ select: globalConfig.forceSelect }),
|
forceSelect: getQueryDraftsSelect({ select: globalConfig.forceSelect }),
|
||||||
select: incomingSelect,
|
select: incomingSelect,
|
||||||
|
versions: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const findGlobalVersionsArgs: FindGlobalVersionsArgs = {
|
const findGlobalVersionsArgs: FindGlobalVersionsArgs = {
|
||||||
|
|||||||
@@ -70,8 +70,10 @@ export const findVersionsOperation = async <T extends TypeWithVersion<T>>(
|
|||||||
const fullWhere = combineQueries(where, accessResults)
|
const fullWhere = combineQueries(where, accessResults)
|
||||||
|
|
||||||
const select = sanitizeSelect({
|
const select = sanitizeSelect({
|
||||||
|
fields: buildVersionGlobalFields(payload.config, globalConfig, true),
|
||||||
forceSelect: getQueryDraftsSelect({ select: globalConfig.forceSelect }),
|
forceSelect: getQueryDraftsSelect({ select: globalConfig.forceSelect }),
|
||||||
select: incomingSelect,
|
select: incomingSelect,
|
||||||
|
versions: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|||||||
@@ -246,6 +246,7 @@ export const updateOperation = async <
|
|||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|
||||||
const select = sanitizeSelect({
|
const select = sanitizeSelect({
|
||||||
|
fields: globalConfig.flattenedFields,
|
||||||
forceSelect: globalConfig.forceSelect,
|
forceSelect: globalConfig.forceSelect,
|
||||||
select: incomingSelect,
|
select: incomingSelect,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,17 +1,129 @@
|
|||||||
import { deepMergeSimple } from '@payloadcms/translations/utilities'
|
import { deepMergeSimple } from '@payloadcms/translations/utilities'
|
||||||
|
|
||||||
import type { SelectType } from '../types/index.js'
|
import type { FlattenedField } from '../fields/config/types.js'
|
||||||
|
import type { SelectIncludeType, SelectType } from '../types/index.js'
|
||||||
|
|
||||||
import { getSelectMode } from './getSelectMode.js'
|
import { getSelectMode } from './getSelectMode.js'
|
||||||
|
|
||||||
|
// Transform post.title -> post, post.category.title -> post
|
||||||
|
const stripVirtualPathToCurrentCollection = ({
|
||||||
|
fields,
|
||||||
|
path,
|
||||||
|
versions,
|
||||||
|
}: {
|
||||||
|
fields: FlattenedField[]
|
||||||
|
path: string
|
||||||
|
versions: boolean
|
||||||
|
}) => {
|
||||||
|
const resultSegments: string[] = []
|
||||||
|
|
||||||
|
if (versions) {
|
||||||
|
resultSegments.push('version')
|
||||||
|
const versionField = fields.find((each) => each.name === 'version')
|
||||||
|
|
||||||
|
if (versionField && versionField.type === 'group') {
|
||||||
|
fields = versionField.flattenedFields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const segment of path.split('.')) {
|
||||||
|
const field = fields.find((each) => each.name === segment)
|
||||||
|
|
||||||
|
if (!field) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
resultSegments.push(segment)
|
||||||
|
|
||||||
|
if (field.type === 'relationship' || field.type === 'upload') {
|
||||||
|
return resultSegments.join('.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultSegments.join('.')
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAllVirtualRelations = ({ fields }: { fields: FlattenedField[] }) => {
|
||||||
|
const result: string[] = []
|
||||||
|
|
||||||
|
for (const field of fields) {
|
||||||
|
if ('virtual' in field && typeof field.virtual === 'string') {
|
||||||
|
result.push(field.virtual)
|
||||||
|
} else if (field.type === 'group' || field.type === 'tab') {
|
||||||
|
const nestedResult = getAllVirtualRelations({ fields: field.flattenedFields })
|
||||||
|
|
||||||
|
for (const nestedItem of nestedResult) {
|
||||||
|
result.push(nestedItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolveVirtualRelationsToSelect = ({
|
||||||
|
fields,
|
||||||
|
selectValue,
|
||||||
|
topLevelFields,
|
||||||
|
versions,
|
||||||
|
}: {
|
||||||
|
fields: FlattenedField[]
|
||||||
|
selectValue: SelectIncludeType | true
|
||||||
|
topLevelFields: FlattenedField[]
|
||||||
|
versions: boolean
|
||||||
|
}) => {
|
||||||
|
const result: string[] = []
|
||||||
|
if (selectValue === true) {
|
||||||
|
for (const item of getAllVirtualRelations({ fields })) {
|
||||||
|
result.push(
|
||||||
|
stripVirtualPathToCurrentCollection({ fields: topLevelFields, path: item, versions }),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const fieldName in selectValue) {
|
||||||
|
const field = fields.find((each) => each.name === fieldName)
|
||||||
|
if (!field) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('virtual' in field && typeof field.virtual === 'string') {
|
||||||
|
result.push(
|
||||||
|
stripVirtualPathToCurrentCollection({
|
||||||
|
fields: topLevelFields,
|
||||||
|
path: field.virtual,
|
||||||
|
versions,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
} else if (field.type === 'group' || field.type === 'tab') {
|
||||||
|
for (const item of resolveVirtualRelationsToSelect({
|
||||||
|
fields: field.flattenedFields,
|
||||||
|
selectValue: selectValue[fieldName],
|
||||||
|
topLevelFields,
|
||||||
|
versions,
|
||||||
|
})) {
|
||||||
|
result.push(
|
||||||
|
stripVirtualPathToCurrentCollection({ fields: topLevelFields, path: item, versions }),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
export const sanitizeSelect = ({
|
export const sanitizeSelect = ({
|
||||||
|
fields,
|
||||||
forceSelect,
|
forceSelect,
|
||||||
select,
|
select,
|
||||||
|
versions,
|
||||||
}: {
|
}: {
|
||||||
|
fields: FlattenedField[]
|
||||||
forceSelect?: SelectType
|
forceSelect?: SelectType
|
||||||
select?: SelectType
|
select?: SelectType
|
||||||
|
versions?: boolean
|
||||||
}): SelectType | undefined => {
|
}): SelectType | undefined => {
|
||||||
if (!forceSelect || !select) {
|
if (!select) {
|
||||||
return select
|
return select
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,5 +133,36 @@ export const sanitizeSelect = ({
|
|||||||
return select
|
return select
|
||||||
}
|
}
|
||||||
|
|
||||||
return deepMergeSimple(select, forceSelect)
|
if (forceSelect) {
|
||||||
|
select = deepMergeSimple(select, forceSelect)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (select) {
|
||||||
|
const virtualRelations = resolveVirtualRelationsToSelect({
|
||||||
|
fields,
|
||||||
|
selectValue: select as SelectIncludeType,
|
||||||
|
topLevelFields: fields,
|
||||||
|
versions: versions ?? false,
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const path of virtualRelations) {
|
||||||
|
let currentRef = select
|
||||||
|
const segments = path.split('.')
|
||||||
|
for (let i = 0; i < segments.length; i++) {
|
||||||
|
const isLast = segments.length - 1 === i
|
||||||
|
const segment = segments[i]
|
||||||
|
|
||||||
|
if (isLast) {
|
||||||
|
currentRef[segment] = true
|
||||||
|
} else {
|
||||||
|
if (!(segment in currentRef)) {
|
||||||
|
currentRef[segment] = {}
|
||||||
|
currentRef = currentRef[segment]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return select
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1997,6 +1997,23 @@ describe('database', () => {
|
|||||||
expect(draft.docs[0]?.postTitle).toBe('my-title')
|
expect(draft.docs[0]?.postTitle).toBe('my-title')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not break when using select', async () => {
|
||||||
|
const post = await payload.create({ collection: 'posts', data: { title: 'my-title-10' } })
|
||||||
|
const { id } = await payload.create({
|
||||||
|
collection: 'virtual-relations',
|
||||||
|
depth: 0,
|
||||||
|
data: { post: post.id },
|
||||||
|
})
|
||||||
|
|
||||||
|
const doc = await payload.findByID({
|
||||||
|
collection: 'virtual-relations',
|
||||||
|
depth: 0,
|
||||||
|
id,
|
||||||
|
select: { postTitle: true },
|
||||||
|
})
|
||||||
|
expect(doc.postTitle).toBe('my-title-10')
|
||||||
|
})
|
||||||
|
|
||||||
it('should allow virtual field as reference to ID', async () => {
|
it('should allow virtual field as reference to ID', async () => {
|
||||||
const post = await payload.create({ collection: 'posts', data: { title: 'my-title' } })
|
const post = await payload.create({ collection: 'posts', data: { title: 'my-title' } })
|
||||||
const { id } = await payload.create({
|
const { id } = await payload.create({
|
||||||
@@ -2129,6 +2146,26 @@ describe('database', () => {
|
|||||||
expect(doc.postCategoryTitle).toBe('1-category')
|
expect(doc.postCategoryTitle).toBe('1-category')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not break when using select 2x deep', async () => {
|
||||||
|
const category = await payload.create({
|
||||||
|
collection: 'categories',
|
||||||
|
data: { title: '3-category' },
|
||||||
|
})
|
||||||
|
const post = await payload.create({
|
||||||
|
collection: 'posts',
|
||||||
|
data: { title: '3-post', category: category.id },
|
||||||
|
})
|
||||||
|
const doc = await payload.create({ collection: 'virtual-relations', data: { post: post.id } })
|
||||||
|
|
||||||
|
const docWithSelect = await payload.findByID({
|
||||||
|
collection: 'virtual-relations',
|
||||||
|
depth: 0,
|
||||||
|
id: doc.id,
|
||||||
|
select: { postCategoryTitle: true },
|
||||||
|
})
|
||||||
|
expect(docWithSelect.postCategoryTitle).toBe('3-category')
|
||||||
|
})
|
||||||
|
|
||||||
it('should allow to query by virtual field 2x deep', async () => {
|
it('should allow to query by virtual field 2x deep', async () => {
|
||||||
const category = await payload.create({
|
const category = await payload.create({
|
||||||
collection: 'categories',
|
collection: 'categories',
|
||||||
|
|||||||
@@ -98,6 +98,19 @@ export const Pages: CollectionConfig = {
|
|||||||
type: 'relationship',
|
type: 'relationship',
|
||||||
relationTo: 'users',
|
relationTo: 'users',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'virtualRelationship',
|
||||||
|
type: 'text',
|
||||||
|
virtual: 'author.name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'virtual',
|
||||||
|
type: 'text',
|
||||||
|
virtual: true,
|
||||||
|
hooks: {
|
||||||
|
afterRead: [() => 'virtual value'],
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'hasManyNumber',
|
name: 'hasManyNumber',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ export const Users: CollectionConfig = {
|
|||||||
read: () => true,
|
read: () => true,
|
||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
// Email added by default
|
// Email added by default
|
||||||
// Add more fields as needed
|
// Add more fields as needed
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -255,6 +255,35 @@ describe('@payloadcms/plugin-import-export', () => {
|
|||||||
expect(str.indexOf('createdAt')).toBeLessThan(str.indexOf('updatedAt'))
|
expect(str.indexOf('createdAt')).toBeLessThan(str.indexOf('updatedAt'))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should create a CSV file with virtual fields', async () => {
|
||||||
|
const fields = ['id', 'virtual', 'virtualRelationship']
|
||||||
|
const doc = await payload.create({
|
||||||
|
collection: 'exports',
|
||||||
|
user,
|
||||||
|
data: {
|
||||||
|
collectionSlug: 'pages',
|
||||||
|
fields,
|
||||||
|
format: 'csv',
|
||||||
|
where: {
|
||||||
|
title: { contains: 'Virtual ' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const exportDoc = await payload.findByID({
|
||||||
|
collection: 'exports',
|
||||||
|
id: doc.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(exportDoc.filename).toBeDefined()
|
||||||
|
const expectedPath = path.join(dirname, './uploads', exportDoc.filename as string)
|
||||||
|
const data = await readCSV(expectedPath)
|
||||||
|
|
||||||
|
// Assert that the csv file contains the expected virtual fields
|
||||||
|
expect(data[0].virtual).toStrictEqual('virtual value')
|
||||||
|
expect(data[0].virtualRelationship).toStrictEqual('name value')
|
||||||
|
})
|
||||||
|
|
||||||
it('should create a file for collection csv from array.subfield', async () => {
|
it('should create a file for collection csv from array.subfield', async () => {
|
||||||
let doc = await payload.create({
|
let doc = await payload.create({
|
||||||
collection: 'exports',
|
collection: 'exports',
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ export interface UserAuthOperations {
|
|||||||
*/
|
*/
|
||||||
export interface User {
|
export interface User {
|
||||||
id: string;
|
id: string;
|
||||||
|
name?: string | null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
email: string;
|
email: string;
|
||||||
@@ -199,6 +200,8 @@ export interface Page {
|
|||||||
)[]
|
)[]
|
||||||
| null;
|
| null;
|
||||||
author?: (string | null) | User;
|
author?: (string | null) | User;
|
||||||
|
virtualRelationship?: string | null;
|
||||||
|
virtual?: string | null;
|
||||||
hasManyNumber?: number[] | null;
|
hasManyNumber?: number[] | null;
|
||||||
relationship?: (string | null) | User;
|
relationship?: (string | null) | User;
|
||||||
excerpt?: string | null;
|
excerpt?: string | null;
|
||||||
@@ -444,6 +447,7 @@ export interface PayloadMigration {
|
|||||||
* via the `definition` "users_select".
|
* via the `definition` "users_select".
|
||||||
*/
|
*/
|
||||||
export interface UsersSelect<T extends boolean = true> {
|
export interface UsersSelect<T extends boolean = true> {
|
||||||
|
name?: T;
|
||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
email?: T;
|
email?: T;
|
||||||
@@ -500,6 +504,8 @@ export interface PagesSelect<T extends boolean = true> {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
author?: T;
|
author?: T;
|
||||||
|
virtualRelationship?: T;
|
||||||
|
virtual?: T;
|
||||||
hasManyNumber?: T;
|
hasManyNumber?: T;
|
||||||
relationship?: T;
|
relationship?: T;
|
||||||
excerpt?: T;
|
excerpt?: T;
|
||||||
|
|||||||
@@ -6,11 +6,12 @@ import { richTextData } from './richTextData.js'
|
|||||||
export const seed = async (payload: Payload): Promise<boolean> => {
|
export const seed = async (payload: Payload): Promise<boolean> => {
|
||||||
payload.logger.info('Seeding data...')
|
payload.logger.info('Seeding data...')
|
||||||
try {
|
try {
|
||||||
await payload.create({
|
const user = await payload.create({
|
||||||
collection: 'users',
|
collection: 'users',
|
||||||
data: {
|
data: {
|
||||||
email: devUser.email,
|
email: devUser.email,
|
||||||
password: devUser.password,
|
password: devUser.password,
|
||||||
|
name: 'name value',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
// create pages
|
// create pages
|
||||||
@@ -80,6 +81,16 @@ export const seed = async (payload: Payload): Promise<boolean> => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
await payload.create({
|
||||||
|
collection: 'pages',
|
||||||
|
data: {
|
||||||
|
author: user.id,
|
||||||
|
title: `Virtual ${i}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
await payload.create({
|
await payload.create({
|
||||||
collection: 'pages',
|
collection: 'pages',
|
||||||
|
|||||||
Reference in New Issue
Block a user