feat: add populate property to Local / REST API (#8969)
### What?
Adds `populate` property to Local API and REST API operations that can
be used to specify `select` for a specific collection when it's
populated
```ts
const result = await payload.findByID({
populate: {
// type safe if you have generated types
posts: {
text: true,
},
},
collection: 'pages',
depth: 1,
id: aboutPage.id,
})
result.relatedPost // only has text and id properties
```
```ts
fetch('https://localhost:3000/api/pages?populate[posts][text]=true') // highlight-line
.then((res) => res.json())
.then((data) => console.log(data))
```
It also overrides
[`defaultPopulate`](https://github.com/payloadcms/payload/pull/8934)
Ensures `defaultPopulate` doesn't affect GraphQL.
### How?
Implements the property for all operations that have the `depth`
argument.
This commit is contained in:
@@ -11,13 +11,14 @@ export const Pages: CollectionConfig<'pages'> = {
|
||||
defaultPopulate: {
|
||||
slug: true,
|
||||
},
|
||||
access: { read: () => true },
|
||||
fields: [
|
||||
{
|
||||
name: 'content',
|
||||
type: 'blocks',
|
||||
blocks: [
|
||||
{
|
||||
slug: 'cta',
|
||||
slug: 'introduction',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
@@ -80,5 +81,9 @@ export const Pages: CollectionConfig<'pages'> = {
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'additional',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1612,24 +1612,26 @@ describe('Select', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('defaultPopulate', () => {
|
||||
describe('populate / defaultPopulate', () => {
|
||||
let homePage: Page
|
||||
let aboutPage: Page
|
||||
let expectedHomePage: { id: number | string; slug: string }
|
||||
let expectedHomePageOverride: { additional: string; id: number | string }
|
||||
beforeAll(async () => {
|
||||
homePage = await payload.create({
|
||||
depth: 0,
|
||||
collection: 'pages',
|
||||
data: { content: [], slug: 'home' },
|
||||
data: { content: [], slug: 'home', additional: 'additional-data' },
|
||||
})
|
||||
expectedHomePage = { id: homePage.id, slug: homePage.slug }
|
||||
expectedHomePageOverride = { id: homePage.id, additional: homePage.additional }
|
||||
aboutPage = await payload.create({
|
||||
depth: 0,
|
||||
collection: 'pages',
|
||||
data: {
|
||||
content: [
|
||||
{
|
||||
blockType: 'cta',
|
||||
blockType: 'introduction',
|
||||
richTextSlate: [
|
||||
{
|
||||
type: 'relationship',
|
||||
@@ -1745,6 +1747,151 @@ describe('Select', () => {
|
||||
expect(richTextLexicalRel.value).toMatchObject(expectedHomePage)
|
||||
expect(richTextSlateRel.value).toMatchObject(expectedHomePage)
|
||||
})
|
||||
|
||||
it('graphQL - should retrieve fields against defaultPopulate', async () => {
|
||||
const query = `query {
|
||||
Pages {
|
||||
docs {
|
||||
id,
|
||||
content {
|
||||
... on Introduction {
|
||||
link {
|
||||
doc {
|
||||
id,
|
||||
additional,
|
||||
slug,
|
||||
}
|
||||
},
|
||||
richTextLexical(depth: 1)
|
||||
richTextSlate(depth: 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
const {
|
||||
data: {
|
||||
Pages: {
|
||||
docs: [
|
||||
{
|
||||
content: [
|
||||
{
|
||||
link,
|
||||
richTextSlate: [richTextSlateRel],
|
||||
richTextLexical: {
|
||||
root: {
|
||||
children: [richTextLexicalRel],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
} = await restClient
|
||||
.GRAPHQL_POST({
|
||||
body: JSON.stringify({ query }),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
|
||||
expect(link.doc).toMatchObject({
|
||||
id: homePage.id,
|
||||
additional: homePage.additional,
|
||||
slug: homePage.slug,
|
||||
})
|
||||
expect(richTextLexicalRel.value).toMatchObject(homePage)
|
||||
expect(richTextSlateRel.value).toMatchObject(homePage)
|
||||
})
|
||||
|
||||
it('local API - should populate and override defaultSelect select shape from the populate arg', async () => {
|
||||
const result = await payload.findByID({
|
||||
populate: {
|
||||
pages: {
|
||||
additional: true,
|
||||
},
|
||||
},
|
||||
collection: 'pages',
|
||||
depth: 1,
|
||||
id: aboutPage.id,
|
||||
})
|
||||
|
||||
const {
|
||||
content: [
|
||||
{
|
||||
link: { doc, docHasManyPoly, docMany, docPoly },
|
||||
richTextSlate: [richTextSlateRel],
|
||||
richTextLexical: {
|
||||
root: {
|
||||
children: [richTextLexicalRel],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
} = result
|
||||
|
||||
expect(doc).toStrictEqual(expectedHomePageOverride)
|
||||
expect(docMany).toStrictEqual([expectedHomePageOverride])
|
||||
expect(docPoly).toStrictEqual({
|
||||
relationTo: 'pages',
|
||||
value: expectedHomePageOverride,
|
||||
})
|
||||
expect(docHasManyPoly).toStrictEqual([
|
||||
{
|
||||
relationTo: 'pages',
|
||||
value: expectedHomePageOverride,
|
||||
},
|
||||
])
|
||||
|
||||
expect(richTextLexicalRel.value).toStrictEqual(expectedHomePageOverride)
|
||||
expect(richTextSlateRel.value).toStrictEqual(expectedHomePageOverride)
|
||||
})
|
||||
|
||||
it('rEST API - should populate and override defaultSelect select shape from the populate arg', async () => {
|
||||
const result = await restClient
|
||||
.GET(`/pages/${aboutPage.id}`, {
|
||||
query: {
|
||||
populate: {
|
||||
pages: {
|
||||
additional: true,
|
||||
},
|
||||
},
|
||||
depth: 1,
|
||||
},
|
||||
})
|
||||
.then((res) => res.json())
|
||||
|
||||
const {
|
||||
content: [
|
||||
{
|
||||
link: { doc, docHasManyPoly, docMany, docPoly },
|
||||
richTextSlate: [richTextSlateRel],
|
||||
richTextLexical: {
|
||||
root: {
|
||||
children: [richTextLexicalRel],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
} = result
|
||||
|
||||
expect(doc).toMatchObject(expectedHomePageOverride)
|
||||
expect(docMany).toMatchObject([expectedHomePageOverride])
|
||||
expect(docPoly).toMatchObject({
|
||||
relationTo: 'pages',
|
||||
value: expectedHomePageOverride,
|
||||
})
|
||||
expect(docHasManyPoly).toMatchObject([
|
||||
{
|
||||
relationTo: 'pages',
|
||||
value: expectedHomePageOverride,
|
||||
},
|
||||
])
|
||||
|
||||
expect(richTextLexicalRel.value).toMatchObject(expectedHomePageOverride)
|
||||
expect(richTextSlateRel.value).toMatchObject(expectedHomePageOverride)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -297,10 +297,11 @@ export interface Page {
|
||||
| null;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'cta';
|
||||
blockType: 'introduction';
|
||||
}[]
|
||||
| null;
|
||||
slug: string;
|
||||
additional?: string | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
@@ -605,7 +606,7 @@ export interface PagesSelect<T extends boolean = true> {
|
||||
content?:
|
||||
| T
|
||||
| {
|
||||
cta?:
|
||||
introduction?:
|
||||
| T
|
||||
| {
|
||||
title?: T;
|
||||
@@ -625,6 +626,7 @@ export interface PagesSelect<T extends boolean = true> {
|
||||
};
|
||||
};
|
||||
slug?: T;
|
||||
additional?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user