From 09883ee78c842ce24b46f6c0f360a1aebf80c114 Mon Sep 17 00:00:00 2001 From: James Date: Wed, 27 Sep 2023 16:25:23 -0400 Subject: [PATCH] chore: passing field tests --- .../convertPathToJSONTraversal.ts | 4 +- .../src/queries/createJSONQuery/index.ts | 2 +- .../src/queries/getTableColumnFromPath.ts | 27 +++++++++-- .../db-postgres/src/queries/parseParams.ts | 11 +++-- packages/db-postgres/src/schema/build.ts | 9 ++++ .../db-postgres/src/schema/traverseFields.ts | 48 ++++++++++++++++--- .../validateExistingBlockIsIdentical.ts | 40 ++++++++++++++++ .../db-postgres/src/transform/write/blocks.ts | 2 +- packages/db-postgres/src/upsertRow/index.ts | 6 +-- test/fields/collections/Blocks/index.ts | 36 +++++++------- test/fields/collections/Tabs/index.ts | 6 +-- test/fields/int.spec.ts | 14 +++--- 12 files changed, 154 insertions(+), 51 deletions(-) create mode 100644 packages/db-postgres/src/schema/validateExistingBlockIsIdentical.ts diff --git a/packages/db-postgres/src/queries/createJSONQuery/convertPathToJSONTraversal.ts b/packages/db-postgres/src/queries/createJSONQuery/convertPathToJSONTraversal.ts index 1ab12bb94..b6c78d033 100644 --- a/packages/db-postgres/src/queries/createJSONQuery/convertPathToJSONTraversal.ts +++ b/packages/db-postgres/src/queries/createJSONQuery/convertPathToJSONTraversal.ts @@ -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) => { diff --git a/packages/db-postgres/src/queries/createJSONQuery/index.ts b/packages/db-postgres/src/queries/createJSONQuery/index.ts index 7cf283094..0c6868dc6 100644 --- a/packages/db-postgres/src/queries/createJSONQuery/index.ts +++ b/packages/db-postgres/src/queries/createJSONQuery/index.ts @@ -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}'` } diff --git a/packages/db-postgres/src/queries/getTableColumnFromPath.ts b/packages/db-postgres/src/queries/getTableColumnFromPath.ts index a87ea5190..4f2716a48 100644 --- a/packages/db-postgres/src/queries/getTableColumnFromPath.ts +++ b/packages/db-postgres/src/queries/getTableColumnFromPath.ts @@ -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, } } diff --git a/packages/db-postgres/src/queries/parseParams.ts b/packages/db-postgres/src/queries/parseParams.ts index 61870211d..068840cea 100644 --- a/packages/db-postgres/src/queries/parseParams.ts +++ b/packages/db-postgres/src/queries/parseParams.ts @@ -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}%'`)) } diff --git a/packages/db-postgres/src/schema/build.ts b/packages/db-postgres/src/schema/build.ts index c7851cf32..333a7fb77 100644 --- a/packages/db-postgres/src/schema/build.ts +++ b/packages/db-postgres/src/schema/build.ts @@ -29,6 +29,9 @@ type Args = { buildRelationships?: boolean disableUnique: boolean fields: Field[] + rootRelationsToBuild?: Map + 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) { diff --git a/packages/db-postgres/src/schema/traverseFields.ts b/packages/db-postgres/src/schema/traverseFields.ts index 7ef15d8ad..1095fd991 100644 --- a/packages/db-postgres/src/schema/traverseFields.ts +++ b/packages/db-postgres/src/schema/traverseFields.ts @@ -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 relationships: Set + rootRelationsToBuild?: Map + 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 = { _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> = { - _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 diff --git a/packages/db-postgres/src/schema/validateExistingBlockIsIdentical.ts b/packages/db-postgres/src/schema/validateExistingBlockIsIdentical.ts new file mode 100644 index 000000000..3f2ff8937 --- /dev/null +++ b/packages/db-postgres/src/schema/validateExistingBlockIsIdentical.ts @@ -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.`, + ) + } + } +} diff --git a/packages/db-postgres/src/transform/write/blocks.ts b/packages/db-postgres/src/transform/write/blocks.ts index a3a4de4ce..b03c9343c 100644 --- a/packages/db-postgres/src/transform/write/blocks.ts +++ b/packages/db-postgres/src/transform/write/blocks.ts @@ -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, diff --git a/packages/db-postgres/src/upsertRow/index.ts b/packages/db-postgres/src/upsertRow/index.ts index 32ab65030..af2f7c129 100644 --- a/packages/db-postgres/src/upsertRow/index.ts +++ b/packages/db-postgres/src/upsertRow/index.ts @@ -185,12 +185,12 @@ export const upsertRow = async ({ 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 ({ if (blockLocaleRowsToInsert.length > 0) { await db - .insert(adapter.tables[`${tableName}_${blockName}_locales`]) + .insert(adapter.tables[`${tableName}_blocks_${blockName}_locales`]) .values(blockLocaleRowsToInsert) .returning() } diff --git a/test/fields/collections/Blocks/index.ts b/test/fields/collections/Blocks/index.ts index 449930259..f1a92be6d 100644 --- a/test/fields/collections/Blocks/index.ts +++ b/test/fields/collections/Blocks/index.ts @@ -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 diff --git a/test/fields/collections/Tabs/index.ts b/test/fields/collections/Tabs/index.ts index a38b7b970..59c16d3cc 100644 --- a/test/fields/collections/Tabs/index.ts +++ b/test/fields/collections/Tabs/index.ts @@ -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, }, diff --git a/test/fields/int.spec.ts b/test/fields/int.spec.ts index 08785e73a..73b3517b8 100644 --- a/test/fields/int.spec.ts +++ b/test/fields/int.spec.ts @@ -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, ) })