mirror of
https://github.com/Floriansylvain/SnippetsManager.git
synced 2026-08-19 11:43:15 +02:00
Added tags to GET snippet routes
This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE `Snippet` DROP FOREIGN KEY `Snippet_category_id_fkey`;
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE `Snippet_tag` DROP FOREIGN KEY `Snippet_tag_snippet_id_fkey`;
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE `Snippet_tag` DROP FOREIGN KEY `Snippet_tag_tag_id_fkey`;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Snippet` ADD CONSTRAINT `Snippet_category_id_fkey` FOREIGN KEY (`category_id`) REFERENCES `Category`(`id`) ON DELETE SET NULL ON UPDATE SET NULL;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Snippet_tag` ADD CONSTRAINT `Snippet_tag_tag_id_fkey` FOREIGN KEY (`tag_id`) REFERENCES `Tag`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Snippet_tag` ADD CONSTRAINT `Snippet_tag_snippet_id_fkey` FOREIGN KEY (`snippet_id`) REFERENCES `Snippet`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
@@ -30,7 +30,7 @@ model Snippet {
|
|||||||
user_id Int
|
user_id Int
|
||||||
user User @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
user User @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||||
category_id Int?
|
category_id Int?
|
||||||
category Category? @relation(fields: [category_id], references: [id])
|
category Category? @relation(fields: [category_id], references: [id], onDelete: SetNull, onUpdate: SetNull)
|
||||||
language_id Int
|
language_id Int
|
||||||
language Language @relation(fields: [language_id], references: [id])
|
language Language @relation(fields: [language_id], references: [id])
|
||||||
Snippet_tag Snippet_tag[]
|
Snippet_tag Snippet_tag[]
|
||||||
@@ -52,9 +52,9 @@ model Language {
|
|||||||
|
|
||||||
model Snippet_tag {
|
model Snippet_tag {
|
||||||
tag_id Int
|
tag_id Int
|
||||||
tag Tag @relation(fields: [tag_id], references: [id])
|
tag Tag @relation(fields: [tag_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||||
snippet_id Int
|
snippet_id Int
|
||||||
snippet Snippet @relation(fields: [snippet_id], references: [id])
|
snippet Snippet @relation(fields: [snippet_id], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||||
|
|
||||||
@@id([tag_id, snippet_id])
|
@@id([tag_id, snippet_id])
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-26
@@ -181,18 +181,6 @@ describe('PUT /v1/category/:id', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('DELETE /v1/category/:id', () => {
|
|
||||||
it('returns status code 200 and a success message', async () => {
|
|
||||||
const res = await request(app)
|
|
||||||
.delete(`/v1/category/${category_id}`)
|
|
||||||
.set('Content-Type', 'application/json')
|
|
||||||
.set('Cookie', jwtCookie as string)
|
|
||||||
|
|
||||||
expect(res.status).toEqual(200)
|
|
||||||
expect(res.body).toHaveProperty('message')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('POST /v1/snippet', () => {
|
describe('POST /v1/snippet', () => {
|
||||||
it('returns status code 200 and the snippets', async () => {
|
it('returns status code 200 and the snippets', async () => {
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
@@ -209,7 +197,9 @@ describe('POST /v1/snippet', () => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
</style>`,
|
</style>`,
|
||||||
language: 'vue'
|
language: 'vue',
|
||||||
|
tags: ['template', 'vuejs'],
|
||||||
|
category_id
|
||||||
}))
|
}))
|
||||||
|
|
||||||
expect(res.status).toEqual(200)
|
expect(res.status).toEqual(200)
|
||||||
@@ -228,18 +218,7 @@ describe('GET /v1/snippet/', () => {
|
|||||||
|
|
||||||
expect(res.status).toEqual(200)
|
expect(res.status).toEqual(200)
|
||||||
expect(res.body.snippets[0].title).toEqual('Vue3 CompAPI TS script-template-style')
|
expect(res.body.snippets[0].title).toEqual('Vue3 CompAPI TS script-template-style')
|
||||||
})
|
expect(res.body.snippets[0].category_id).toEqual(category_id)
|
||||||
})
|
|
||||||
|
|
||||||
describe('GET /v1/snippet/:id', () => {
|
|
||||||
it('returns status code 200 and one snippet', async () => {
|
|
||||||
const res = await request(app)
|
|
||||||
.get(`/v1/snippet/${snippet_id}`)
|
|
||||||
.set('Content-Type', 'application/json')
|
|
||||||
.set('Cookie', jwtCookie as string)
|
|
||||||
|
|
||||||
expect(res.status).toEqual(200)
|
|
||||||
expect(res.body.snippet.title).toEqual('Vue3 CompAPI TS script-template-style')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -251,7 +230,8 @@ describe('PUT /v1/snippet/:id', () => {
|
|||||||
.set('Cookie', jwtCookie as string)
|
.set('Cookie', jwtCookie as string)
|
||||||
.send(JSON.stringify({
|
.send(JSON.stringify({
|
||||||
code: '<p>en fait non à vuejs</p>',
|
code: '<p>en fait non à vuejs</p>',
|
||||||
// language: 'html'
|
language: 'html',
|
||||||
|
tags: ['pouet', 'pouet', 'pouet']
|
||||||
}))
|
}))
|
||||||
|
|
||||||
expect(res.status).toEqual(200)
|
expect(res.status).toEqual(200)
|
||||||
@@ -259,6 +239,21 @@ describe('PUT /v1/snippet/:id', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
describe('GET /v1/snippet/:id', () => {
|
||||||
|
it('returns status code 200 and one snippet', async () => {
|
||||||
|
const res = await request(app)
|
||||||
|
.get(`/v1/snippet/${snippet_id}`)
|
||||||
|
.set('Content-Type', 'application/json')
|
||||||
|
.set('Cookie', jwtCookie as string)
|
||||||
|
|
||||||
|
expect(res.status).toEqual(200)
|
||||||
|
expect(res.body.snippet.tags.length).toEqual(3)
|
||||||
|
expect(res.body.snippet.tags).not.toContain('vuejs')
|
||||||
|
expect(res.body.snippet.category_id).toEqual(category_id)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('DELETE /v1/snippet/:id', () => {
|
describe('DELETE /v1/snippet/:id', () => {
|
||||||
it('returns status code 200 and success message', async () => {
|
it('returns status code 200 and success message', async () => {
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
@@ -271,6 +266,18 @@ describe('DELETE /v1/snippet/:id', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('DELETE /v1/category/:id', () => {
|
||||||
|
it('returns status code 200 and a success message', async () => {
|
||||||
|
const res = await request(app)
|
||||||
|
.delete(`/v1/category/${category_id}`)
|
||||||
|
.set('Content-Type', 'application/json')
|
||||||
|
.set('Cookie', jwtCookie as string)
|
||||||
|
|
||||||
|
expect(res.status).toEqual(200)
|
||||||
|
expect(res.body).toHaveProperty('message')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('PUT /v1/user', () => {
|
describe('PUT /v1/user', () => {
|
||||||
it('returns status code 200 and success message', async () => {
|
it('returns status code 200 and success message', async () => {
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
|
|||||||
+3
-2
@@ -1,13 +1,14 @@
|
|||||||
import express, { RequestHandler } from 'express'
|
import express, { RequestHandler } from 'express'
|
||||||
import userRouter from './routers/user.js'
|
|
||||||
import * as dotenv from 'dotenv'
|
import * as dotenv from 'dotenv'
|
||||||
import jwt from 'jsonwebtoken'
|
import jwt from 'jsonwebtoken'
|
||||||
import cors from 'cors'
|
import cors from 'cors'
|
||||||
import cookieParser from 'cookie-parser'
|
import cookieParser from 'cookie-parser'
|
||||||
import { getJwtSecret } from './utils/jwt.js'
|
import { getJwtSecret } from './utils/jwt.js'
|
||||||
|
|
||||||
|
import userRouter from './routers/user.js'
|
||||||
import sessionRouter from './routers/session.js'
|
import sessionRouter from './routers/session.js'
|
||||||
import categoryRouter from './routers/category.js'
|
import categoryRouter from './routers/category.js'
|
||||||
import snippetRouter from './routers/snippet'
|
import snippetRouter from './routers/snippet.js'
|
||||||
|
|
||||||
const authMiddleware: RequestHandler = (req, res, next) => {
|
const authMiddleware: RequestHandler = (req, res, next) => {
|
||||||
const token = req.cookies.jwt
|
const token = req.cookies.jwt
|
||||||
|
|||||||
+90
-11
@@ -9,19 +9,29 @@ const prisma = new PrismaClient()
|
|||||||
const snippetPostParser = z.object({
|
const snippetPostParser = z.object({
|
||||||
code: z.string(),
|
code: z.string(),
|
||||||
title: z.string().max(50),
|
title: z.string().max(50),
|
||||||
language: z.string().max(32)
|
language: z.string().max(32),
|
||||||
|
tags: z.array(z.string()),
|
||||||
|
category_id: z.number()
|
||||||
}).required()
|
}).required()
|
||||||
|
|
||||||
const snippetUpdateParser = z.object({
|
const snippetUpdateParser = z.object({
|
||||||
code: z.string().optional(),
|
code: z.string().optional(),
|
||||||
title: z.string().max(50).optional(),
|
title: z.string().max(50).optional(),
|
||||||
language: z.string().max(32).optional()
|
language: z.string().max(32).optional(),
|
||||||
|
tags: z.array(z.string()).optional()
|
||||||
})
|
})
|
||||||
|
|
||||||
const paramsIdParser = z.object({
|
const paramsIdParser = z.object({
|
||||||
id: z.coerce.number(),
|
id: z.coerce.number(),
|
||||||
}).required()
|
}).required()
|
||||||
|
|
||||||
|
function shortenSnippetsTagDepth(snippets: Snippet[]) {
|
||||||
|
snippets.forEach((snippet: any) => {
|
||||||
|
snippet.tags = snippet.Snippet_tag.map((x: any) => ({ ...x.tag }))
|
||||||
|
snippet.Snippet_tag = undefined
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// TODO Ajouter paramètres sur la requête pour rechercher des snippets
|
// TODO Ajouter paramètres sur la requête pour rechercher des snippets
|
||||||
// TODO Ajouter pagination
|
// TODO Ajouter pagination
|
||||||
const snippetGet: RequestHandler = async (req, res) => {
|
const snippetGet: RequestHandler = async (req, res) => {
|
||||||
@@ -29,8 +39,14 @@ const snippetGet: RequestHandler = async (req, res) => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
snippets = await prisma.snippet.findMany({
|
snippets = await prisma.snippet.findMany({
|
||||||
where: { user_id: req.body.userId }
|
where: { user_id: req.body.userId },
|
||||||
|
include: {
|
||||||
|
Snippet_tag: {
|
||||||
|
select: { tag: { select: { id: true, name: true } } },
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
shortenSnippetsTagDepth(snippets)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
res.status(400).json({ message: (error.issues ?? error) })
|
res.status(400).json({ message: (error.issues ?? error) })
|
||||||
return;
|
return;
|
||||||
@@ -47,8 +63,16 @@ const snippetGetUnique: RequestHandler = async (req, res) => {
|
|||||||
where: {
|
where: {
|
||||||
id: paramsIdParser.parse(req.params).id,
|
id: paramsIdParser.parse(req.params).id,
|
||||||
user_id: req.body.userId
|
user_id: req.body.userId
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
Snippet_tag: {
|
||||||
|
select: { tag: { select: { id: true, name: true } } },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
if (snippet != undefined) {
|
||||||
|
shortenSnippetsTagDepth([snippet])
|
||||||
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
res.status(400).json({ message: (error.issues ?? error) })
|
res.status(400).json({ message: (error.issues ?? error) })
|
||||||
return;
|
return;
|
||||||
@@ -57,10 +81,60 @@ const snippetGetUnique: RequestHandler = async (req, res) => {
|
|||||||
res.json({ snippet })
|
res.json({ snippet })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function deleteSnippetTags(userId: number, snippetId: number): Promise<void> {
|
||||||
|
await prisma.snippet_tag.deleteMany({
|
||||||
|
where: {
|
||||||
|
tag: { user_id: userId },
|
||||||
|
snippet_id: snippetId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createSnippetTag(snippetId: number, userId: number, tagName: string): Promise<void> {
|
||||||
|
await prisma.snippet_tag.create({
|
||||||
|
data: {
|
||||||
|
snippet: {
|
||||||
|
connect: { id: snippetId }
|
||||||
|
},
|
||||||
|
tag: {
|
||||||
|
create: {
|
||||||
|
name: tagName,
|
||||||
|
user_id: userId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateSnippet(snippetId: number, userId: number, languageId: number | undefined, snippetData: any): Promise<Prisma.BatchPayload> {
|
||||||
|
return await prisma.snippet.updateMany({
|
||||||
|
where: {
|
||||||
|
id: snippetId,
|
||||||
|
user_id: userId
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title: snippetData.title,
|
||||||
|
code: snippetData.code,
|
||||||
|
user_id: userId,
|
||||||
|
language_id: languageId,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateTagsFromSnippet(snippetId: number, userId: number, snippetData: any): Promise<void> {
|
||||||
|
if (snippetData.tags == undefined) return;
|
||||||
|
|
||||||
|
await deleteSnippetTags(userId, snippetId)
|
||||||
|
|
||||||
|
for (const tag of snippetData.tags) {
|
||||||
|
await createSnippetTag(snippetId, userId, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const snippetPost: RequestHandler = async (req, res) => {
|
const snippetPost: RequestHandler = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const newSnippet = snippetPostParser.parse(req.body)
|
const newSnippet = snippetPostParser.parse(req.body)
|
||||||
await prisma.snippet.create({
|
const snippet = await prisma.snippet.create({
|
||||||
data: {
|
data: {
|
||||||
title: newSnippet.title,
|
title: newSnippet.title,
|
||||||
code: newSnippet.code,
|
code: newSnippet.code,
|
||||||
@@ -72,9 +146,15 @@ const snippetPost: RequestHandler = async (req, res) => {
|
|||||||
where: { name: newSnippet.language },
|
where: { name: newSnippet.language },
|
||||||
create: { name: newSnippet.language }
|
create: { name: newSnippet.language }
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
category: {
|
||||||
|
connect: { id: newSnippet.category_id }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
for (const tag of newSnippet.tags) {
|
||||||
|
await createSnippetTag(snippet.id, req.body.userId, tag)
|
||||||
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
res.status(400).json({ message: (error.issues ?? error) })
|
res.status(400).json({ message: (error.issues ?? error) })
|
||||||
return;
|
return;
|
||||||
@@ -87,14 +167,13 @@ const snippetUpdate: RequestHandler = async (req, res) => {
|
|||||||
let updated: Prisma.BatchPayload
|
let updated: Prisma.BatchPayload
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const snippetToUpdate = snippetUpdateParser.parse(req.body)
|
const snippetData = snippetUpdateParser.parse(req.body)
|
||||||
updated = await prisma.snippet.updateMany({
|
const languageId = await prisma.language.findFirst({
|
||||||
where: {
|
where: { name: snippetData.language }
|
||||||
id: paramsIdParser.parse(req.params).id,
|
|
||||||
user_id: req.body.userId
|
|
||||||
},
|
|
||||||
data: snippetToUpdate
|
|
||||||
})
|
})
|
||||||
|
const snippetId = paramsIdParser.parse(req.params).id
|
||||||
|
updated = await updateSnippet(snippetId, req.body.userId, languageId?.id, snippetData)
|
||||||
|
await updateTagsFromSnippet(snippetId, req.body.userId, snippetData)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
res.status(400).json({ message: (error.issues ?? error) })
|
res.status(400).json({ message: (error.issues ?? error) })
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user