diff --git a/package.json b/package.json index bbd4982993..481a673524 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "jest": "29.6.4", "jest-environment-jsdom": "29.6.4", "jwt-decode": "3.1.2", - "mongodb-memory-server": "8.13.0", + "mongodb-memory-server": "8.12.2", "node-fetch": "2.6.12", "prettier": "^3.0.3", "qs": "6.11.2", diff --git a/packages/payload/src/database/queryValidation/validateQueryPaths.ts b/packages/payload/src/database/queryValidation/validateQueryPaths.ts index bb34e12742..e7c4c00344 100644 --- a/packages/payload/src/database/queryValidation/validateQueryPaths.ts +++ b/packages/payload/src/database/queryValidation/validateQueryPaths.ts @@ -3,14 +3,13 @@ import type { SanitizedCollectionConfig } from '../../collections/config/types' import type { Field, FieldAffectingData } from '../../fields/config/types' import type { SanitizedGlobalConfig } from '../../globals/config/types' /* eslint-disable no-await-in-loop */ -import type { Operator, PayloadRequest, Where } from '../../types' +import type { Operator, PayloadRequest, Where, WhereField } from '../../types' import type { EntityPolicies } from './types' import QueryError from '../../errors/QueryError' import { validOperators } from '../../types/constants' import deepCopyObject from '../../utilities/deepCopyObject' import flattenFields from '../../utilities/flattenTopLevelFields' -import flattenWhereToOperators from '../flattenWhereToOperators' import { validateSearchParam } from './validateSearchParams' type Args = { @@ -30,6 +29,16 @@ type Args = { globalConfig: SanitizedGlobalConfig } ) + +const flattenWhere = (query: Where): WhereField[] => + Object.entries(query).reduce((flattenedConstraints, [key, val]) => { + if ((key === 'and' || key === 'or') && Array.isArray(val)) { + return [...flattenedConstraints, ...val.map((subVal) => flattenWhere(subVal))] + } + + return [...flattenedConstraints, { [key]: val }] + }, []) + export async function validateQueryPaths({ collectionConfig, errors = [], @@ -47,8 +56,7 @@ export async function validateQueryPaths({ versionFields || (globalConfig || collectionConfig).fields, ) as FieldAffectingData[] if (typeof where === 'object') { - // const flattenedWhere = flattenWhere(where); - const whereFields = flattenWhereToOperators(where) + const whereFields = flattenWhere(where) // We need to determine if the whereKey is an AND, OR, or a schema path const promises = [] whereFields.map(async (constraint) => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9c8b7a6fb5..515b958c8f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -66,26 +66,23 @@ importers: specifier: 3.1.2 version: 3.1.2 mongodb-memory-server: - specifier: 8.13.0 - version: 8.13.0 + specifier: 8.12.2 + version: 8.12.2 node-fetch: specifier: 2.6.12 version: 2.6.12 prettier: specifier: ^3.0.3 version: 3.0.3 -<<<<<<< HEAD qs: specifier: 6.11.2 version: 6.11.2 react: specifier: 18.2.0 version: 18.2.0 -======= rimraf: specifier: 3.0.2 version: 3.0.2 ->>>>>>> dd0514bd2cd312dbbc01ec8f018ff575bb93182f shelljs: specifier: 0.8.5 version: 0.8.5 @@ -10375,6 +10372,29 @@ packages: '@types/whatwg-url': 8.2.2 whatwg-url: 11.0.0 + /mongodb-memory-server-core@8.12.2: + resolution: {integrity: sha512-bls+lroejnhbZTOm5KHtxtf9PK6xASEAsCvZCPoXrNk1f10p0jDw7Xb4GUqVi0ZuVmuLZBNgmzYeHmb3WUgvLg==} + engines: {node: '>=12.22.0'} + dependencies: + async-mutex: 0.3.2 + camelcase: 6.3.0 + debug: 4.3.4 + find-cache-dir: 3.3.2 + get-port: 5.1.1 + https-proxy-agent: 5.0.1 + md5-file: 5.0.0 + mongodb: 4.17.1 + new-find-package-json: 2.0.0 + semver: 7.5.4 + tar-stream: 2.2.0 + tslib: 2.6.2 + uuid: 9.0.0 + yauzl: 2.10.0 + transitivePeerDependencies: + - aws-crt + - supports-color + dev: true + /mongodb-memory-server-core@8.13.0: resolution: {integrity: sha512-4NTOzYOlRUilwb8CxOKix/XbZmac4cLpmEU03eaHx90lgEp+ARZM2PQtIOEg3nhHo97r9THIEv6Gs4LECokp0Q==} engines: {node: '>=12.22.0'} @@ -10398,6 +10418,18 @@ packages: - supports-color dev: true + /mongodb-memory-server@8.12.2: + resolution: {integrity: sha512-WM3uJnKWqhJxu3LlHfvRXRrhc+kiEGdGDHMrAG0N1E2fWbRlvSnUKau7Jdcf7cIA5HlRC/K8uVe0DCym45KfAA==} + engines: {node: '>=12.22.0'} + requiresBuild: true + dependencies: + mongodb-memory-server-core: 8.12.2 + tslib: 2.6.2 + transitivePeerDependencies: + - aws-crt + - supports-color + dev: true + /mongodb-memory-server@8.13.0: resolution: {integrity: sha512-CyrKMwEmRePn8iQ3LtWQiOJxlGK0eM+NNTq3Yg8m7gaywepFu24mF7s13q87Kfuq0WgBuCJQ4t6VcUZJ4m+KWQ==} engines: {node: '>=12.22.0'} diff --git a/test/collections-graphql/int.spec.ts b/test/collections-graphql/int.spec.ts index c231b2007c..7c1c447f19 100644 --- a/test/collections-graphql/int.spec.ts +++ b/test/collections-graphql/int.spec.ts @@ -17,6 +17,15 @@ describe('collections-graphql', () => { const config = await configPromise const url = `${serverURL}${config.routes.api}${config.routes.graphQL}` client = new GraphQLClient(url) + + // Wait for indexes to be created, + // as we need them to query by point + await new Promise((resolve, reject) => { + payload.db.collections.point.ensureIndexes(function (err) { + if (err) reject(err) + resolve(true) + }) + }) }) afterAll(async () => { @@ -173,12 +182,14 @@ describe('collections-graphql', () => { } }` - const response = await client.request(query); - const { docs } = response.Posts; - const docsWithWhereTitleNotEqualPostTitle = docs.filter((post) => post.title === post1.title); + const response = await client.request(query) + const { docs } = response.Posts + const docsWithWhereTitleNotEqualPostTitle = docs.filter( + (post) => post.title === post1.title, + ) - expect(docsWithWhereTitleNotEqualPostTitle).toHaveLength(0); - }); + expect(docsWithWhereTitleNotEqualPostTitle).toHaveLength(0) + }) it('like', async () => { const postWithWords = await createPost({ title: 'the quick brown fox' }) @@ -271,10 +282,10 @@ describe('collections-graphql', () => { } }` - const response = await client.request(query); - const { docs } = response.Posts; - expect(docs.map(({ id }) => id)).toContain(numPost2.id); - }); + const response = await client.request(query) + const { docs } = response.Posts + expect(docs.map(({ id }) => id)).toContain(numPost2.id) + }) it('greater_than_equal', async () => { const query = `query { diff --git a/test/collections-rest/int.spec.ts b/test/collections-rest/int.spec.ts index 78e6d15111..b95ead84c5 100644 --- a/test/collections-rest/int.spec.ts +++ b/test/collections-rest/int.spec.ts @@ -22,6 +22,15 @@ describe('collections-rest', () => { beforeAll(async () => { const { serverURL } = await initPayloadTest({ __dirname, init: { local: false } }) client = new RESTClient(await config, { serverURL, defaultSlug: slug }) + + // Wait for indexes to be created, + // as we need them to query by point + await new Promise((resolve, reject) => { + payload.db.collections[pointSlug].ensureIndexes(function (err) { + if (err) reject(err) + resolve(true) + }) + }) }) afterAll(async () => { @@ -245,7 +254,7 @@ describe('collections-rest', () => { const { status, docs } = await client.deleteMany({ where: { title: { equals: 'title' } }, - }); + }) expect(status).toEqual(200) expect(docs[0].title).toEqual('title') // Check was not modified @@ -306,17 +315,29 @@ describe('collections-rest', () => { }) it('should query - equals', async () => { - const customId = `custom-${randomBytes(32).toString('hex').slice(0, 12)}`; - const { doc } = await client.create({ slug: customIdSlug, data: { id: customId, name: 'custom-id-name' } }); - const { result } = await client.find({ slug: customIdSlug, query: { id: { equals: customId } } }); + const customId = `custom-${randomBytes(32).toString('hex').slice(0, 12)}` + const { doc } = await client.create({ + slug: customIdSlug, + data: { id: customId, name: 'custom-id-name' }, + }) + const { result } = await client.find({ + slug: customIdSlug, + query: { id: { equals: customId } }, + }) - expect(result.docs.map(({ id }) => id)).toContain(doc.id); - }); + expect(result.docs.map(({ id }) => id)).toContain(doc.id) + }) it('should query - like', async () => { - const customId = `custom-${randomBytes(32).toString('hex').slice(0, 12)}`; - const { doc } = await client.create({ slug: customIdSlug, data: { id: customId, name: 'custom-id-name' } }); - const { result } = await client.find({ slug: customIdSlug, query: { id: { like: 'custom' } } }); + const customId = `custom-${randomBytes(32).toString('hex').slice(0, 12)}` + const { doc } = await client.create({ + slug: customIdSlug, + data: { id: customId, name: 'custom-id-name' }, + }) + const { result } = await client.find({ + slug: customIdSlug, + query: { id: { like: 'custom' } }, + }) expect(result.docs.map(({ id }) => id)).toContain(doc.id) })