Files
payload/test/joins/int.spec.ts
Dan Ribbens 27b1629927 fix(db-postgres, db-sqlite): joins relation name (#8491)
A join field on a relationship with a camelCase name would cause an
error from drizzle due to the relation name not matching.
2024-09-30 16:47:21 -04:00

463 lines
13 KiB
TypeScript

import type { Payload } from 'payload'
import path from 'path'
import { getFileByPath } from 'payload'
import { fileURLToPath } from 'url'
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
import type { Category, Post } from './payload-types.js'
import { devUser } from '../credentials.js'
import { idToString } from '../helpers/idToString.js'
import { initPayloadInt } from '../helpers/initPayloadInt.js'
import { categoriesSlug, uploadsSlug } from './shared.js'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
let payload: Payload
let token: string
let restClient: NextRESTClient
const { email, password } = devUser
describe('Joins Field', () => {
let category: Category
let categoryID
// --__--__--__--__--__--__--__--__--__
// Boilerplate test setup/teardown
// --__--__--__--__--__--__--__--__--__
beforeAll(async () => {
;({ payload, restClient } = await initPayloadInt(dirname))
const data = await restClient
.POST('/users/login', {
body: JSON.stringify({
email,
password,
}),
})
.then((res) => res.json())
token = data.token
category = await payload.create({
collection: categoriesSlug,
data: {
name: 'paginate example',
group: {},
},
})
// create an upload
const imageFilePath = path.resolve(dirname, './image.png')
const imageFile = await getFileByPath(imageFilePath)
const { id: uploadedImage } = await payload.create({
collection: uploadsSlug,
data: {},
file: imageFile,
})
categoryID = idToString(category.id, payload)
for (let i = 0; i < 15; i++) {
await createPost({
title: `test ${i}`,
category: category.id,
upload: uploadedImage,
group: {
category: category.id,
camelCaseCategory: category.id,
},
})
}
})
afterAll(async () => {
if (typeof payload.db.destroy === 'function') {
await payload.db.destroy()
}
})
it('should populate joins using findByID', async () => {
const categoryWithPosts = await payload.findByID({
id: category.id,
joins: {
'group.relatedPosts': {
sort: '-title',
},
},
collection: 'categories',
})
expect(categoryWithPosts.group.relatedPosts.docs).toHaveLength(10)
expect(categoryWithPosts.group.relatedPosts.docs[0]).toHaveProperty('id')
expect(categoryWithPosts.group.relatedPosts.docs[0]).toHaveProperty('title')
expect(categoryWithPosts.group.relatedPosts.docs[0].title).toStrictEqual('test 9')
})
it('should populate relationships in joins', async () => {
const { docs } = await payload.find({
limit: 1,
collection: 'posts',
})
expect(docs[0].category.id).toBeDefined()
expect(docs[0].category.name).toBeDefined()
expect(docs[0].category.relatedPosts.docs).toHaveLength(10)
})
it('should populate relationships in joins with camelCase names', async () => {
const { docs } = await payload.find({
limit: 1,
collection: 'posts',
})
expect(docs[0].group.camelCaseCategory.id).toBeDefined()
expect(docs[0].group.camelCaseCategory.name).toBeDefined()
expect(docs[0].group.camelCaseCategory.group.camelCasePosts.docs).toHaveLength(10)
})
it('should populate uploads in joins', async () => {
const { docs } = await payload.find({
limit: 1,
collection: 'posts',
})
expect(docs[0].upload.id).toBeDefined()
expect(docs[0].upload.relatedPosts.docs).toHaveLength(10)
})
it('should filter joins using where query', async () => {
const categoryWithPosts = await payload.findByID({
id: category.id,
joins: {
relatedPosts: {
sort: '-title',
where: {
title: {
equals: 'test 9',
},
},
},
},
collection: 'categories',
})
expect(categoryWithPosts.relatedPosts.docs).toHaveLength(1)
expect(categoryWithPosts.relatedPosts.hasNextPage).toStrictEqual(false)
})
it('should populate joins using find', async () => {
const result = await payload.find({
collection: 'categories',
where: {
id: { equals: category.id },
},
})
const [categoryWithPosts] = result.docs
expect(categoryWithPosts.group.relatedPosts.docs).toHaveLength(10)
expect(categoryWithPosts.group.relatedPosts.docs[0]).toHaveProperty('title')
expect(categoryWithPosts.group.relatedPosts.docs[0].title).toBe('test 14')
})
it('should not error when deleting documents with joins', async () => {
const category = await payload.create({
collection: 'categories',
data: {
name: 'category with post',
},
})
const post = await createPost({
category: category.id,
})
const result = await payload.delete({
collection: 'categories',
// id: category.id,
where: {
id: { equals: category.id },
},
})
expect(result.docs[0].id).toStrictEqual(category.id)
})
describe('Joins with localization', () => {
let localizedCategory: Category
beforeAll(async () => {
localizedCategory = await payload.create({
collection: 'localized-categories',
locale: 'en',
data: {
name: 'localized category',
},
})
const post1 = await payload.create({
collection: 'localized-posts',
locale: 'en',
data: {
title: 'english post 1',
category: localizedCategory.id,
},
})
await payload.update({
collection: 'localized-posts',
id: post1.id,
locale: 'es',
data: {
title: 'spanish post',
category: localizedCategory.id,
},
})
await payload.create({
collection: 'localized-posts',
locale: 'en',
data: {
title: 'english post 2',
category: localizedCategory.id,
},
})
})
it('should populate joins using findByID with localization on the relationship', async () => {
const enCategory = await payload.findByID({
id: localizedCategory.id,
collection: 'localized-categories',
locale: 'en',
})
const esCategory = await payload.findByID({
id: localizedCategory.id,
collection: 'localized-categories',
locale: 'es',
})
expect(enCategory.relatedPosts.docs).toHaveLength(2)
expect(esCategory.relatedPosts.docs).toHaveLength(1)
})
})
describe('REST', () => {
it('should have simple paginate for joins', async () => {
const query = {
depth: 1,
where: {
name: { equals: 'paginate example' },
},
joins: {
relatedPosts: {
sort: 'createdAt',
limit: 4,
},
},
}
const pageWithLimit = await restClient.GET(`/categories`, { query }).then((res) => res.json())
query.joins.relatedPosts.limit = 0
const unlimited = await restClient.GET(`/categories`, { query }).then((res) => res.json())
expect(pageWithLimit.docs[0].relatedPosts.docs).toHaveLength(4)
expect(pageWithLimit.docs[0].relatedPosts.docs[0].title).toStrictEqual('test 0')
expect(pageWithLimit.docs[0].relatedPosts.hasNextPage).toStrictEqual(true)
expect(unlimited.docs[0].relatedPosts.docs).toHaveLength(15)
expect(unlimited.docs[0].relatedPosts.docs[0].title).toStrictEqual('test 0')
expect(unlimited.docs[0].relatedPosts.hasNextPage).toStrictEqual(false)
})
it('should sort joins', async () => {
const response = await restClient
.GET(`/categories/${category.id}?joins[relatedPosts][sort]=-title`)
.then((res) => res.json())
expect(response.relatedPosts.docs[0].title).toStrictEqual('test 9')
})
it('should query in on collections with joins', async () => {
const response = await restClient
.GET(`/categories?where[id][in]=${category.id}`)
.then((res) => res.json())
expect(response.docs[0].name).toStrictEqual(category.name)
})
})
describe('GraphQL', () => {
it('should have simple paginate for joins', async () => {
const queryWithLimit = `query {
Categories(where: {
name: { equals: "paginate example" }
}) {
docs {
relatedPosts(
sort: "createdAt",
limit: 4
) {
docs {
title
}
hasNextPage
}
}
}
}`
const pageWithLimit = await restClient
.GRAPHQL_POST({ body: JSON.stringify({ query: queryWithLimit }) })
.then((res) => res.json())
const queryUnlimited = `query {
Categories(
where: {
name: { equals: "paginate example" }
}
) {
docs {
relatedPosts(
sort: "createdAt",
limit: 0
) {
docs {
title
createdAt
}
hasNextPage
}
}
}
}`
const unlimited = await restClient
.GRAPHQL_POST({ body: JSON.stringify({ query: queryUnlimited }) })
.then((res) => res.json())
expect(pageWithLimit.data.Categories.docs[0].relatedPosts.docs).toHaveLength(4)
expect(pageWithLimit.data.Categories.docs[0].relatedPosts.docs[0].title).toStrictEqual(
'test 0',
)
expect(pageWithLimit.data.Categories.docs[0].relatedPosts.hasNextPage).toStrictEqual(true)
expect(unlimited.data.Categories.docs[0].relatedPosts.docs).toHaveLength(15)
expect(unlimited.data.Categories.docs[0].relatedPosts.docs[0].title).toStrictEqual('test 0')
expect(unlimited.data.Categories.docs[0].relatedPosts.hasNextPage).toStrictEqual(false)
})
it('should have simple paginate for joins inside groups', async () => {
const queryWithLimit = `query {
Categories(where: {
name: { equals: "paginate example" }
}) {
docs {
group {
relatedPosts(
sort: "createdAt",
limit: 4
) {
docs {
title
}
hasNextPage
}
}
}
}
}`
const pageWithLimit = await restClient
.GRAPHQL_POST({ body: JSON.stringify({ query: queryWithLimit }) })
.then((res) => res.json())
const queryUnlimited = `query {
Categories(
where: {
name: { equals: "paginate example" }
}
) {
docs {
group {
relatedPosts(
sort: "createdAt",
limit: 0
) {
docs {
title
}
hasNextPage
}
}
}
}
}`
const unlimited = await restClient
.GRAPHQL_POST({ body: JSON.stringify({ query: queryUnlimited }) })
.then((res) => res.json())
expect(pageWithLimit.data.Categories.docs[0].group.relatedPosts.docs).toHaveLength(4)
expect(pageWithLimit.data.Categories.docs[0].group.relatedPosts.docs[0].title).toStrictEqual(
'test 0',
)
expect(pageWithLimit.data.Categories.docs[0].group.relatedPosts.hasNextPage).toStrictEqual(
true,
)
expect(unlimited.data.Categories.docs[0].group.relatedPosts.docs).toHaveLength(15)
expect(unlimited.data.Categories.docs[0].group.relatedPosts.docs[0].title).toStrictEqual(
'test 0',
)
expect(unlimited.data.Categories.docs[0].group.relatedPosts.hasNextPage).toStrictEqual(false)
})
it('should sort joins', async () => {
const query = `query {
Category(id: ${categoryID}) {
relatedPosts(
sort: "-title"
) {
docs {
title
}
}
}
}`
const response = await restClient
.GRAPHQL_POST({ body: JSON.stringify({ query }) })
.then((res) => res.json())
expect(response.data.Category.relatedPosts.docs[0].title).toStrictEqual('test 9')
})
it('should query in on collections with joins', async () => {
const query = `query {
Category(id: ${categoryID}) {
relatedPosts(
where: {
title: {
equals: "test 3"
}
}
) {
docs {
title
}
}
}
}`
const response = await restClient
.GRAPHQL_POST({ body: JSON.stringify({ query }) })
.then((res) => res.json())
expect(response.data.Category.relatedPosts.docs[0].title).toStrictEqual('test 3')
})
})
})
async function createPost(overrides?: Partial<Post>) {
return payload.create({
collection: 'posts',
data: {
title: 'test',
...overrides,
},
})
}