chore: passing field tests
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { formatJSONPathSegment } from './formatJSONPathSegment'
|
||||
|
||||
export const convertPathToJSONTraversal = (path: string) => {
|
||||
const segments = path.split('.')
|
||||
export const convertPathToJSONTraversal = (incomingSegments: string[]) => {
|
||||
const segments = [...incomingSegments]
|
||||
segments.shift()
|
||||
|
||||
return segments.reduce((res, segment, i) => {
|
||||
|
||||
@@ -45,7 +45,7 @@ type CreateConstraintArgs = {
|
||||
}
|
||||
|
||||
const createConstraint = ({ operator, pathSegments, value }: CreateConstraintArgs): string => {
|
||||
const jsonQuery = convertPathToJSONTraversal(pathSegments.join('.'))
|
||||
const jsonQuery = convertPathToJSONTraversal(pathSegments)
|
||||
return `${pathSegments[0]}${jsonQuery} ${operatorMap[operator]} '${value}'`
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ type TableColumn = {
|
||||
constraints: Constraint[]
|
||||
field: FieldAffectingData
|
||||
getNotNullColumnByValue?: (val: unknown) => string
|
||||
pathSegments?: string[]
|
||||
rawColumn?: SQL
|
||||
table: GenericTable
|
||||
}
|
||||
@@ -56,12 +57,13 @@ export const getTableColumnFromPath = ({
|
||||
fields,
|
||||
joinAliases,
|
||||
joins,
|
||||
locale,
|
||||
pathSegments,
|
||||
locale: incomingLocale,
|
||||
pathSegments: incomingSegments,
|
||||
selectFields,
|
||||
tableName,
|
||||
}: Args): TableColumn => {
|
||||
const fieldPath = pathSegments[0]
|
||||
const fieldPath = incomingSegments[0]
|
||||
let locale = incomingLocale
|
||||
const field = flattenTopLevelFields(fields as Field[]).find(
|
||||
(fieldToFind) => fieldAffectsData(fieldToFind) && fieldToFind.name === fieldPath,
|
||||
) as Field | TabAsField
|
||||
@@ -81,6 +83,21 @@ export const getTableColumnFromPath = ({
|
||||
}
|
||||
|
||||
if (field) {
|
||||
const pathSegments = [...incomingSegments]
|
||||
|
||||
// If next segment is a locale,
|
||||
// we need to take it out and use it as the locale from this point on
|
||||
if ('localized' in field && field.localized && adapter.payload.config.localization) {
|
||||
const matchedLocale = adapter.payload.config.localization.localeCodes.find(
|
||||
(locale) => locale === pathSegments[1],
|
||||
)
|
||||
|
||||
if (matchedLocale) {
|
||||
locale = matchedLocale
|
||||
pathSegments.splice(1, 1)
|
||||
}
|
||||
}
|
||||
|
||||
switch (field.type) {
|
||||
case 'tabs': {
|
||||
return getTableColumnFromPath({
|
||||
@@ -204,7 +221,7 @@ export const getTableColumnFromPath = ({
|
||||
let blockTableColumn: TableColumn
|
||||
let newTableName: string
|
||||
const hasBlockField = field.blocks.some((block) => {
|
||||
newTableName = `${tableName}_${toSnakeCase(block.slug)}`
|
||||
newTableName = `${tableName}_blocks_${toSnakeCase(block.slug)}`
|
||||
let result
|
||||
const blockConstraints = []
|
||||
const blockSelectFields = {}
|
||||
@@ -255,6 +272,7 @@ export const getTableColumnFromPath = ({
|
||||
columnName: blockTableColumn.columnName,
|
||||
constraints,
|
||||
field: blockTableColumn.field,
|
||||
pathSegments: pathSegments.slice(1),
|
||||
rawColumn: blockTableColumn.rawColumn,
|
||||
table: adapter.tables[newTableName],
|
||||
}
|
||||
@@ -381,6 +399,7 @@ export const getTableColumnFromPath = ({
|
||||
columnName: `${columnPrefix}${field.name}`,
|
||||
constraints,
|
||||
field,
|
||||
pathSegments: pathSegments,
|
||||
table: targetTable,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ export async function parseParams({
|
||||
constraints: queryConstraints,
|
||||
field,
|
||||
getNotNullColumnByValue,
|
||||
pathSegments,
|
||||
rawColumn,
|
||||
table,
|
||||
} = getTableColumnFromPath({
|
||||
@@ -102,14 +103,14 @@ export async function parseParams({
|
||||
constraints.push(operatorMap.equals(constraintTable[col], value))
|
||||
})
|
||||
|
||||
if (['json', 'richText'].includes(field.type)) {
|
||||
const pathSegments = relationOrPath.split('.').slice(1)
|
||||
pathSegments.unshift(table[columnName].name)
|
||||
if (['json', 'richText'].includes(field.type) && Array.isArray(pathSegments)) {
|
||||
const segments = pathSegments.slice(1)
|
||||
segments.unshift(table[columnName].name)
|
||||
|
||||
if (field.type === 'richText') {
|
||||
const jsonQuery = createJSONQuery({
|
||||
operator,
|
||||
pathSegments,
|
||||
pathSegments: segments,
|
||||
treatAsArray: ['children'],
|
||||
treatRootAsArray: true,
|
||||
value: val,
|
||||
@@ -119,7 +120,7 @@ export async function parseParams({
|
||||
}
|
||||
|
||||
if (field.type === 'json') {
|
||||
const jsonQuery = convertPathToJSONTraversal(relationOrPath)
|
||||
const jsonQuery = convertPathToJSONTraversal(pathSegments)
|
||||
constraints.push(sql.raw(`${table[columnName].name}${jsonQuery} = '%${val}%'`))
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@ type Args = {
|
||||
buildRelationships?: boolean
|
||||
disableUnique: boolean
|
||||
fields: Field[]
|
||||
rootRelationsToBuild?: Map<string, string>
|
||||
rootTableIDColType?: string
|
||||
rootTableName?: string
|
||||
tableName: string
|
||||
timestamps?: boolean
|
||||
}
|
||||
@@ -44,6 +47,9 @@ export const buildTable = ({
|
||||
buildRelationships,
|
||||
disableUnique = false,
|
||||
fields,
|
||||
rootRelationsToBuild,
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
tableName,
|
||||
timestamps,
|
||||
}: Args): Result => {
|
||||
@@ -100,6 +106,9 @@ export const buildTable = ({
|
||||
parentTableName: tableName,
|
||||
relationsToBuild,
|
||||
relationships,
|
||||
rootRelationsToBuild: rootRelationsToBuild || relationsToBuild,
|
||||
rootTableIDColType: rootTableIDColType || idColType,
|
||||
rootTableName: rootTableName || tableName,
|
||||
}))
|
||||
|
||||
if (timestamps) {
|
||||
|
||||
@@ -28,6 +28,7 @@ import { hasLocalesTable } from '../utilities/hasLocalesTable'
|
||||
import { buildTable } from './build'
|
||||
import { createIndex } from './createIndex'
|
||||
import { parentIDColumnMap } from './parentIDColumnMap'
|
||||
import { validateExistingBlockIsIdentical } from './validateExistingBlockIsIdentical'
|
||||
|
||||
type Args = {
|
||||
adapter: PostgresAdapter
|
||||
@@ -45,6 +46,9 @@ type Args = {
|
||||
parentTableName: string
|
||||
relationsToBuild: Map<string, string>
|
||||
relationships: Set<string>
|
||||
rootRelationsToBuild?: Map<string, string>
|
||||
rootTableIDColType: string
|
||||
rootTableName: string
|
||||
}
|
||||
|
||||
type Result = {
|
||||
@@ -70,6 +74,9 @@ export const traverseFields = ({
|
||||
parentTableName,
|
||||
relationsToBuild,
|
||||
relationships,
|
||||
rootRelationsToBuild,
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
}: Args): Result => {
|
||||
let hasLocalizedField = false
|
||||
let hasLocalizedRelationshipField = false
|
||||
@@ -89,7 +96,9 @@ export const traverseFields = ({
|
||||
let targetIndexes = indexes
|
||||
|
||||
if (fieldAffectsData(field)) {
|
||||
columnName = `${columnPrefix || ''}${field.name[0] === '_' ? '_' : ''}${toSnakeCase(field.name)}`
|
||||
columnName = `${columnPrefix || ''}${field.name[0] === '_' ? '_' : ''}${toSnakeCase(
|
||||
field.name,
|
||||
)}`
|
||||
fieldName = `${fieldPrefix || ''}${field.name}`
|
||||
|
||||
// If field is localized,
|
||||
@@ -271,6 +280,9 @@ export const traverseFields = ({
|
||||
baseExtraConfig,
|
||||
disableUnique,
|
||||
fields: field.fields,
|
||||
rootRelationsToBuild,
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
tableName: arrayTableName,
|
||||
})
|
||||
|
||||
@@ -302,12 +314,12 @@ export const traverseFields = ({
|
||||
|
||||
case 'blocks': {
|
||||
field.blocks.forEach((block) => {
|
||||
const blockTableName = `${newTableName}_${toSnakeCase(block.slug)}`
|
||||
const blockTableName = `${rootTableName}_blocks_${toSnakeCase(block.slug)}`
|
||||
if (!adapter.tables[blockTableName]) {
|
||||
const baseColumns: Record<string, PgColumnBuilder> = {
|
||||
_order: integer('_order').notNull(),
|
||||
_parentID: parentIDColumnMap[parentIDColType]('_parent_id')
|
||||
.references(() => adapter.tables[parentTableName].id, { onDelete: 'cascade' })
|
||||
_parentID: parentIDColumnMap[rootTableIDColType]('_parent_id')
|
||||
.references(() => adapter.tables[rootTableName].id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
_path: text('_path').notNull(),
|
||||
}
|
||||
@@ -332,6 +344,9 @@ export const traverseFields = ({
|
||||
baseExtraConfig,
|
||||
disableUnique,
|
||||
fields: block.fields,
|
||||
rootRelationsToBuild,
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
tableName: blockTableName,
|
||||
})
|
||||
|
||||
@@ -339,9 +354,9 @@ export const traverseFields = ({
|
||||
adapter.tables[blockTableName],
|
||||
({ many, one }) => {
|
||||
const result: Record<string, Relation<string>> = {
|
||||
_parentID: one(adapter.tables[parentTableName], {
|
||||
_parentID: one(adapter.tables[rootTableName], {
|
||||
fields: [adapter.tables[blockTableName]._parentID],
|
||||
references: [adapter.tables[parentTableName].id],
|
||||
references: [adapter.tables[rootTableName].id],
|
||||
}),
|
||||
}
|
||||
|
||||
@@ -358,9 +373,16 @@ export const traverseFields = ({
|
||||
)
|
||||
|
||||
adapter.relations[`relations_${blockTableName}`] = blockTableRelations
|
||||
} else if (process.env.NODE_ENV !== 'production') {
|
||||
validateExistingBlockIsIdentical({
|
||||
block,
|
||||
localized: field.localized,
|
||||
rootTableName,
|
||||
table: adapter.tables[blockTableName],
|
||||
})
|
||||
}
|
||||
|
||||
relationsToBuild.set(`_blocks_${block.slug}`, blockTableName)
|
||||
rootRelationsToBuild.set(`_blocks_${block.slug}`, blockTableName)
|
||||
})
|
||||
|
||||
break
|
||||
@@ -390,6 +412,9 @@ export const traverseFields = ({
|
||||
parentTableName,
|
||||
relationsToBuild,
|
||||
relationships,
|
||||
rootRelationsToBuild,
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
})
|
||||
|
||||
if (groupHasLocalizedField) hasLocalizedField = true
|
||||
@@ -420,6 +445,9 @@ export const traverseFields = ({
|
||||
parentTableName,
|
||||
relationsToBuild,
|
||||
relationships,
|
||||
rootRelationsToBuild,
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
})
|
||||
|
||||
if (groupHasLocalizedField) hasLocalizedField = true
|
||||
@@ -451,6 +479,9 @@ export const traverseFields = ({
|
||||
parentTableName,
|
||||
relationsToBuild,
|
||||
relationships,
|
||||
rootRelationsToBuild,
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
})
|
||||
|
||||
if (tabHasLocalizedField) hasLocalizedField = true
|
||||
@@ -484,6 +515,9 @@ export const traverseFields = ({
|
||||
parentTableName,
|
||||
relationsToBuild,
|
||||
relationships,
|
||||
rootRelationsToBuild,
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
})
|
||||
|
||||
if (rowHasLocalizedField) hasLocalizedField = true
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { Block } from 'payload/types'
|
||||
|
||||
import { InvalidConfiguration } from 'payload/errors'
|
||||
import { flattenTopLevelFields } from 'payload/utilities'
|
||||
|
||||
import type { GenericTable } from '../types'
|
||||
|
||||
type Args = {
|
||||
block: Block
|
||||
localized: boolean
|
||||
rootTableName: string
|
||||
table: GenericTable
|
||||
}
|
||||
|
||||
export const validateExistingBlockIsIdentical = ({
|
||||
block,
|
||||
localized,
|
||||
rootTableName,
|
||||
table,
|
||||
}: Args): void => {
|
||||
if (table) {
|
||||
const fieldNames = flattenTopLevelFields(block.fields).flatMap((field) => field.name)
|
||||
|
||||
Object.keys(table).forEach((fieldName) => {
|
||||
if (!['_locale', '_order', '_parentID', '_path'].includes(fieldName)) {
|
||||
if (fieldNames.indexOf(fieldName) === -1) {
|
||||
throw new InvalidConfiguration(
|
||||
`The table ${rootTableName} has multiple blocks with slug ${block.slug}, but the schemas do not match. One block includes the field ${fieldName}, while the other block does not.`,
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (Boolean(localized) !== Boolean(table._locale)) {
|
||||
throw new InvalidConfiguration(
|
||||
`The table ${rootTableName} has multiple blocks with slug ${block.slug}, but the schemas do not match. One is localized, but another is not. Block schemas of the same name must match exactly.`,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ export const transformBlocks = ({
|
||||
|
||||
if (field.localized && locale) newRow.row._locale = locale
|
||||
|
||||
const blockTableName = `${baseTableName}_${blockType}`
|
||||
const blockTableName = `${baseTableName}_blocks_${blockType}`
|
||||
|
||||
traverseFields({
|
||||
arrays: newRow.arrays,
|
||||
|
||||
@@ -185,12 +185,12 @@ export const upsertRow = async <T extends TypeWithID>({
|
||||
parentID: insertedRow.id,
|
||||
pathColumnName: '_path',
|
||||
rows: blockRows.map(({ row }) => row),
|
||||
tableName: `${tableName}_${blockName}`,
|
||||
tableName: `${tableName}_blocks_${blockName}`,
|
||||
})
|
||||
}
|
||||
|
||||
insertedBlockRows[blockName] = await db
|
||||
.insert(adapter.tables[`${tableName}_${blockName}`])
|
||||
.insert(adapter.tables[`${tableName}_blocks_${blockName}`])
|
||||
.values(blockRows.map(({ row }) => row))
|
||||
.returning()
|
||||
|
||||
@@ -217,7 +217,7 @@ export const upsertRow = async <T extends TypeWithID>({
|
||||
|
||||
if (blockLocaleRowsToInsert.length > 0) {
|
||||
await db
|
||||
.insert(adapter.tables[`${tableName}_${blockName}_locales`])
|
||||
.insert(adapter.tables[`${tableName}_blocks_${blockName}_locales`])
|
||||
.values(blockLocaleRowsToInsert)
|
||||
.returning()
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { CollectionConfig } from '../../../../packages/payload/src/collections/config/types'
|
||||
import type { Field } from '../../../../packages/payload/src/fields/config/types'
|
||||
import type { BlockField } from '../../../../packages/payload/src/fields/config/types'
|
||||
|
||||
export const blocksFieldSeedData = [
|
||||
export const getBlocksFieldSeedData = (prefix?: string): any => [
|
||||
{
|
||||
blockName: 'First block',
|
||||
blockType: 'text',
|
||||
blockType: prefix ? `${prefix}Content` : 'content',
|
||||
text: 'first block',
|
||||
richText: [
|
||||
{
|
||||
@@ -14,12 +14,12 @@ export const blocksFieldSeedData = [
|
||||
},
|
||||
{
|
||||
blockName: 'Second block',
|
||||
blockType: 'number',
|
||||
blockType: prefix ? `${prefix}Number` : 'number',
|
||||
number: 342,
|
||||
},
|
||||
{
|
||||
blockName: 'Sub-block demonstration',
|
||||
blockType: 'subBlocks',
|
||||
blockType: prefix ? `${prefix}SubBlocks` : 'subBlocks',
|
||||
subBlocks: [
|
||||
{
|
||||
blockName: 'First sub block',
|
||||
@@ -38,15 +38,15 @@ export const blocksFieldSeedData = [
|
||||
blockType: 'i18n-text',
|
||||
text: 'first block',
|
||||
},
|
||||
] as const
|
||||
]
|
||||
|
||||
export const blocksField: Field = {
|
||||
export const getBlocksField = (prefix?: string): BlockField => ({
|
||||
name: 'blocks',
|
||||
type: 'blocks',
|
||||
required: true,
|
||||
blocks: [
|
||||
{
|
||||
slug: 'text',
|
||||
slug: prefix ? `${prefix}Content` : 'content',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
@@ -60,7 +60,7 @@ export const blocksField: Field = {
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'number',
|
||||
slug: prefix ? `${prefix}Number` : 'number',
|
||||
fields: [
|
||||
{
|
||||
name: 'number',
|
||||
@@ -70,7 +70,7 @@ export const blocksField: Field = {
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'subBlocks',
|
||||
slug: prefix ? `${prefix}SubBlocks` : 'subBlocks',
|
||||
fields: [
|
||||
{
|
||||
type: 'collapsible',
|
||||
@@ -107,7 +107,7 @@ export const blocksField: Field = {
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'tabs',
|
||||
slug: prefix ? `${prefix}Tabs` : 'tabs',
|
||||
fields: [
|
||||
{
|
||||
type: 'tabs',
|
||||
@@ -143,15 +143,15 @@ export const blocksField: Field = {
|
||||
],
|
||||
},
|
||||
],
|
||||
defaultValue: blocksFieldSeedData,
|
||||
}
|
||||
defaultValue: getBlocksFieldSeedData(prefix),
|
||||
})
|
||||
|
||||
const BlockFields: CollectionConfig = {
|
||||
slug: 'block-fields',
|
||||
fields: [
|
||||
blocksField,
|
||||
getBlocksField(),
|
||||
{
|
||||
...blocksField,
|
||||
...getBlocksField('localized'),
|
||||
name: 'collapsedByDefaultBlocks',
|
||||
localized: true,
|
||||
admin: {
|
||||
@@ -159,7 +159,7 @@ const BlockFields: CollectionConfig = {
|
||||
},
|
||||
},
|
||||
{
|
||||
...blocksField,
|
||||
...getBlocksField('localized'),
|
||||
name: 'localizedBlocks',
|
||||
localized: true,
|
||||
},
|
||||
@@ -247,8 +247,8 @@ const BlockFields: CollectionConfig = {
|
||||
}
|
||||
|
||||
export const blocksDoc = {
|
||||
blocks: blocksFieldSeedData,
|
||||
localizedBlocks: blocksFieldSeedData,
|
||||
blocks: getBlocksFieldSeedData(),
|
||||
localizedBlocks: getBlocksFieldSeedData('localized'),
|
||||
}
|
||||
|
||||
export default BlockFields
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import type { CollectionConfig } from '../../../../packages/payload/src/collections/config/types'
|
||||
|
||||
import { blocksField, blocksFieldSeedData } from '../Blocks'
|
||||
import { getBlocksField, getBlocksFieldSeedData } from '../Blocks'
|
||||
import { UIField } from './UIField'
|
||||
import { localizedTextValue, namedTabDefaultValue, namedTabText, tabsSlug } from './constants'
|
||||
|
||||
@@ -50,7 +50,7 @@ const TabsFields: CollectionConfig = {
|
||||
{
|
||||
label: 'Tab with Blocks',
|
||||
description: 'Blocks are rendered here to ensure they populate and render correctly.',
|
||||
fields: [blocksField],
|
||||
fields: [getBlocksField()],
|
||||
},
|
||||
{
|
||||
label: 'Tab with Group',
|
||||
@@ -277,7 +277,7 @@ export const tabsDoc = {
|
||||
text: 'Here is some data for the third row',
|
||||
},
|
||||
],
|
||||
blocks: blocksFieldSeedData,
|
||||
blocks: getBlocksFieldSeedData(),
|
||||
group: {
|
||||
number: 12,
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@ import { initPayloadTest } from '../helpers/configHelpers'
|
||||
import { RESTClient } from '../helpers/rest'
|
||||
import configPromise from '../uploads/config'
|
||||
import { arrayDefaultValue, arrayFieldsSlug } from './collections/Array'
|
||||
import { blocksFieldSeedData } from './collections/Blocks'
|
||||
import { blocksDoc } from './collections/Blocks'
|
||||
import { dateDoc } from './collections/Date'
|
||||
import {
|
||||
groupDefaultChild,
|
||||
@@ -612,16 +612,16 @@ describe('Fields', () => {
|
||||
collection: 'block-fields',
|
||||
})
|
||||
|
||||
expect(blockFields.docs[0].blocks[0].blockType).toEqual(blocksFieldSeedData[0].blockType)
|
||||
expect(blockFields.docs[0].blocks[0].text).toEqual(blocksFieldSeedData[0].text)
|
||||
expect(blockFields.docs[0].blocks[0].blockType).toEqual(blocksDoc.blocks[0].blockType)
|
||||
expect(blockFields.docs[0].blocks[0].text).toEqual(blocksDoc.blocks[0].text)
|
||||
|
||||
expect(blockFields.docs[0].blocks[2].blockType).toEqual(blocksFieldSeedData[2].blockType)
|
||||
expect(blockFields.docs[0].blocks[2].blockName).toEqual(blocksFieldSeedData[2].blockName)
|
||||
expect(blockFields.docs[0].blocks[2].blockType).toEqual(blocksDoc.blocks[2].blockType)
|
||||
expect(blockFields.docs[0].blocks[2].blockName).toEqual(blocksDoc.blocks[2].blockName)
|
||||
expect(blockFields.docs[0].blocks[2].subBlocks[0].number).toEqual(
|
||||
blocksFieldSeedData[2].subBlocks[0].number,
|
||||
blocksDoc.blocks[2].subBlocks[0].number,
|
||||
)
|
||||
expect(blockFields.docs[0].blocks[2].subBlocks[1].text).toEqual(
|
||||
blocksFieldSeedData[2].subBlocks[1].text,
|
||||
blocksDoc.blocks[2].subBlocks[1].text,
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user