fix(next): removes reliance on instanceof from api error formatting (#5482)
This commit is contained in:
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@@ -10,7 +10,7 @@
|
|||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "pnpm run dev _community -- --no-turbo",
|
"command": "node --no-deprecation test/dev.js fields",
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"name": "Run Dev Community",
|
"name": "Run Dev Community",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export const updateOne: UpdateOne = async function updateOne(
|
|||||||
})
|
})
|
||||||
|
|
||||||
let result
|
let result
|
||||||
|
|
||||||
try {
|
try {
|
||||||
result = await Model.findOneAndUpdate(query, data, options)
|
result = await Model.findOneAndUpdate(query, data, options)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import path from 'path'
|
|||||||
import { APIError } from 'payload/errors'
|
import { APIError } from 'payload/errors'
|
||||||
|
|
||||||
import { streamFile } from '../../../next-stream-file/index.js'
|
import { streamFile } from '../../../next-stream-file/index.js'
|
||||||
import { RouteError } from '../RouteError.js'
|
import { routeError } from '../routeError.js'
|
||||||
import { checkFileAccess } from './checkFileAccess.js'
|
import { checkFileAccess } from './checkFileAccess.js'
|
||||||
|
|
||||||
// /:collectionSlug/file/:filename
|
// /:collectionSlug/file/:filename
|
||||||
@@ -64,7 +64,7 @@ export const getFile = async ({ collection, filename, req }: Args): Promise<Resp
|
|||||||
status: httpStatus.OK,
|
status: httpStatus.OK,
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return RouteError({
|
return routeError({
|
||||||
collection,
|
collection,
|
||||||
err: error,
|
err: error,
|
||||||
req,
|
req,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import type {
|
|||||||
} from './types.js'
|
} from './types.js'
|
||||||
|
|
||||||
import { createPayloadRequest } from '../../utilities/createPayloadRequest.js'
|
import { createPayloadRequest } from '../../utilities/createPayloadRequest.js'
|
||||||
import { RouteError } from './RouteError.js'
|
import { routeError } from './routeError.js'
|
||||||
import { access } from './auth/access.js'
|
import { access } from './auth/access.js'
|
||||||
import { forgotPassword } from './auth/forgotPassword.js'
|
import { forgotPassword } from './auth/forgotPassword.js'
|
||||||
import { init } from './auth/init.js'
|
import { init } from './auth/init.js'
|
||||||
@@ -281,7 +281,7 @@ export const GET =
|
|||||||
|
|
||||||
return RouteNotFoundResponse(slug)
|
return RouteNotFoundResponse(slug)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return RouteError({
|
return routeError({
|
||||||
collection,
|
collection,
|
||||||
err: error,
|
err: error,
|
||||||
req,
|
req,
|
||||||
@@ -423,7 +423,7 @@ export const POST =
|
|||||||
|
|
||||||
return RouteNotFoundResponse(slug)
|
return RouteNotFoundResponse(slug)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return RouteError({
|
return routeError({
|
||||||
collection,
|
collection,
|
||||||
err: error,
|
err: error,
|
||||||
req,
|
req,
|
||||||
@@ -492,7 +492,7 @@ export const DELETE =
|
|||||||
|
|
||||||
return RouteNotFoundResponse(slug)
|
return RouteNotFoundResponse(slug)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return RouteError({
|
return routeError({
|
||||||
collection,
|
collection,
|
||||||
err: error,
|
err: error,
|
||||||
req,
|
req,
|
||||||
@@ -561,7 +561,7 @@ export const PATCH =
|
|||||||
|
|
||||||
return RouteNotFoundResponse(slug)
|
return RouteNotFoundResponse(slug)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return RouteError({
|
return routeError({
|
||||||
collection,
|
collection,
|
||||||
err: error,
|
err: error,
|
||||||
req,
|
req,
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
import type { Collection, PayloadRequest } from 'payload/types'
|
import type { Collection, PayloadRequest } from 'payload/types'
|
||||||
|
|
||||||
import httpStatus from 'http-status'
|
import httpStatus from 'http-status'
|
||||||
import { APIError } from 'payload/errors'
|
import { APIError, ValidationError } from 'payload/errors'
|
||||||
|
|
||||||
export type ErrorResponse = { data?: any; errors: unknown[]; stack?: string }
|
export type ErrorResponse = { data?: any; errors: unknown[]; stack?: string }
|
||||||
|
|
||||||
const formatErrors = (incoming: { [key: string]: unknown } | APIError | Error): ErrorResponse => {
|
const formatErrors = (incoming: { [key: string]: unknown } | APIError): ErrorResponse => {
|
||||||
if (incoming) {
|
if (incoming) {
|
||||||
if (incoming instanceof APIError && incoming.data) {
|
// Cannot use `instanceof` to check error type: https://github.com/microsoft/TypeScript/issues/13965
|
||||||
|
// Instead, get the prototype of the incoming error and check its constructor name
|
||||||
|
const proto = Object.getPrototypeOf(incoming)
|
||||||
|
|
||||||
|
// Payload 'ValidationError' and 'APIError'
|
||||||
|
if (
|
||||||
|
(proto.constructor.name === 'ValidationError' || proto.constructor.name === 'APIError') &&
|
||||||
|
incoming.data
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
errors: [
|
errors: [
|
||||||
{
|
{
|
||||||
@@ -19,8 +27,8 @@ const formatErrors = (incoming: { [key: string]: unknown } | APIError | Error):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mongoose
|
// Mongoose 'ValidationError': https://mongoosejs.com/docs/api/error.html#Error.ValidationError
|
||||||
if (!(incoming instanceof APIError || incoming instanceof Error) && incoming.errors) {
|
if (proto.constructor.name === 'ValidationError' && 'errors' in incoming && incoming.errors) {
|
||||||
return {
|
return {
|
||||||
errors: Object.keys(incoming.errors).reduce((acc, key) => {
|
errors: Object.keys(incoming.errors).reduce((acc, key) => {
|
||||||
acc.push({
|
acc.push({
|
||||||
@@ -58,7 +66,7 @@ const formatErrors = (incoming: { [key: string]: unknown } | APIError | Error):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const RouteError = async ({
|
export const routeError = async ({
|
||||||
collection,
|
collection,
|
||||||
err,
|
err,
|
||||||
req,
|
req,
|
||||||
@@ -78,7 +86,9 @@ export const RouteError = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { config, logger } = req.payload
|
const { config, logger } = req.payload
|
||||||
|
|
||||||
let response = formatErrors(err)
|
let response = formatErrors(err)
|
||||||
|
|
||||||
let status = err.status || httpStatus.INTERNAL_SERVER_ERROR
|
let status = err.status || httpStatus.INTERNAL_SERVER_ERROR
|
||||||
|
|
||||||
logger.error(err.stack)
|
logger.error(err.stack)
|
||||||
@@ -11,7 +11,11 @@
|
|||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
@@ -19,7 +23,11 @@
|
|||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"strict": false,
|
"strict": false,
|
||||||
"types": ["jest", "node", "@types/jest"],
|
"types": [
|
||||||
|
"jest",
|
||||||
|
"node",
|
||||||
|
"@types/jest"
|
||||||
|
],
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"plugins": [
|
"plugins": [
|
||||||
@@ -28,26 +36,65 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@payload-config": ["./test/admin/config.ts"],
|
"@payload-config": [
|
||||||
"@payloadcms/live-preview": ["./packages/live-preview/src"],
|
"./test/fields/config.ts"
|
||||||
"@payloadcms/live-preview-react": ["./packages/live-preview-react/src/index.ts"],
|
],
|
||||||
"@payloadcms/ui/assets": ["./packages/ui/src/assets/index.ts"],
|
"@payloadcms/live-preview": [
|
||||||
"@payloadcms/ui/elements/*": ["./packages/ui/src/elements/*/index.tsx"],
|
"./packages/live-preview/src"
|
||||||
"@payloadcms/ui/fields/*": ["./packages/ui/src/fields/*/index.tsx"],
|
],
|
||||||
"@payloadcms/ui/forms/*": ["./packages/ui/src/forms/*/index.tsx"],
|
"@payloadcms/live-preview-react": [
|
||||||
"@payloadcms/ui/graphics/*": ["./packages/ui/src/graphics/*/index.tsx"],
|
"./packages/live-preview-react/src/index.ts"
|
||||||
"@payloadcms/ui/hooks/*": ["./packages/ui/src/hooks/*.ts"],
|
],
|
||||||
"@payloadcms/ui/icons/*": ["./packages/ui/src/icons/*/index.tsx"],
|
"@payloadcms/ui/assets": [
|
||||||
"@payloadcms/ui/providers/*": ["./packages/ui/src/providers/*/index.tsx"],
|
"./packages/ui/src/assets/index.ts"
|
||||||
"@payloadcms/ui/templates/*": ["./packages/ui/src/templates/*/index.tsx"],
|
],
|
||||||
"@payloadcms/ui/utilities/*": ["./packages/ui/src/utilities/*.ts"],
|
"@payloadcms/ui/elements/*": [
|
||||||
"@payloadcms/ui/scss": ["./packages/ui/src/scss.scss"],
|
"./packages/ui/src/elements/*/index.tsx"
|
||||||
"@payloadcms/ui/scss/app.scss": ["./packages/ui/src/scss/app.scss"],
|
],
|
||||||
"@payloadcms/next/*": ["./packages/next/src/*"],
|
"@payloadcms/ui/fields/*": [
|
||||||
"@payloadcms/next": ["./packages/next/src/exports/*"]
|
"./packages/ui/src/fields/*/index.tsx"
|
||||||
|
],
|
||||||
|
"@payloadcms/ui/forms/*": [
|
||||||
|
"./packages/ui/src/forms/*/index.tsx"
|
||||||
|
],
|
||||||
|
"@payloadcms/ui/graphics/*": [
|
||||||
|
"./packages/ui/src/graphics/*/index.tsx"
|
||||||
|
],
|
||||||
|
"@payloadcms/ui/hooks/*": [
|
||||||
|
"./packages/ui/src/hooks/*.ts"
|
||||||
|
],
|
||||||
|
"@payloadcms/ui/icons/*": [
|
||||||
|
"./packages/ui/src/icons/*/index.tsx"
|
||||||
|
],
|
||||||
|
"@payloadcms/ui/providers/*": [
|
||||||
|
"./packages/ui/src/providers/*/index.tsx"
|
||||||
|
],
|
||||||
|
"@payloadcms/ui/templates/*": [
|
||||||
|
"./packages/ui/src/templates/*/index.tsx"
|
||||||
|
],
|
||||||
|
"@payloadcms/ui/utilities/*": [
|
||||||
|
"./packages/ui/src/utilities/*.ts"
|
||||||
|
],
|
||||||
|
"@payloadcms/ui/scss": [
|
||||||
|
"./packages/ui/src/scss.scss"
|
||||||
|
],
|
||||||
|
"@payloadcms/ui/scss/app.scss": [
|
||||||
|
"./packages/ui/src/scss/app.scss"
|
||||||
|
],
|
||||||
|
"@payloadcms/next/*": [
|
||||||
|
"./packages/next/src/*"
|
||||||
|
],
|
||||||
|
"@payloadcms/next": [
|
||||||
|
"./packages/next/src/exports/*"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"exclude": ["dist", "build", "temp", "node_modules"],
|
"exclude": [
|
||||||
|
"dist",
|
||||||
|
"build",
|
||||||
|
"temp",
|
||||||
|
"node_modules"
|
||||||
|
],
|
||||||
"composite": true,
|
"composite": true,
|
||||||
"references": [
|
"references": [
|
||||||
{
|
{
|
||||||
@@ -108,5 +155,9 @@
|
|||||||
"path": "./packages/ui"
|
"path": "./packages/ui"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"include": ["next-env.d.ts", ".next/types/**/*.ts", "scripts/**/*.ts"]
|
"include": [
|
||||||
}
|
"next-env.d.ts",
|
||||||
|
".next/types/**/*.ts",
|
||||||
|
"scripts/**/*.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user