From 28c404630010ee35ab9d74e3c2089201f9c98913 Mon Sep 17 00:00:00 2001 From: Jarrod Flesch Date: Fri, 16 Feb 2024 15:09:51 -0500 Subject: [PATCH] chore: more passing int suites --- .../[collection]/file/[filename]/route.ts | 4 + .../next/src/routes/rest/checkEndpoints.ts | 21 +++++ packages/next/src/routes/rest/index.ts | 81 ++++++++++++++++-- test/buildConfigWithDefaults.ts | 1 + test/config/int.spec.ts | 12 ++- test/custom-graphql/int.spec.ts | 35 +++++--- test/database/int.spec.ts | 30 +++---- test/dataloader/int.spec.ts | 59 +++++++------ test/endpoints/int.spec.ts | 85 +++++++++---------- 9 files changed, 215 insertions(+), 113 deletions(-) create mode 100644 packages/next/src/routes/rest/checkEndpoints.ts diff --git a/packages/next/src/routes/rest/[collection]/file/[filename]/route.ts b/packages/next/src/routes/rest/[collection]/file/[filename]/route.ts index 640cc2da17..8c08ad5de7 100644 --- a/packages/next/src/routes/rest/[collection]/file/[filename]/route.ts +++ b/packages/next/src/routes/rest/[collection]/file/[filename]/route.ts @@ -7,6 +7,7 @@ import { APIError, Forbidden } from 'payload/errors' import { RouteError } from '../../../RouteError' import { createPayloadRequest } from '../../../../../utilities/createPayloadRequest' import httpStatus from 'http-status' +import { endpointsAreDisabled } from '../../../checkEndpoints' async function checkFileAccess({ req, @@ -18,6 +19,9 @@ async function checkFileAccess({ collection: Collection }) { const { config } = collection + const disableEndpoints = endpointsAreDisabled({ request: req, endpoints: config.endpoints }) + if (disableEndpoints) return disableEndpoints + const accessResult = await executeAccess({ isReadingStaticFile: true, req }, config.access.read) if (typeof accessResult === 'object') { diff --git a/packages/next/src/routes/rest/checkEndpoints.ts b/packages/next/src/routes/rest/checkEndpoints.ts new file mode 100644 index 0000000000..b734ed557f --- /dev/null +++ b/packages/next/src/routes/rest/checkEndpoints.ts @@ -0,0 +1,21 @@ +import httpStatus from 'http-status' +import { CollectionConfig, GlobalConfig } from 'payload/types' + +export const endpointsAreDisabled = ({ + request, + endpoints, +}: { + request: Partial + endpoints: unknown[] | false +}) => { + if (!endpoints) { + return Response.json( + { + message: `Cannot ${request.method.toUpperCase()} ${request.url}`, + }, + { + status: httpStatus.NOT_IMPLEMENTED, + }, + ) + } +} diff --git a/packages/next/src/routes/rest/index.ts b/packages/next/src/routes/rest/index.ts index 1ac87d67ec..cca841ad07 100644 --- a/packages/next/src/routes/rest/index.ts +++ b/packages/next/src/routes/rest/index.ts @@ -11,6 +11,9 @@ import { GlobalRouteHandlerWithID, } from './types' +import { RouteError } from './RouteError' +import { endpointsAreDisabled } from './checkEndpoints' + import { me } from './auth/me' import { init } from './auth/init' import { login } from './auth/login' @@ -41,7 +44,6 @@ import { docAccess as docAccessGlobal } from './globals/docAccess' import { findVersions as findVersionsGlobal } from './globals/findVersions' import { restoreVersion as restoreVersionGlobal } from './globals/restoreVersion' import { findVersionByID as findVersionByIdGlobal } from './globals/findVersionByID' -import { RouteError } from './RouteError' const endpoints = { root: { @@ -161,13 +163,25 @@ export const GET = }, }) + const disableEndpoints = endpointsAreDisabled({ + request, + endpoints: req.payload.config.endpoints, + }) + if (disableEndpoints) return disableEndpoints + collection = req.payload.collections?.[slug1] if (collection) { + const disableEndpoints = endpointsAreDisabled({ + request, + endpoints: collection.config.endpoints, + }) + if (disableEndpoints) return disableEndpoints + const customEndpointResponse = await handleCustomEndpoints({ entitySlug: slug1, payloadRequest: req, - endpoints: collection.config?.endpoints || [], + endpoints: collection.config.endpoints, }) if (customEndpointResponse) return customEndpointResponse @@ -202,10 +216,17 @@ export const GET = } } else if (slug1 === 'globals') { const globalConfig = req.payload.config.globals.find((global) => global.slug === slug2) + + const disableEndpoints = endpointsAreDisabled({ + request, + endpoints: globalConfig.endpoints, + }) + if (disableEndpoints) return disableEndpoints + const customEndpointResponse = await handleCustomEndpoints({ entitySlug: `${slug1}/${slug2}`, payloadRequest: req, - endpoints: globalConfig?.endpoints || [], + endpoints: globalConfig.endpoints, }) if (customEndpointResponse) return customEndpointResponse @@ -276,11 +297,23 @@ export const POST = }) collection = req.payload.collections?.[slug1] + const disableEndpoints = endpointsAreDisabled({ + request, + endpoints: req.payload.config.endpoints, + }) + if (disableEndpoints) return disableEndpoints + if (collection) { + const disableEndpoints = endpointsAreDisabled({ + request, + endpoints: collection.config.endpoints, + }) + if (disableEndpoints) return disableEndpoints + const customEndpointResponse = await handleCustomEndpoints({ entitySlug: slug1, payloadRequest: req, - endpoints: collection.config?.endpoints || [], + endpoints: collection.config.endpoints, }) if (customEndpointResponse) return customEndpointResponse @@ -318,6 +351,18 @@ export const POST = } } else if (slug1 === 'globals' && slug2) { const globalConfig = req.payload.config.globals.find((global) => global.slug === slug2) + const disableEndpoints = endpointsAreDisabled({ + request, + endpoints: globalConfig.endpoints, + }) + if (disableEndpoints) return disableEndpoints + + const customEndpointResponse = await handleCustomEndpoints({ + entitySlug: `${slug1}/${slug2}`, + payloadRequest: req, + endpoints: globalConfig.endpoints, + }) + if (customEndpointResponse) return customEndpointResponse switch (slug.length) { case 2: @@ -387,11 +432,23 @@ export const DELETE = }) collection = req.payload.collections?.[slug1] + const disableEndpoints = endpointsAreDisabled({ + request, + endpoints: req.payload.config.endpoints, + }) + if (disableEndpoints) return disableEndpoints + if (collection) { + const disableEndpoints = endpointsAreDisabled({ + request, + endpoints: collection.config.endpoints, + }) + if (disableEndpoints) return disableEndpoints + const customEndpointResponse = await handleCustomEndpoints({ entitySlug: slug1, payloadRequest: req, - endpoints: collection.config?.endpoints || [], + endpoints: collection.config.endpoints, }) if (customEndpointResponse) return customEndpointResponse @@ -444,11 +501,23 @@ export const PATCH = }) collection = req.payload.collections?.[slug1] + const disableEndpoints = endpointsAreDisabled({ + request, + endpoints: req.payload.config.endpoints, + }) + if (disableEndpoints) return disableEndpoints + if (collection) { + const disableEndpoints = endpointsAreDisabled({ + request, + endpoints: collection.config.endpoints, + }) + if (disableEndpoints) return disableEndpoints + const customEndpointResponse = await handleCustomEndpoints({ entitySlug: slug1, payloadRequest: req, - endpoints: collection.config?.endpoints || [], + endpoints: collection.config.endpoints, }) if (customEndpointResponse) return customEndpointResponse diff --git a/test/buildConfigWithDefaults.ts b/test/buildConfigWithDefaults.ts index 84380ef731..a13af28a14 100644 --- a/test/buildConfigWithDefaults.ts +++ b/test/buildConfigWithDefaults.ts @@ -25,6 +25,7 @@ const databaseAdapters = { export function buildConfigWithDefaults(testConfig?: Partial): Promise { const config: Config = { secret: 'TEST_SECRET', + // editor: slateEditor({}), editor: undefined, rateLimit: { max: 9999999999, diff --git a/test/config/int.spec.ts b/test/config/int.spec.ts index 89c35f5c06..63ba65f29d 100644 --- a/test/config/int.spec.ts +++ b/test/config/int.spec.ts @@ -1,11 +1,15 @@ -import payload from '../../packages/payload/src' -import { initPayloadTest } from '../helpers/configHelpers' +import type { Payload } from '../../packages/payload/src' -require('isomorphic-fetch') +import { getPayload } from '../../packages/payload/src' +import { startMemoryDB } from '../startMemoryDB' +import configPromise from './config' + +let payload: Payload describe('Config', () => { beforeAll(async () => { - await initPayloadTest({ __dirname, init: { local: true } }) + const config = await startMemoryDB(configPromise) + payload = await getPayload({ config }) }) describe('payload config', () => { diff --git a/test/custom-graphql/int.spec.ts b/test/custom-graphql/int.spec.ts index 6b3b09eb25..c1a1a0c371 100644 --- a/test/custom-graphql/int.spec.ts +++ b/test/custom-graphql/int.spec.ts @@ -1,16 +1,15 @@ -import { GraphQLClient } from 'graphql-request' - -import { initPayloadTest } from '../helpers/configHelpers' +import { getPayload } from '../../packages/payload/src' +import { NextRESTClient } from '../helpers/NextRESTClient' +import { startMemoryDB } from '../startMemoryDB' import configPromise from './config' -let client: GraphQLClient +let restClient: NextRESTClient describe('Custom GraphQL', () => { beforeAll(async () => { - const { serverURL } = await initPayloadTest({ __dirname, init: { local: false } }) - const config = await configPromise - const url = `${serverURL}${config.routes.api}${config.routes.graphQL}` - client = new GraphQLClient(url) + const config = await startMemoryDB(configPromise) + const payload = await getPayload({ config }) + restClient = new NextRESTClient(payload.config) }) describe('Isolated Transaction ID', () => { @@ -19,11 +18,15 @@ describe('Custom GraphQL', () => { TransactionID1 TransactionID2 }` - const response = await client.request(query) + const { data } = await restClient + .GRAPHQL_POST({ + body: JSON.stringify({ query }), + }) + .then((res) => res.json()) // either no transactions at all or they are different expect( - (response.TransactionID2 === null && response.TransactionID1 === null) || - response.TransactionID2 !== response.TransactionID1, + (data.TransactionID2 === null && data.TransactionID1 === null) || + data.TransactionID2 !== data.TransactionID1, ).toBe(true) }) it('should isolate transaction IDs between mutations in the same request', async () => { @@ -31,11 +34,15 @@ describe('Custom GraphQL', () => { MutateTransactionID1 MutateTransactionID2 }` - const response = await client.request(query) + const { data } = await restClient + .GRAPHQL_POST({ + body: JSON.stringify({ query }), + }) + .then((res) => res.json()) // either no transactions at all or they are different expect( - (response.MutateTransactionID2 === null && response.MutateTransactionID1 === null) || - response.MutateTransactionID2 !== response.MutateTransactionID1, + (data.MutateTransactionID2 === null && data.MutateTransactionID1 === null) || + data.MutateTransactionID2 !== data.MutateTransactionID1, ).toBe(true) }) }) diff --git a/test/database/int.spec.ts b/test/database/int.spec.ts index c9fa264191..52e4988373 100644 --- a/test/database/int.spec.ts +++ b/test/database/int.spec.ts @@ -1,28 +1,25 @@ -import { GraphQLClient } from 'graphql-request' - +import type { Payload } from '../../packages/payload/src' import type { TypeWithID } from '../../packages/payload/src/collections/config/types' import type { PayloadRequest } from '../../packages/payload/src/types' -import payload from '../../packages/payload/src' +import { getPayload } from '../../packages/payload/src' import { commitTransaction } from '../../packages/payload/src/utilities/commitTransaction' import { initTransaction } from '../../packages/payload/src/utilities/initTransaction' import { devUser } from '../credentials' -import { initPayloadTest } from '../helpers/configHelpers' +import { startMemoryDB } from '../startMemoryDB' +import configPromise from './config' + +let payload: Payload +let user: TypeWithID & Record +let useTransactions = true +const collection = 'posts' +const title = 'title' describe('database', () => { - let serverURL - let client: GraphQLClient - let token: string - const collection = 'posts' - const title = 'title' - let user: TypeWithID & Record - let useTransactions = true - beforeAll(async () => { - const init = await initPayloadTest({ __dirname, init: { local: false } }) - serverURL = init.serverURL - const url = `${serverURL}/api/graphql` - client = new GraphQLClient(url) + const config = await startMemoryDB(configPromise) + payload = await getPayload({ config }) + if (payload.db.name === 'mongoose') { useTransactions = false } @@ -35,7 +32,6 @@ describe('database', () => { }, }) - if (loginResult.token) token = loginResult.token user = loginResult.user }) diff --git a/test/dataloader/int.spec.ts b/test/dataloader/int.spec.ts index 523a7cfcba..812bf4bb3f 100644 --- a/test/dataloader/int.spec.ts +++ b/test/dataloader/int.spec.ts @@ -1,36 +1,34 @@ -import { GraphQLClient } from 'graphql-request' +import type { Payload } from '../../packages/payload/src' -import payload from '../../packages/payload/src' +import { getPayload } from '../../packages/payload/src' import { devUser } from '../credentials' -import { initPayloadTest } from '../helpers/configHelpers' +import { NextRESTClient } from '../helpers/NextRESTClient' +import { startMemoryDB } from '../startMemoryDB' +import configPromise from './config' import { postDoc } from './config' +let restClient: NextRESTClient +let payload: Payload +let token: string + describe('dataloader', () => { - let serverURL beforeAll(async () => { - const init = await initPayloadTest({ __dirname, init: { local: false } }) - serverURL = init.serverURL + const config = await startMemoryDB(configPromise) + payload = await getPayload({ config }) + restClient = new NextRESTClient(payload.config) + + const loginResult = await payload.login({ + collection: 'users', + data: { + email: devUser.email, + password: devUser.password, + }, + }) + + if (loginResult.token) token = loginResult.token }) describe('graphql', () => { - let client: GraphQLClient - let token: string - - beforeAll(async () => { - const url = `${serverURL}/api/graphql` - client = new GraphQLClient(url) - - const loginResult = await payload.login({ - collection: 'users', - data: { - email: devUser.email, - password: devUser.password, - }, - }) - - if (loginResult.token) token = loginResult.token - }) - it('should allow querying via graphql', async () => { const query = `query { Posts { @@ -43,11 +41,16 @@ describe('dataloader', () => { } }` - const response = await client.request(query, null, { - Authorization: `JWT ${token}`, - }) + const { data } = await restClient + .GRAPHQL_POST({ + body: JSON.stringify({ query }), + headers: { + Authorization: `JWT ${token}`, + }, + }) + .then((res) => res.json()) - const { docs } = response.Posts + const { docs } = data.Posts expect(docs[0].title).toStrictEqual(postDoc.title) }) diff --git a/test/endpoints/int.spec.ts b/test/endpoints/int.spec.ts index d429579076..0a823c81ab 100644 --- a/test/endpoints/int.spec.ts +++ b/test/endpoints/int.spec.ts @@ -1,5 +1,7 @@ -import { initPayloadTest } from '../helpers/configHelpers' -import { RESTClient } from '../helpers/rest' +import { getPayload } from '../../packages/payload/src' +import { NextRESTClient } from '../helpers/NextRESTClient' +import { startMemoryDB } from '../startMemoryDB' +import configPromise from './config' import { applicationEndpoint, collectionSlug, @@ -10,90 +12,82 @@ import { rootEndpoint, } from './shared' -require('isomorphic-fetch') - -let client: RESTClient +let restClient: NextRESTClient describe('Endpoints', () => { beforeAll(async () => { - const config = await initPayloadTest({ __dirname, init: { local: false } }) - const { serverURL } = config - client = new RESTClient(config, { serverURL, defaultSlug: collectionSlug }) + const config = await startMemoryDB(configPromise) + await getPayload({ config }) + restClient = new NextRESTClient(config) }) describe('Collections', () => { it('should GET a static endpoint', async () => { - const { status, data } = await client.endpoint(`/api/${collectionSlug}/say-hello/joe-bloggs`) - expect(status).toBe(200) + const response = await restClient.GET(`/${collectionSlug}/say-hello/joe-bloggs`) + const data = await response.json() + expect(response.status).toBe(200) expect(data.message).toStrictEqual('Hey Joey!') }) it('should GET an endpoint with a parameter', async () => { const name = 'George' - const { status, data } = await client.endpoint(`/api/${collectionSlug}/say-hello/${name}`) - expect(status).toBe(200) + const response = await restClient.GET(`/${collectionSlug}/say-hello/${name}`) + const data = await response.json() + expect(response.status).toBe(200) expect(data.message).toStrictEqual(`Hello ${name}!`) }) it('should POST an endpoint with data', async () => { const params = { name: 'George', age: 29 } - const { status, data } = await client.endpoint( - `/api/${collectionSlug}/whoami`, - 'post', - params, - ) - expect(status).toBe(200) + const response = await restClient.POST(`/${collectionSlug}/whoami`, { + body: JSON.stringify(params), + }) + const data = await response.json() + expect(response.status).toBe(200) expect(data.name).toStrictEqual(params.name) expect(data.age).toStrictEqual(params.age) }) it('should disable built-in endpoints when false', async () => { - let result - try { - result = await client.endpoint(`/api/${noEndpointsCollectionSlug}`, 'get') - } catch (err: unknown) { - result = err - } - expect(result instanceof Error).toBe(true) + const response = await restClient.GET(`/${noEndpointsCollectionSlug}`) + expect(response.status).toBe(501) }) }) describe('Globals', () => { it('should call custom endpoint', async () => { const params = { globals: 'response' } - const { status, data } = await client.endpoint( - `/api/globals/${globalSlug}/${globalEndpoint}`, - 'post', - params, - ) + const response = await restClient.POST(`/globals/${globalSlug}/${globalEndpoint}`, { + body: JSON.stringify(params), + }) + const data = await response.json() - expect(status).toBe(200) + expect(response.status).toBe(200) expect(params).toMatchObject(data) }) it('should disable built-in endpoints when false', async () => { - let result - try { - result = await client.endpoint(`/api/globals/${noEndpointsGlobalSlug}`, 'get') - } catch (err: unknown) { - result = err - } - expect(result instanceof Error).toBe(true) + const response = await restClient.GET(`/globals/${noEndpointsGlobalSlug}`) + expect(response.status).toBe(501) }) }) describe('API', () => { it('should call custom endpoint', async () => { const params = { app: 'response' } - const { status, data } = await client.endpoint(`/api/${applicationEndpoint}`, 'post', params) + const response = await restClient.POST(`/${applicationEndpoint}`, { + body: JSON.stringify(params), + }) + const data = await response.json() - expect(status).toBe(200) + expect(response.status).toBe(200) expect(params).toMatchObject(data) }) it('should have i18n on req', async () => { - const { status, data } = await client.endpoint(`/api/${applicationEndpoint}/i18n`, 'get') + const response = await restClient.GET(`/${applicationEndpoint}/i18n`) + const data = await response.json() - expect(status).toBe(200) + expect(response.status).toBe(200) expect(data.message).toStrictEqual('Back to Dashboard') }) }) @@ -101,9 +95,12 @@ describe('Endpoints', () => { describe('Root', () => { it('should call custom root endpoint', async () => { const params = { root: 'response' } - const { status, data } = await client.endpoint(`/${rootEndpoint}`, 'post', params) + const response = await restClient.POST(`/${rootEndpoint}`, { + body: JSON.stringify(params), + }) + const data = await response.json() - expect(status).toBe(200) + expect(response.status).toBe(200) expect(params).toMatchObject(data) }) })