오류 수정
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
const dbQueryMock = vi.fn();
|
||||
const { dbQueryMock } = vi.hoisted(() => ({
|
||||
dbQueryMock: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../lib/database.js', () => ({
|
||||
db: {
|
||||
@@ -15,17 +17,25 @@ describe('resolveLinkTemplateId', () => {
|
||||
dbQueryMock.mockReset();
|
||||
});
|
||||
|
||||
it('returns null when the provided template id does not exist', async () => {
|
||||
dbQueryMock.mockResolvedValueOnce({ rows: [] });
|
||||
|
||||
await expect(resolveLinkTemplateId('00000000-0000-0000-0000-000000000000', null)).resolves.toBeNull();
|
||||
});
|
||||
|
||||
it('falls back to template slug when the provided id is not usable', async () => {
|
||||
it('falls back to the default template when the provided id does not exist', async () => {
|
||||
dbQueryMock.mockResolvedValueOnce({ rows: [] });
|
||||
dbQueryMock.mockResolvedValueOnce({ rows: [{ id: '11111111-1111-1111-1111-111111111111' }] });
|
||||
|
||||
await expect(resolveLinkTemplateId('00000000-0000-0000-0000-000000000000', null)).resolves.toBe('11111111-1111-1111-1111-111111111111');
|
||||
expect(dbQueryMock).toHaveBeenNthCalledWith(2, 'SELECT id FROM link_templates WHERE is_default = true LIMIT 1');
|
||||
});
|
||||
|
||||
it('uses the template slug when it can be resolved', async () => {
|
||||
dbQueryMock.mockResolvedValueOnce({ rows: [{ id: '11111111-1111-1111-1111-111111111111' }] });
|
||||
|
||||
await expect(resolveLinkTemplateId(null, 'default')).resolves.toBe('11111111-1111-1111-1111-111111111111');
|
||||
expect(dbQueryMock).toHaveBeenNthCalledWith(2, 'SELECT id FROM link_templates WHERE slug = $1 LIMIT 1', ['default']);
|
||||
expect(dbQueryMock).toHaveBeenCalledWith('SELECT id FROM link_templates WHERE slug = $1 LIMIT 1', ['default']);
|
||||
});
|
||||
|
||||
it('falls back to the default template when the provided template id is invalid', async () => {
|
||||
dbQueryMock.mockResolvedValueOnce({ rows: [{ id: '22222222-2222-2222-2222-222222222222' }] });
|
||||
|
||||
await expect(resolveLinkTemplateId('not-a-uuid', null)).resolves.toBe('22222222-2222-2222-2222-222222222222');
|
||||
expect(dbQueryMock).toHaveBeenCalledWith('SELECT id FROM link_templates WHERE is_default = true LIMIT 1');
|
||||
});
|
||||
});
|
||||
|
||||
+44
-2
@@ -13,6 +13,43 @@ async function getTemplateSlug(templateId: string | null): Promise<string | null
|
||||
return result.rows[0]?.slug ?? null;
|
||||
}
|
||||
|
||||
export async function resolveLinkTemplateId(
|
||||
templateId: string | null | undefined,
|
||||
templateSlug: string | null | undefined
|
||||
): Promise<string | null> {
|
||||
const normalizedTemplateId = templateId?.trim();
|
||||
const normalizedSlug = templateSlug?.trim();
|
||||
|
||||
if (normalizedTemplateId) {
|
||||
const parsed = z.string().uuid().safeParse(normalizedTemplateId);
|
||||
if (parsed.success) {
|
||||
const result = await db.query(
|
||||
'SELECT id FROM link_templates WHERE id = $1 LIMIT 1',
|
||||
[normalizedTemplateId]
|
||||
);
|
||||
if (result.rows[0]?.id) {
|
||||
return result.rows[0].id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (normalizedSlug) {
|
||||
const result = await db.query(
|
||||
'SELECT id FROM link_templates WHERE slug = $1 LIMIT 1',
|
||||
[normalizedSlug]
|
||||
);
|
||||
if (result.rows[0]?.id) {
|
||||
return result.rows[0].id;
|
||||
}
|
||||
}
|
||||
|
||||
const defaultResult = await db.query(
|
||||
'SELECT id FROM link_templates WHERE is_default = true LIMIT 1'
|
||||
);
|
||||
|
||||
return defaultResult.rows[0]?.id ?? null;
|
||||
}
|
||||
|
||||
const createLinkSchema = z.object({
|
||||
userId: z.string().uuid().optional(),
|
||||
templateId: z.string().uuid().optional(),
|
||||
@@ -182,6 +219,7 @@ export async function linkRoutes(fastify: FastifyInstance) {
|
||||
}
|
||||
|
||||
const originalUrl = data.originalUrl || 'https://angkorlifes.com';
|
||||
const resolvedTemplateId = await resolveLinkTemplateId(data.templateId, null);
|
||||
|
||||
const result = await db.query(
|
||||
`INSERT INTO links (
|
||||
@@ -195,7 +233,7 @@ export async function linkRoutes(fastify: FastifyInstance) {
|
||||
RETURNING *`,
|
||||
[
|
||||
data.userId || null,
|
||||
data.templateId || null,
|
||||
resolvedTemplateId,
|
||||
shortCode,
|
||||
originalUrl,
|
||||
data.title || null,
|
||||
@@ -238,6 +276,10 @@ export async function linkRoutes(fastify: FastifyInstance) {
|
||||
const { userId } = request.query;
|
||||
|
||||
const data = updateLinkSchema.parse(request.body);
|
||||
const resolvedData = {
|
||||
...data,
|
||||
templateId: data.templateId === undefined ? undefined : await resolveLinkTemplateId(data.templateId, null),
|
||||
};
|
||||
|
||||
// Capture current identifiers for cache invalidation after the update
|
||||
const oldLinkResult = await db.query(
|
||||
@@ -250,7 +292,7 @@ export async function linkRoutes(fastify: FastifyInstance) {
|
||||
const values: any[] = [];
|
||||
let paramIndex = 1;
|
||||
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
Object.entries(resolvedData).forEach(([key, value]) => {
|
||||
if (value !== undefined) {
|
||||
if (key === 'utmParameters' || key === 'targetingRules' || key === 'deepLinkParameters') {
|
||||
updates.push(`${key.replace(/([A-Z])/g, '_$1').toLowerCase()} = $${paramIndex}`);
|
||||
|
||||
+3
-1
@@ -12,6 +12,7 @@ import { triggerWebhooks } from '../lib/webhook.js';
|
||||
import { parseUserAgent, getLocationFromIP, detectDevice } from '../lib/utils.js';
|
||||
import { emitClickEvent } from '../lib/event-emitter.js';
|
||||
import { classifyBot, edgeBotSignal } from '../lib/bot-detection.js';
|
||||
import { resolveLinkTemplateId } from './links.js';
|
||||
|
||||
/**
|
||||
* SDK Routes - Mobile SDK endpoints for deferred deep linking
|
||||
@@ -66,6 +67,7 @@ export async function sdkRoutes(fastify: FastifyInstance) {
|
||||
const originalUrl = body.originalUrl || 'https://angkorlifes.com';
|
||||
|
||||
const shortCode = body.customCode || 'sdk-link';
|
||||
const resolvedTemplateId = await resolveLinkTemplateId(body.templateId, null);
|
||||
|
||||
const result = await db.query(
|
||||
`INSERT INTO links (
|
||||
@@ -79,7 +81,7 @@ export async function sdkRoutes(fastify: FastifyInstance) {
|
||||
RETURNING *`,
|
||||
[
|
||||
body.userId || null,
|
||||
body.templateId || null,
|
||||
resolvedTemplateId,
|
||||
shortCode,
|
||||
originalUrl,
|
||||
body.title || null,
|
||||
|
||||
Reference in New Issue
Block a user