diff --git a/packages/server/src/openapi/index.ts b/packages/server/src/openapi/index.ts index 13f40edef..42f47e9bf 100644 --- a/packages/server/src/openapi/index.ts +++ b/packages/server/src/openapi/index.ts @@ -7,7 +7,6 @@ import { } from '@zenstackhq/runtime'; import type { ModelZodSchema } from '@zenstackhq/runtime/zod'; import { capitalCase } from 'change-case'; -import invariant from 'tiny-invariant'; import { fromZodError } from 'zod-validation-error'; import { stripAuxFields } from './utils'; @@ -96,18 +95,15 @@ export async function handleRequest({ logger, zodSchemas, }: RequestContext): Promise { - const parts = path.split('/'); - if (parts.length < 2) { - return { status: 400, body: { error: 'invalid request path' } }; - } - - method = method.toUpperCase(); + const parts = path.split('/').filter((p) => !!p); const op = parts.pop(); const model = parts.pop(); - invariant(op); - invariant(model); + if (parts.length !== 0 || !op || !model) { + return { status: 400, body: { message: 'invalid request path' } }; + } + method = method.toUpperCase(); const dbOp = op as keyof DbOperations; let args: unknown; let resCode = 200; diff --git a/packages/server/tests/express-adapter.test.ts b/packages/server/tests/express-adapter.test.ts index 345e170a3..6f77928fc 100644 --- a/packages/server/tests/express-adapter.test.ts +++ b/packages/server/tests/express-adapter.test.ts @@ -87,4 +87,21 @@ describe('Express adapter tests', () => { expect(r.status).toBe(200); expect(r.body.count).toBe(1); }); + + it('invalid path or args', async () => { + const { prisma, zodSchemas } = await loadSchema(schema); + + const app = express(); + app.use(bodyParser.json()); + app.use('/api', ZenStackMiddleware({ getPrisma: () => prisma, zodSchemas })); + + let r = await request(app).get('/api/post/'); + expect(r.status).toBe(400); + + r = await request(app).get('/api/post/findMany/abc'); + expect(r.status).toBe(400); + + r = await request(app).get('/api/post/findMany?q=abc'); + expect(r.status).toBe(400); + }); }); diff --git a/packages/server/tests/fastify-adapter.test.ts b/packages/server/tests/fastify-adapter.test.ts index 45c08100c..78c798037 100644 --- a/packages/server/tests/fastify-adapter.test.ts +++ b/packages/server/tests/fastify-adapter.test.ts @@ -112,4 +112,33 @@ describe('Fastify adapter tests', () => { expect(r.statusCode).toBe(200); expect(r.json().count).toBe(1); }); + + it('invalid path or args', async () => { + const { prisma, zodSchemas } = await loadSchema(schema); + + const app = fastify(); + app.register(ZenStackFastifyPlugin, { + prefix: '/api', + getPrisma: () => prisma, + zodSchemas, + }); + + let r = await app.inject({ + method: 'GET', + url: '/api/post/', + }); + expect(r.statusCode).toBe(400); + + r = await app.inject({ + method: 'GET', + url: '/api/post/findMany/abc', + }); + expect(r.statusCode).toBe(400); + + r = await app.inject({ + method: 'GET', + url: '/api/post/findMany?q=abc', + }); + expect(r.statusCode).toBe(400); + }); }); diff --git a/packages/server/tests/open-api.test.ts b/packages/server/tests/open-api.test.ts index 89e563958..d651d68b8 100644 --- a/packages/server/tests/open-api.test.ts +++ b/packages/server/tests/open-api.test.ts @@ -11,7 +11,7 @@ describe('OpenAPI server tests', () => { let r = await handleRequest({ method: 'get', - path: '/api/post/findMany', + path: '/post/findMany', prisma, zodSchemas, }); @@ -20,7 +20,7 @@ describe('OpenAPI server tests', () => { r = await handleRequest({ method: 'post', - path: '/api/user/create', + path: '/user/create', query: {}, requestBody: { include: { posts: true }, @@ -56,7 +56,7 @@ describe('OpenAPI server tests', () => { r = await handleRequest({ method: 'get', - path: '/api/post/findMany', + path: '/post/findMany', prisma, zodSchemas, }); @@ -65,7 +65,7 @@ describe('OpenAPI server tests', () => { r = await handleRequest({ method: 'get', - path: '/api/post/findMany', + path: '/post/findMany', query: { q: JSON.stringify({ where: { viewCount: { gt: 1 } } }) }, prisma, zodSchemas, @@ -75,7 +75,7 @@ describe('OpenAPI server tests', () => { r = await handleRequest({ method: 'put', - path: '/api/user/update', + path: '/user/update', requestBody: { where: { id: 'user1' }, data: { email: 'user1@def.com' } }, prisma, zodSchemas, @@ -85,7 +85,7 @@ describe('OpenAPI server tests', () => { r = await handleRequest({ method: 'get', - path: '/api/post/count', + path: '/post/count', query: { q: JSON.stringify({ where: { viewCount: { gt: 1 } } }) }, prisma, zodSchemas, @@ -95,7 +95,7 @@ describe('OpenAPI server tests', () => { r = await handleRequest({ method: 'get', - path: '/api/post/aggregate', + path: '/post/aggregate', query: { q: JSON.stringify({ _sum: { viewCount: true } }) }, prisma, zodSchemas, @@ -105,7 +105,7 @@ describe('OpenAPI server tests', () => { r = await handleRequest({ method: 'get', - path: '/api/post/groupBy', + path: '/post/groupBy', query: { q: JSON.stringify({ by: ['published'], _sum: { viewCount: true } }) }, prisma, zodSchemas, @@ -120,7 +120,7 @@ describe('OpenAPI server tests', () => { r = await handleRequest({ method: 'delete', - path: '/api/user/deleteMany', + path: '/user/deleteMany', query: { q: JSON.stringify({ where: { id: 'user1' } }) }, prisma, zodSchemas, @@ -135,14 +135,14 @@ describe('OpenAPI server tests', () => { // without validation let r = await handleRequest({ method: 'get', - path: '/api/post/findUnique', + path: '/post/findUnique', prisma, }); expect(r.status).toBe(400); r = await handleRequest({ method: 'get', - path: '/api/post/findUnique', + path: '/post/findUnique', prisma, zodSchemas, }); @@ -152,7 +152,7 @@ describe('OpenAPI server tests', () => { r = await handleRequest({ method: 'post', - path: '/api/post/create', + path: '/post/create', requestBody: { data: {} }, prisma, zodSchemas, @@ -161,4 +161,42 @@ describe('OpenAPI server tests', () => { expect((r.body as any).message).toContain('Validation error'); expect((r.body as any).message).toContain('data.title'); }); + + it('invalid path or args', async () => { + const { prisma } = await loadSchema(schema); + + let r = await handleRequest({ + method: 'get', + path: '/post/', + prisma, + }); + expect(r.status).toBe(400); + expect((r.body as any).message).toContain('invalid request path'); + + r = await handleRequest({ + method: 'get', + path: '/post/findMany/abc', + prisma, + }); + expect(r.status).toBe(400); + expect((r.body as any).message).toContain('invalid request path'); + + r = await handleRequest({ + method: 'get', + path: '/post/findUnique', + query: { q: 'abc' }, + prisma, + }); + expect(r.status).toBe(400); + expect((r.body as any).message).toContain('query param must contain valid JSON'); + + r = await handleRequest({ + method: 'delete', + path: '/post/deleteMany', + query: { q: 'abc' }, + prisma, + }); + expect(r.status).toBe(400); + expect((r.body as any).message).toContain('query param must contain valid JSON'); + }); });