mirror of
https://github.com/zenstackhq/zenstack
synced 2026-05-24 10:08:55 +00:00
* feat: custom procs
* chore: cleanup
* fix: remove $procedures from client
* fix: failing test due to previous alias
* feat(custom-procs)!: make procedures envelope-only via $procs
- Switch procedure calls to `db.$procs.name({ args: {...} })` (no positional args)
- Remove legacy `$procedures` alias entirely (client API + server routing/logging)
- Validate procedure envelope input (`args` object, required/unknown keys)
- Keep TanStack Query procedure hooks as `(args, options)` (with conditional args optionality)
- Update server/ORM/client tests for the envelope API
* fix: code review feedback
* fix: code review comments
* fix: coderabbit review comments
* fix: remove useless proxy method
* test: add a couple of e2e tests that verify both typing and runtime
* test: improve e2e tests
* test: add missing mutation flag
* regenerate test schema
* refactor: procedure params generation fix and type refactors
- Simplified procedure's params definition from a tuple an object, since procs are now called with an envelop now
- Refactored procedure related typing to make them more consistent with other CURD types (that usually takes the schema as the first type parameter, and a name as the second)
- Moved detailed procedure's types to "crud-types" where other ORM client detailed types are defined
- Removed some type duplication from hooks side
- Updated the "orm" sample to demonstrate procedures
* fix: disable infinite custom proc queries for now
---------
Co-authored-by: ymc9 <104139426+ymc9@users.noreply.github.com>
86 lines
2.5 KiB
TypeScript
86 lines
2.5 KiB
TypeScript
import type { ClientContract } from '@zenstackhq/orm';
|
|
import type { SchemaDef } from '@zenstackhq/orm/schema';
|
|
import { createTestClient } from '@zenstackhq/testtools';
|
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
import { RestApiHandler } from '../../src/api/rest';
|
|
|
|
describe('Procedures E2E', () => {
|
|
let client: ClientContract<SchemaDef>;
|
|
let api: RestApiHandler;
|
|
|
|
const schema = `
|
|
datasource db {
|
|
provider = 'sqlite'
|
|
url = 'file:./test.db'
|
|
}
|
|
|
|
model User {
|
|
id Int @id @default(autoincrement())
|
|
email String @unique
|
|
}
|
|
|
|
procedure greet(name: String?): String
|
|
mutation procedure createTwoAndFail(email1: String, email2: String): Int
|
|
`;
|
|
|
|
beforeEach(async () => {
|
|
client = await createTestClient(
|
|
schema,
|
|
{
|
|
procedures: {
|
|
greet: async ({ args }: any) => {
|
|
const name = args?.name as string | undefined;
|
|
return `hello ${name ?? 'world'}`;
|
|
},
|
|
createTwoAndFail: async ({ client, args }: any) => {
|
|
const email1 = args.email1 as string;
|
|
const email2 = args.email2 as string;
|
|
await client.user.create({ data: { email: email1 } });
|
|
await client.user.create({ data: { email: email2 } });
|
|
throw new Error('boom');
|
|
},
|
|
},
|
|
} as any
|
|
);
|
|
|
|
api = new RestApiHandler({
|
|
schema: client.$schema,
|
|
endpoint: 'http://localhost/api',
|
|
pageSize: 5,
|
|
});
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await client?.$disconnect();
|
|
});
|
|
|
|
it('supports $procs routes', async () => {
|
|
const r = await api.handleRequest({
|
|
client,
|
|
method: 'get',
|
|
path: '/$procs/greet',
|
|
query: { args: { name: 'alice' } } as any,
|
|
});
|
|
expect(r.status).toBe(200);
|
|
expect(r.body).toEqual({ data: 'hello alice' });
|
|
});
|
|
|
|
it('returns 422 for invalid input', async () => {
|
|
const r = await api.handleRequest({
|
|
client,
|
|
method: 'get',
|
|
path: '/$procs/greet',
|
|
query: { args: { name: 123 } } as any,
|
|
});
|
|
|
|
expect(r.status).toBe(422);
|
|
expect(r.body).toMatchObject({
|
|
errors: [
|
|
{
|
|
status: 422,
|
|
code: 'validation-error',
|
|
},
|
|
],
|
|
});
|
|
});
|
|
});
|