Files
payload/test/select/int.spec.ts
Sasha dae832c288 feat: select fields (#8550)
Adds `select` which is used to specify the field projection for local
and rest API calls. This is available as an optimization to reduce the
payload's of requests and make the database queries more efficient.

Includes:
- [x] generate types for the `select` property
- [x] infer the return type by `select` with 2 modes - include (`field:
true`) and exclude (`field: false`)
- [x] lots of integration tests, including deep fields / localization
etc
- [x] implement the property in db adapters
- [x] implement the property in the local api for most operations
- [x] implement the property in the rest api 
- [x] docs

---------

Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
2024-10-29 21:47:18 +00:00

1678 lines
40 KiB
TypeScript

import path from 'path'
import { deepCopyObject, type Payload } from 'payload'
import { fileURLToPath } from 'url'
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
import type {
Config,
DeepPost,
GlobalPost,
LocalizedPost,
Post,
VersionedPost,
} from './payload-types.js'
import { initPayloadInt } from '../helpers/initPayloadInt.js'
let payload: Payload
let restClient: NextRESTClient
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
describe('Select', () => {
// --__--__--__--__--__--__--__--__--__
// Boilerplate test setup/teardown
// --__--__--__--__--__--__--__--__--__
beforeAll(async () => {
const initialized = await initPayloadInt(dirname)
;({ payload, restClient } = initialized)
})
afterAll(async () => {
if (typeof payload.db.destroy === 'function') {
await payload.db.destroy()
}
})
describe('Local API - Base', () => {
let post: Post
let postId: number | string
beforeEach(async () => {
post = await createPost()
postId = post.id
})
// Clean up to safely mutate in each test
afterEach(async () => {
await payload.delete({ id: postId, collection: 'posts' })
})
describe('Include mode', () => {
it('should select only id as default', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {},
})
expect(res).toStrictEqual({
id: postId,
})
})
it('should select only number', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
number: true,
},
})
expect(res).toStrictEqual({
id: postId,
number: post.number,
})
})
it('should select number and text', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
number: true,
text: true,
},
})
expect(res).toStrictEqual({
id: postId,
number: post.number,
text: post.text,
})
})
it('should select all the fields inside of group', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
group: true,
},
})
expect(res).toStrictEqual({
id: postId,
group: post.group,
})
})
it('should select text field inside of group', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
group: {
text: true,
},
},
})
expect(res).toStrictEqual({
id: postId,
group: {
text: post.group.text,
},
})
})
it('should select id as default from array', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
array: {},
},
})
expect(res).toStrictEqual({
id: postId,
array: post.array.map((item) => ({ id: item.id })),
})
})
it('should select all the fields inside of array', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
array: true,
},
})
expect(res).toStrictEqual({
id: postId,
array: post.array,
})
})
it('should select text field inside of array', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
array: {
text: true,
},
},
})
expect(res).toStrictEqual({
id: postId,
array: post.array.map((item) => ({
id: item.id,
text: item.text,
})),
})
})
it('should select base fields (id, blockType) inside of blocks', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
blocks: {},
},
})
expect(res).toStrictEqual({
id: postId,
blocks: post.blocks.map((block) => ({ blockType: block.blockType, id: block.id })),
})
})
it('should select all the fields inside of blocks', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
blocks: true,
},
})
expect(res).toStrictEqual({
id: postId,
blocks: post.blocks,
})
})
it('should select all the fields inside of specific block', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
blocks: {
cta: true,
},
},
})
expect(res).toStrictEqual({
id: postId,
blocks: post.blocks.map((block) =>
// eslint-disable-next-line jest/no-conditional-in-test
block.blockType === 'cta'
? block
: {
id: block.id,
blockType: block.blockType,
},
),
})
})
it('should select a specific field inside of specific block', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
blocks: {
cta: { ctaText: true },
},
},
})
expect(res).toStrictEqual({
id: postId,
blocks: post.blocks.map((block) =>
// eslint-disable-next-line jest/no-conditional-in-test
block.blockType === 'cta'
? { id: block.id, blockType: block.blockType, ctaText: block.ctaText }
: {
id: block.id,
blockType: block.blockType,
},
),
})
})
})
describe('Exclude mode', () => {
it('should exclude only text field', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
text: false,
},
})
const expected = { ...post }
delete expected['text']
expect(res).toStrictEqual(expected)
})
it('should exclude number', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
number: false,
},
})
const expected = { ...post }
delete expected['number']
expect(res).toStrictEqual(expected)
})
it('should exclude number and text', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
number: false,
text: false,
},
})
const expected = { ...post }
delete expected['text']
delete expected['number']
expect(res).toStrictEqual(expected)
})
it('should exclude group', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
group: false,
},
})
const expected = { ...post }
delete expected['group']
expect(res).toStrictEqual(expected)
})
it('should exclude text field inside of group', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
group: {
text: false,
},
},
})
const expected = deepCopyObject(post)
delete expected.group.text
expect(res).toStrictEqual(expected)
})
it('should exclude array', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
array: false,
},
})
const expected = { ...post }
delete expected['array']
expect(res).toStrictEqual(expected)
})
it('should exclude text field inside of array', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
array: {
text: false,
},
},
})
expect(res).toStrictEqual({
...post,
array: post.array.map((item) => ({
id: item.id,
number: item.number,
})),
})
})
it('should exclude blocks', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
blocks: false,
},
})
const expected = { ...post }
delete expected['blocks']
expect(res).toStrictEqual(expected)
})
it('should exclude all the fields inside of specific block while keeping base fields', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
blocks: {
cta: false,
},
},
})
expect(res).toStrictEqual({
...post,
blocks: post.blocks.map((block) =>
// eslint-disable-next-line jest/no-conditional-in-test
block.blockType === 'cta' ? { id: block.id, blockType: block.blockType } : block,
),
})
})
it('should exclude a specific field inside of specific block', async () => {
const res = await payload.findByID({
collection: 'posts',
id: postId,
select: {
blocks: {
cta: { ctaText: false },
},
},
})
expect(res).toStrictEqual({
...post,
blocks: post.blocks.map((block) => {
delete block['ctaText']
return block
}),
})
})
})
})
describe('Local API - Localization', () => {
let post: LocalizedPost
let postId: number | string
beforeEach(async () => {
post = await createLocalizedPost()
postId = post.id
})
// Clean up to safely mutate in each test
afterEach(async () => {
await payload.delete({ id: postId, collection: 'localized-posts' })
})
describe('Include mode', () => {
it('should select only id as default', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {},
})
expect(res).toStrictEqual({
id: postId,
})
})
it('should select only number', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
number: true,
},
})
expect(res).toStrictEqual({
id: postId,
number: post.number,
})
})
it('should select number and text', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
number: true,
text: true,
},
})
expect(res).toStrictEqual({
id: postId,
number: post.number,
text: post.text,
})
})
it('should select all the fields inside of group', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
group: true,
},
})
expect(res).toStrictEqual({
id: postId,
group: post.group,
})
})
it('should select text field inside of group', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
group: {
text: true,
},
},
})
expect(res).toStrictEqual({
id: postId,
group: {
text: post.group.text,
},
})
})
it('should select localized text field inside of group', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
groupSecond: {
text: true,
},
},
})
expect(res).toStrictEqual({
id: postId,
groupSecond: {
text: post.groupSecond.text,
},
})
})
it('should select id as default from array', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
array: {},
},
})
expect(res).toStrictEqual({
id: postId,
array: post.array.map((item) => ({ id: item.id })),
})
})
it('should select all the fields inside of array', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
array: true,
},
})
expect(res).toStrictEqual({
id: postId,
array: post.array,
})
})
it('should select text field inside of localized array', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
array: {
text: true,
},
},
})
expect(res).toStrictEqual({
id: postId,
array: post.array.map((item) => ({
id: item.id,
text: item.text,
})),
})
})
it('should select localized text field inside of array', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
arraySecond: {
text: true,
},
},
})
expect(res).toStrictEqual({
id: postId,
arraySecond: post.arraySecond.map((item) => ({
id: item.id,
text: item.text,
})),
})
})
it('should select base fields (id, blockType) inside of blocks', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
blocks: {},
},
})
expect(res).toStrictEqual({
id: postId,
blocks: post.blocks.map((block) => ({ blockType: block.blockType, id: block.id })),
})
})
it('should select all the fields inside of blocks', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
blocks: true,
},
})
expect(res).toStrictEqual({
id: postId,
blocks: post.blocks,
})
})
it('should select all the fields inside of specific block', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
blocks: {
cta: true,
},
},
})
expect(res).toStrictEqual({
id: postId,
blocks: post.blocks.map((block) =>
// eslint-disable-next-line jest/no-conditional-in-test
block.blockType === 'cta'
? block
: {
id: block.id,
blockType: block.blockType,
},
),
})
})
it('should select a specific field inside of specific block', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
blocks: {
cta: { ctaText: true },
},
},
})
expect(res).toStrictEqual({
id: postId,
blocks: post.blocks.map((block) =>
// eslint-disable-next-line jest/no-conditional-in-test
block.blockType === 'cta'
? { id: block.id, blockType: block.blockType, ctaText: block.ctaText }
: {
id: block.id,
blockType: block.blockType,
},
),
})
})
it('should select a specific localized field inside of specific block 1', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
blocksSecond: {
second: { text: true },
},
},
})
expect(res).toStrictEqual({
id: postId,
blocksSecond: post.blocksSecond.map((block) =>
// eslint-disable-next-line jest/no-conditional-in-test
block.blockType === 'second'
? { id: block.id, blockType: block.blockType, text: block.text }
: {
id: block.id,
blockType: block.blockType,
},
),
})
})
it('should select a specific localized field inside of specific block 2', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
blocksSecond: {
first: { firstText: true },
},
},
})
expect(res).toStrictEqual({
id: postId,
blocksSecond: post.blocksSecond.map((block) =>
// eslint-disable-next-line jest/no-conditional-in-test
block.blockType === 'first'
? { id: block.id, blockType: block.blockType, firstText: block.firstText }
: {
id: block.id,
blockType: block.blockType,
},
),
})
})
})
describe('Exclude mode', () => {
it('should exclude only text field', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
text: false,
},
})
const expected = { ...post }
delete expected['text']
expect(res).toStrictEqual(expected)
})
it('should exclude number', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
number: false,
},
})
const expected = { ...post }
delete expected['number']
expect(res).toStrictEqual(expected)
})
it('should exclude number and text', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
number: false,
text: false,
},
})
const expected = { ...post }
delete expected['text']
delete expected['number']
expect(res).toStrictEqual(expected)
})
it('should exclude group', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
group: false,
},
})
const expected = { ...post }
delete expected['group']
expect(res).toStrictEqual(expected)
})
it('should exclude text field inside of group', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
group: {
text: false,
},
},
})
const expected = deepCopyObject(post)
delete expected.group.text
expect(res).toStrictEqual(expected)
})
it('should exclude localized text field inside of group', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
groupSecond: {
text: false,
},
},
})
const expected = deepCopyObject(post)
delete expected.groupSecond.text
expect(res).toStrictEqual(expected)
})
it('should exclude array', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
array: false,
},
})
const expected = { ...post }
delete expected['array']
expect(res).toStrictEqual(expected)
})
it('should exclude text field inside of array', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
array: {
text: false,
},
},
})
expect(res).toStrictEqual({
...post,
array: post.array.map((item) => ({
id: item.id,
number: item.number,
})),
})
})
it('should exclude localized text field inside of array', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
arraySecond: {
text: false,
},
},
})
expect(res).toStrictEqual({
...post,
arraySecond: post.arraySecond.map((item) => ({
id: item.id,
number: item.number,
})),
})
})
it('should exclude blocks', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
blocks: false,
},
})
const expected = { ...post }
delete expected['blocks']
expect(res).toStrictEqual(expected)
})
it('should exclude all the fields inside of specific block while keeping base fields', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
blocks: {
cta: false,
},
},
})
expect(res).toStrictEqual({
...post,
blocks: post.blocks.map((block) =>
// eslint-disable-next-line jest/no-conditional-in-test
block.blockType === 'cta' ? { id: block.id, blockType: block.blockType } : block,
),
})
})
it('should exclude a specific field inside of specific block', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
blocks: {
cta: { ctaText: false },
},
},
})
expect(res).toStrictEqual({
...post,
blocks: post.blocks.map((block) => {
delete block['ctaText']
return block
}),
})
})
it('should exclude a specific localized field inside of specific block 1', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
blocksSecond: {
second: { text: false },
},
},
})
expect(res).toStrictEqual({
...post,
blocksSecond: post.blocksSecond.map((block) => {
// eslint-disable-next-line jest/no-conditional-in-test
if (block.blockType === 'second') {
delete block['text']
}
return block
}),
})
})
it('should exclude a specific localized field inside of specific block 2', async () => {
const res = await payload.findByID({
collection: 'localized-posts',
id: postId,
select: {
blocksSecond: {
first: { firstText: false },
},
},
})
expect(res).toStrictEqual({
...post,
blocksSecond: post.blocksSecond.map((block) => {
delete block['firstText']
return block
}),
})
})
})
})
describe('Local API - Deep Fields', () => {
let post: DeepPost
let postId: number | string
beforeEach(async () => {
post = await createDeepPost()
postId = post.id
})
it('should select deply group.array.group.text', async () => {
const res = await payload.findByID({
id: postId,
collection: 'deep-posts',
select: { group: { array: { group: { text: true } } } },
})
expect(res).toStrictEqual({
id: postId,
group: {
array: post.group.array.map((item) => ({
id: item.id,
group: {
text: item.group.text,
},
})),
},
})
})
it('should select deply group.array.group.*', async () => {
const res = await payload.findByID({
id: postId,
collection: 'deep-posts',
select: { group: { array: { group: true } } },
})
expect(res).toStrictEqual({
id: postId,
group: {
array: post.group.array.map((item) => ({
id: item.id,
group: item.group,
})),
},
})
})
it('should select deply group.blocks.block.text', async () => {
const res = await payload.findByID({
id: postId,
collection: 'deep-posts',
select: { group: { blocks: { block: { text: true } } } },
})
expect(res).toStrictEqual({
id: postId,
group: {
blocks: post.group.blocks.map((item) => ({
id: item.id,
blockType: item.blockType,
text: item.text,
})),
},
})
})
it('should select deply array.array.text', async () => {
const res = await payload.findByID({
id: postId,
collection: 'deep-posts',
select: { arrayTop: { arrayNested: { text: true } } },
})
expect(res).toStrictEqual({
id: postId,
arrayTop: post.arrayTop.map((item) => ({
id: item.id,
arrayNested: item.arrayNested.map((item) => ({
id: item.id,
text: item.text,
})),
})),
})
})
})
describe('Local API - Versioned Drafts Collection', () => {
let post: VersionedPost
let postId: number | string
beforeEach(async () => {
post = await createVersionedPost()
postId = post.id
})
// Clean up to safely mutate in each test
afterEach(async () => {
await payload.delete({ id: postId, collection: 'versioned-posts' })
})
it('should select only id as default', async () => {
const res = await payload.findByID({
collection: 'versioned-posts',
id: postId,
select: {},
draft: true,
})
expect(res).toStrictEqual({
id: postId,
})
})
it('should select only number', async () => {
const res = await payload.findByID({
collection: 'versioned-posts',
id: postId,
select: {
number: true,
},
draft: true,
})
expect(res).toStrictEqual({
id: postId,
number: post.number,
})
})
it('should exclude only number', async () => {
const res = await payload.findByID({
collection: 'versioned-posts',
id: postId,
select: {
number: false,
},
draft: true,
})
const expected = { ...post }
delete expected['number']
expect(res).toStrictEqual(expected)
})
it('should select number and text', async () => {
const res = await payload.findByID({
collection: 'versioned-posts',
id: postId,
select: {
number: true,
text: true,
},
draft: true,
})
expect(res).toStrictEqual({
id: postId,
number: post.number,
text: post.text,
})
})
it('payload.find should select number and text', async () => {
const res = await payload.find({
collection: 'versioned-posts',
where: {
id: {
equals: postId,
},
},
select: {
number: true,
text: true,
},
draft: true,
})
expect(res.docs[0]).toStrictEqual({
id: postId,
number: post.number,
text: post.text,
})
})
it('should select base id field inside of array', async () => {
const res = await payload.find({
collection: 'versioned-posts',
where: {
id: {
equals: postId,
},
},
select: {
array: {},
},
draft: true,
})
expect(res.docs[0]).toStrictEqual({
id: postId,
array: post.array.map((each) => ({ id: each.id })),
})
})
it('should select base id field inside of blocks', async () => {
const res = await payload.find({
collection: 'versioned-posts',
where: {
id: {
equals: postId,
},
},
select: {
blocks: {},
},
draft: true,
})
expect(res.docs[0]).toStrictEqual({
id: postId,
blocks: post.blocks.map((each) => ({ blockType: each.blockType, id: each.id })),
})
})
it('should select with payload.findVersions', async () => {
const res = await payload.findVersions({
collection: 'versioned-posts',
limit: 1,
sort: '-updatedAt',
where: { parent: { equals: postId } },
select: {
version: {
text: true,
},
},
})
// findVersions doesnt transform result with afterRead hook and so doesn't strip undefined values from the object
// undefined values are happened with drizzle adapters because of transform/read
const doc = res.docs[0]
expect(doc.createdAt).toBeUndefined()
expect(doc.updatedAt).toBeUndefined()
expect(doc.version.number).toBeUndefined()
expect(doc.version.createdAt).toBeUndefined()
expect(doc.version.text).toBe(post.text)
})
})
describe('Local API - Globals', () => {
let globalPost: GlobalPost
beforeAll(async () => {
globalPost = await payload.updateGlobal({
slug: 'global-post',
data: {
number: 2,
text: '3',
},
})
})
it('should select with find', async () => {
const res = await payload.findGlobal({
slug: 'global-post',
select: {
text: true,
},
})
expect(res).toStrictEqual({
id: globalPost.id,
text: globalPost.text,
})
})
it('should select with update', async () => {
const res = await payload.updateGlobal({
slug: 'global-post',
data: {},
select: {
text: true,
},
})
expect(res).toStrictEqual({
id: globalPost.id,
text: globalPost.text,
})
})
})
describe('Local API - operations', () => {
it('should apply select with create', async () => {
const res = await payload.create({
collection: 'posts',
data: {
text: 'asd',
number: 123,
},
select: {
text: true,
},
})
expect(Object.keys(res)).toStrictEqual(['id', 'text'])
})
it('should apply select with updateByID', async () => {
const post = await createPost()
const res = await payload.update({
collection: 'posts',
id: post.id,
data: {},
select: { text: true },
})
expect(Object.keys(res)).toStrictEqual(['id', 'text'])
})
it('should apply select with updateBulk', async () => {
const post = await createPost()
const res = await payload.update({
collection: 'posts',
where: {
id: {
equals: post.id,
},
},
data: {},
select: { text: true },
})
expect(Object.keys(res.docs[0])).toStrictEqual(['id', 'text'])
})
it('should apply select with deleteByID', async () => {
const post = await createPost()
const res = await payload.delete({
collection: 'posts',
id: post.id,
select: { text: true },
})
expect(Object.keys(res)).toStrictEqual(['id', 'text'])
})
it('should apply select with deleteBulk', async () => {
const post = await createPost()
const res = await payload.delete({
collection: 'posts',
where: {
id: {
equals: post.id,
},
},
select: { text: true },
})
expect(Object.keys(res.docs[0])).toStrictEqual(['id', 'text'])
})
it('should apply select with duplicate', async () => {
const post = await createPost()
const res = await payload.duplicate({
collection: 'posts',
id: post.id,
select: { text: true },
})
expect(Object.keys(res)).toStrictEqual(['id', 'text'])
})
})
describe('REST API - Base', () => {
let post: Post
let postId: number | string
beforeEach(async () => {
post = await createPost()
postId = post.id
})
// Clean up to safely mutate in each test
afterEach(async () => {
await payload.delete({ id: postId, collection: 'posts' })
})
describe('Include mode', () => {
it('should select only text', async () => {
const res = await restClient
.GET(`/posts/${postId}`, {
query: {
select: {
text: true,
} satisfies Config['collectionsSelect']['posts'],
},
})
.then((res) => res.json())
expect(res).toMatchObject({
text: post.text,
id: postId,
})
})
it('should select number and text', async () => {
const res = await restClient
.GET(`/posts/${postId}`, {
query: {
select: {
number: true,
text: true,
} satisfies Config['collectionsSelect']['posts'],
},
})
.then((res) => res.json())
expect(res).toMatchObject({
id: postId,
number: post.number,
text: post.text,
})
})
it('should select all the fields inside of group', async () => {
const res = await restClient
.GET(`/posts/${postId}`, {
query: {
select: {
group: true,
} satisfies Config['collectionsSelect']['posts'],
},
})
.then((res) => res.json())
expect(res).toMatchObject({
id: postId,
group: post.group,
})
})
it('should select text field inside of group', async () => {
const res = await restClient
.GET(`/posts/${postId}`, {
query: {
select: {
group: { text: true },
} satisfies Config['collectionsSelect']['posts'],
},
})
.then((res) => res.json())
expect(res).toMatchObject({
id: postId,
group: {
text: post.group.text,
},
})
})
})
describe('Exclude mode', () => {
it('should exclude only text field', async () => {
const res = await restClient
.GET(`/posts/${postId}`, {
query: {
select: {
text: false,
} satisfies Config['collectionsSelect']['posts'],
},
})
.then((res) => res.json())
const expected = { ...post }
delete expected['text']
expect(res).toMatchObject(expected)
})
it('should exclude number', async () => {
const res = await restClient
.GET(`/posts/${postId}`, {
query: {
select: {
number: false,
} satisfies Config['collectionsSelect']['posts'],
},
})
.then((res) => res.json())
const expected = { ...post }
delete expected['number']
expect(res).toMatchObject(expected)
})
it('should exclude number and text', async () => {
const res = await restClient
.GET(`/posts/${postId}`, {
query: {
select: {
number: false,
text: false,
} satisfies Config['collectionsSelect']['posts'],
},
})
.then((res) => res.json())
const expected = { ...post }
delete expected['text']
delete expected['number']
expect(res).toMatchObject(expected)
})
it('should exclude text field inside of group', async () => {
const res = await restClient
.GET(`/posts/${postId}`, {
query: {
select: {
group: {
text: false,
},
} satisfies Config['collectionsSelect']['posts'],
},
})
.then((res) => res.json())
const expected = deepCopyObject(post)
delete expected.group.text
expect(res).toMatchObject(expected)
})
})
})
})
function createPost() {
return payload.create({
collection: 'posts',
depth: 0,
data: {
number: 1,
text: 'text',
group: {
number: 1,
text: 'text',
},
blocks: [
{
blockType: 'cta',
ctaText: 'cta-text',
text: 'text',
},
{
blockType: 'intro',
introText: 'intro-text',
text: 'text',
},
],
array: [
{
text: 'text',
number: 1,
},
],
},
})
}
function createLocalizedPost() {
return payload.create({
collection: 'localized-posts',
depth: 0,
data: {
number: 1,
text: 'text',
group: {
number: 1,
text: 'text',
},
groupSecond: {
number: 1,
text: 'text',
},
blocks: [
{
blockType: 'cta',
ctaText: 'cta-text',
text: 'text',
},
{
blockType: 'intro',
introText: 'intro-text',
text: 'text',
},
],
blocksSecond: [
{
blockType: 'second',
secondText: 'cta-text',
text: 'text',
},
{
blockType: 'first',
firstText: 'intro-text',
text: 'text',
},
],
array: [
{
text: 'text',
number: 1,
},
],
arraySecond: [
{
text: 'text',
number: 1,
},
],
},
})
}
function createDeepPost() {
return payload.create({
collection: 'deep-posts',
data: {
arrayTop: [{ text: 'text1', arrayNested: [{ text: 'text2', number: 34 }] }],
group: {
array: [{ group: { number: 1, text: 'text-3' } }],
blocks: [{ blockType: 'block', number: 3, text: 'text-4' }],
},
},
})
}
function createVersionedPost() {
return payload.create({
collection: 'versioned-posts',
data: {
number: 2,
text: 'text',
array: [{ text: 'hello' }],
blocks: [{ blockType: 'test', text: 'hela' }],
},
})
}