Skip to content

Commit 6441452

Browse files
committed
feat: API can now handle manual bookmark creation
1 parent f9e2243 commit 6441452

File tree

5 files changed

+87
-23
lines changed

5 files changed

+87
-23
lines changed

app/api/bookmarks/route.ts

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -70,29 +70,41 @@ export const GET = withAuthManager(
7070
export const POST = withAuthManager(
7171
async ({ req, user }): Promise<NextResponse<OasisResponse<Bookmark>>> => {
7272
const schema = createBookmarkSchema(user);
73-
const { url } = await schema.parseAsync(await req.json());
73+
const { url, title, description, iconName, isManual } =
74+
await schema.parseAsync(await req.json());
7475

75-
// TODO - add manual creation of bookmark logic.
76-
77-
const { result } = await ogs({
78-
url: url,
79-
fetchOptions: {
80-
headers: {
81-
'user-agent':
82-
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36',
76+
let createdBookmark;
77+
if (isManual) {
78+
createdBookmark = await prisma.bookmark.create({
79+
data: {
80+
url,
81+
userId: user.id,
82+
title: title || 'must be required',
83+
description: description,
84+
iconName: iconName,
8385
},
84-
},
85-
});
86-
87-
const createdBookmark = await prisma.bookmark.create({
88-
data: {
89-
userId: user.id,
86+
});
87+
} else {
88+
const { result } = await ogs({
9089
url: url,
91-
title: result.ogTitle || result.ogSiteName || 'Title',
92-
description: result.ogDescription || result.twitterDescription || '',
93-
imageUrl: parseUrl(result?.favicon),
94-
},
95-
});
90+
fetchOptions: {
91+
headers: {
92+
'user-agent':
93+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36',
94+
},
95+
},
96+
});
97+
98+
createdBookmark = await prisma.bookmark.create({
99+
data: {
100+
url,
101+
userId: user.id,
102+
title: result.ogTitle || result.ogSiteName || 'Title',
103+
description: result.ogDescription || result.twitterDescription || '',
104+
imageUrl: parseUrl(result?.favicon),
105+
},
106+
});
107+
}
96108

97109
return NextResponse.json(
98110
{

lib/zod/bookmarks.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ export const createBookmarkSchema = (user: AuthUser) => {
4646
return z
4747
.object({
4848
url: z.string().url(),
49+
title: z.string().optional(),
50+
description: z.string().nullable().optional(),
51+
iconName: z.string().nullable().optional(),
52+
isManual: z.boolean().optional(),
53+
})
54+
.refine((data) => !data.isManual || (data.isManual && data.title), {
55+
message: 'Title is required',
56+
path: ['title'],
4957
})
5058
.superRefine(async (data, ctx) => {
5159
const urlExists = await prisma.bookmark.findFirst({
@@ -73,6 +81,9 @@ export const updateBookmarkSchema = (user: AuthUser) => {
7381
description: z.string().nullable().optional(),
7482
isFavorite: z.boolean(),
7583
iconName: z.string().nullable().optional(),
84+
// TODO - What happens if it isn't something valid?
85+
// TODO - valid without having to update this every single time there is a release of new icons?
86+
// TODO - Maybe make a scripts folder with a command that updates a types file containing these?
7687
})
7788
.superRefine(async (data, ctx) => {
7889
const bookmarkExists = await prisma.bookmark.findFirst({

prisma/seed.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ async function seedExternalApiToken(user: User): Promise<void> {
6767
await prisma.apiToken.create({
6868
data: {
6969
userId: user.id,
70-
name: 'Custom API Token',
70+
name: `API Token - ${randomBytes(5).toString('hex').toString()}`,
7171
token: hashedApiToken,
7272
},
7373
});

tests/bookmarks/create-bookmark.test.ts renamed to tests/bookmarks/create-automatic-bookmark.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ import { OasisTestContext, getSetupData } from '@/tests/utils/setup';
44
import { Bookmark } from '@prisma/client';
55
import { afterAll, expect, test } from 'vitest';
66

7-
// TODO - Rename this to automatic and create one for manual
8-
test('POST /bookmarks', async (ctx: OasisTestContext) => {
7+
test('POST AUTOMATIC /bookmarks', async (ctx: OasisTestContext) => {
98
const { user } = getSetupData();
109
const { http } = await new IntegrationHarness(ctx).init();
1110

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { prisma } from '@/lib/db';
2+
import { IntegrationHarness } from '@/tests/utils/integration';
3+
import { OasisTestContext, getSetupData } from '@/tests/utils/setup';
4+
import { Bookmark } from '@prisma/client';
5+
import { afterAll, expect, test } from 'vitest';
6+
7+
test('POST MANUAL /bookmarks', async (ctx: OasisTestContext) => {
8+
const { user } = getSetupData();
9+
const { http } = await new IntegrationHarness(ctx).init();
10+
11+
const {
12+
status,
13+
data: { success, message, data: bookmark },
14+
} = await http.post<Bookmark>({
15+
path: '/bookmarks',
16+
body: {
17+
url: 'https://www.youtube.com/',
18+
title: 'Youtube',
19+
description: 'Watch Videos',
20+
iconName: 'Video',
21+
isManual: true,
22+
},
23+
});
24+
25+
expect(status).toBe(201);
26+
expect(success).toBe(true);
27+
expect(message).toBe('Bookmark was created successfully.');
28+
expect(bookmark).toEqual(
29+
expect.objectContaining({
30+
userId: user.id,
31+
url: 'https://www.youtube.com/',
32+
title: 'Youtube',
33+
description: 'Watch Videos',
34+
iconName: 'Video',
35+
visits: 0,
36+
}),
37+
);
38+
});
39+
40+
afterAll(async () => {
41+
await prisma.bookmark.deleteMany({});
42+
});

0 commit comments

Comments
 (0)