mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
refactor(core): fix id guard issue for post api
This commit is contained in:
parent
9a995ef65f
commit
fd7d5a4ab1
2 changed files with 28 additions and 21 deletions
|
@ -10,27 +10,33 @@ import { createRequester, createTestPool } from './test-utils.js';
|
||||||
const { jest } = import.meta;
|
const { jest } = import.meta;
|
||||||
|
|
||||||
type CreateSchema = {
|
type CreateSchema = {
|
||||||
id?: string;
|
id: string;
|
||||||
|
name?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Schema = {
|
type Schema = {
|
||||||
id: string;
|
id: string;
|
||||||
|
name: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('SchemaRouter', () => {
|
describe('SchemaRouter', () => {
|
||||||
const schema: GeneratedSchema<'id', CreateSchema, Schema> = {
|
const schema: GeneratedSchema<'id' | 'name', CreateSchema, Schema> = {
|
||||||
table: 'test_table',
|
table: 'test_tables',
|
||||||
tableSingular: 'test_table',
|
tableSingular: 'test_table',
|
||||||
fields: {
|
fields: {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
|
name: 'name',
|
||||||
},
|
},
|
||||||
fieldKeys: ['id'],
|
fieldKeys: ['id', 'name'],
|
||||||
createGuard: z.object({ id: z.string().optional() }),
|
createGuard: z.object({ id: z.string(), name: z.string().optional() }),
|
||||||
guard: z.object({ id: z.string() }),
|
guard: z.object({ id: z.string(), name: z.string() }),
|
||||||
updateGuard: z.object({ id: z.string().optional() }),
|
updateGuard: z.object({ id: z.string(), name: z.string() }).partial(),
|
||||||
};
|
};
|
||||||
const entities = [{ id: 'test' }, { id: 'test2' }] as const satisfies readonly Schema[];
|
const entities = [
|
||||||
const actions: SchemaActions<'id', CreateSchema, Schema> = {
|
{ id: '1', name: 'test' },
|
||||||
|
{ id: '2', name: 'test2' },
|
||||||
|
] as const satisfies readonly Schema[];
|
||||||
|
const actions: SchemaActions<'name', CreateSchema, Schema> = {
|
||||||
queries: new SchemaQueries(createTestPool(), schema),
|
queries: new SchemaQueries(createTestPool(), schema),
|
||||||
get: jest.fn().mockResolvedValue([entities.length, entities]),
|
get: jest.fn().mockResolvedValue([entities.length, entities]),
|
||||||
getById: jest.fn(async (id) => {
|
getById: jest.fn(async (id) => {
|
||||||
|
@ -40,8 +46,8 @@ describe('SchemaRouter', () => {
|
||||||
}
|
}
|
||||||
return entity;
|
return entity;
|
||||||
}),
|
}),
|
||||||
post: jest.fn(async () => ({ id: 'test_new' })),
|
post: jest.fn(async () => ({ id: 'new', name: 'test_new' })),
|
||||||
patchById: jest.fn(async (id, data) => ({ id, ...data })),
|
patchById: jest.fn(async (id, data) => ({ id, name: 'name_patch_default', ...data })),
|
||||||
deleteById: jest.fn(),
|
deleteById: jest.fn(),
|
||||||
};
|
};
|
||||||
const schemaRouter = new SchemaRouter(schema, actions);
|
const schemaRouter = new SchemaRouter(schema, actions);
|
||||||
|
@ -76,11 +82,11 @@ describe('SchemaRouter', () => {
|
||||||
const response = await request.post(baseRoute).send({});
|
const response = await request.post(baseRoute).send({});
|
||||||
|
|
||||||
expect(actions.post).toHaveBeenCalledWith({});
|
expect(actions.post).toHaveBeenCalledWith({});
|
||||||
expect(response.body).toStrictEqual({ id: 'test_new' });
|
expect(response.body).toStrictEqual({ id: 'new', name: 'test_new' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw with invalid input body', async () => {
|
it('should throw with invalid input body', async () => {
|
||||||
const response = await request.post(baseRoute).send({ id: 1 });
|
const response = await request.post(baseRoute).send({ name: 1 });
|
||||||
|
|
||||||
expect(response.status).toEqual(400);
|
expect(response.status).toEqual(400);
|
||||||
});
|
});
|
||||||
|
@ -88,15 +94,15 @@ describe('SchemaRouter', () => {
|
||||||
|
|
||||||
describe('getById', () => {
|
describe('getById', () => {
|
||||||
it('should be able to get an entity by id', async () => {
|
it('should be able to get an entity by id', async () => {
|
||||||
const response = await request.get(`${baseRoute}/test`);
|
const response = await request.get(`${baseRoute}/1`);
|
||||||
|
|
||||||
expect(actions.getById).toHaveBeenCalledWith('test');
|
expect(actions.getById).toHaveBeenCalledWith('1');
|
||||||
expect(response.body).toStrictEqual(entities[0]);
|
expect(response.body).toStrictEqual(entities[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// This test case is actually nice-to-have. It's not required for the router to work.
|
// This test case is actually nice-to-have. It's not required for the router to work.
|
||||||
it('should throw with invalid id', async () => {
|
it('should throw with invalid id', async () => {
|
||||||
const response = await request.get(`${baseRoute}/2`);
|
const response = await request.get(`${baseRoute}/foo`);
|
||||||
|
|
||||||
expect(response.status).toEqual(404);
|
expect(response.status).toEqual(404);
|
||||||
});
|
});
|
||||||
|
@ -104,10 +110,10 @@ describe('SchemaRouter', () => {
|
||||||
|
|
||||||
describe('patchById', () => {
|
describe('patchById', () => {
|
||||||
it('should be able to patch an entity by id', async () => {
|
it('should be able to patch an entity by id', async () => {
|
||||||
const response = await request.patch(`${baseRoute}/test`).send({ id: 'test_new' });
|
const response = await request.patch(`${baseRoute}/test`).send({ name: 'test_new' });
|
||||||
|
|
||||||
expect(actions.patchById).toHaveBeenCalledWith('test', { id: 'test_new' });
|
expect(actions.patchById).toHaveBeenCalledWith('test', { name: 'test_new' });
|
||||||
expect(response.body).toStrictEqual({ id: 'test_new' });
|
expect(response.body).toStrictEqual({ id: 'test', name: 'test_new' });
|
||||||
expect(response.status).toEqual(200);
|
expect(response.status).toEqual(200);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { type SchemaLike, type GeneratedSchema } from '@logto/schemas';
|
import { type SchemaLike, type GeneratedSchema, type Guard } from '@logto/schemas';
|
||||||
import { generateStandardId, type OmitAutoSetFields } from '@logto/shared';
|
import { generateStandardId, type OmitAutoSetFields } from '@logto/shared';
|
||||||
import Router, { type IRouterParamContext } from 'koa-router';
|
import Router, { type IRouterParamContext } from 'koa-router';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
@ -119,7 +119,8 @@ export default class SchemaRouter<
|
||||||
this.post(
|
this.post(
|
||||||
'/',
|
'/',
|
||||||
koaGuard({
|
koaGuard({
|
||||||
body: schema.createGuard,
|
// eslint-disable-next-line no-restricted-syntax -- `.omit()` doesn't play well for generic types
|
||||||
|
body: schema.createGuard.omit({ id: true }) as Guard<Omit<CreateSchema, 'id'>>,
|
||||||
response: schema.guard,
|
response: schema.guard,
|
||||||
status: [201, 422],
|
status: [201, 422],
|
||||||
}),
|
}),
|
||||||
|
|
Loading…
Reference in a new issue