mirror of
https://github.com/ThisIsBenny/wishlist-app.git
synced 2025-06-07 05:57:41 +00:00
Merge branch 'release/v1.0.0' of github.com:ThisIsBenny/wishlist-app into release/v1.0.0
This commit is contained in:
commit
ca593fc9ff
17 changed files with 474 additions and 96 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -30,5 +30,4 @@ dist/
|
||||||
coverage
|
coverage
|
||||||
.env
|
.env
|
||||||
data
|
data
|
||||||
prisma/seed.ts
|
|
||||||
public/*.jpeg
|
public/*.jpeg
|
||||||
|
|
73
examples.http
Normal file
73
examples.http
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
@BASE_URL=http://localhost:5000/api
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
# @name createWishlistFirst
|
||||||
|
POST {{BASE_URL}}/wishlist
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"title": "Junior",
|
||||||
|
"imageSrc": "https://unsplash.com/photos/JZ51o_-UOY8/download?force=true&w=200",
|
||||||
|
"slugUrlText": "junior"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
# @name createWishlistSecond
|
||||||
|
POST {{BASE_URL}}/wishlist
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"title": "Wedding",
|
||||||
|
"imageSrc": "https://unsplash.com/photos/8vaQKYnawHw/download?ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjQ0MDQ4MTIy&force=true&w=200",
|
||||||
|
"description": "We are getting married",
|
||||||
|
"slugUrlText": "wedding"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
# @name getWishlists
|
||||||
|
GET {{BASE_URL}}/wishlist
|
||||||
|
|
||||||
|
###
|
||||||
|
# @name getFirstWishlist
|
||||||
|
GET {{BASE_URL}}/wishlist/{{getWishlists.response.body.0.slugUrlText}}
|
||||||
|
|
||||||
|
###
|
||||||
|
# @name updateFirstWishlist
|
||||||
|
PUT {{BASE_URL}}/wishlist/{{getWishlists.response.body.0.id}}
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"title": "Junior",
|
||||||
|
"imageSrc": "https://unsplash.com/photos/JZ51o_-UOY8/download?force=true&w=200",
|
||||||
|
"description": "Juniors Wishlist",
|
||||||
|
"slugUrlText": "junior"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
# @name addItemToFirstWishlist
|
||||||
|
POST {{BASE_URL}}/wishlist/{{getWishlists.response.body.0.id}}/item
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"title": "Goldfish 40442 | BrickHeadz",
|
||||||
|
"url": "https://www.lego.com/en-de/product/goldfish-40442",
|
||||||
|
"imageSrc": "https://www.lego.com/cdn/cs/set/assets/blt1fc37afef51cfa9f/40442.jpg?fit=bounds&format=jpg&quality=80&width=1500&height=1500&dpr=1",
|
||||||
|
"description": "Cute goldfish and fry, build-and-display BrickHeadz™ model"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
# @name updateItemOnFirstWishlist
|
||||||
|
PUT {{BASE_URL}}/wishlist/{{getWishlists.response.body.0.id}}/item/1
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"title": "Goldfish | BrickHeadz",
|
||||||
|
"url": "https://www.lego.com/en-de/product/goldfish-40442",
|
||||||
|
"imageSrc": "https://www.lego.com/cdn/cs/set/assets/blt1fc37afef51cfa9f/40442.jpg?fit=bounds&format=jpg&quality=80&width=1500&height=1500&dpr=1",
|
||||||
|
"description": "Cute goldfish and fry, build-and-display BrickHeadz™ model"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
# @name deleteItemToFirstWishlist
|
||||||
|
DELETE {{BASE_URL}}/wishlist/{{getWishlists.response.body.0.id}}/item/2
|
|
@ -4,17 +4,17 @@ CREATE TABLE "Wishlist" (
|
||||||
"title" TEXT NOT NULL,
|
"title" TEXT NOT NULL,
|
||||||
"imageSrc" TEXT NOT NULL,
|
"imageSrc" TEXT NOT NULL,
|
||||||
"slugUrlText" TEXT NOT NULL,
|
"slugUrlText" TEXT NOT NULL,
|
||||||
"description" TEXT
|
"description" TEXT NOT NULL DEFAULT ''
|
||||||
);
|
);
|
||||||
|
|
||||||
-- CreateTable
|
-- CreateTable
|
||||||
CREATE TABLE "Item" (
|
CREATE TABLE "Item" (
|
||||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
"title" TEXT NOT NULL,
|
"title" TEXT NOT NULL,
|
||||||
"url" TEXT,
|
"url" TEXT NOT NULL DEFAULT '',
|
||||||
"imageSrc" TEXT,
|
"imageSrc" TEXT NOT NULL DEFAULT '',
|
||||||
"description" TEXT NOT NULL,
|
"description" TEXT NOT NULL,
|
||||||
"comment" TEXT,
|
"comment" TEXT NOT NULL DEFAULT '',
|
||||||
"bought" BOOLEAN NOT NULL DEFAULT false,
|
"bought" BOOLEAN NOT NULL DEFAULT false,
|
||||||
"wishlistId" TEXT NOT NULL,
|
"wishlistId" TEXT NOT NULL,
|
||||||
CONSTRAINT "Item_wishlistId_fkey" FOREIGN KEY ("wishlistId") REFERENCES "Wishlist" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
CONSTRAINT "Item_wishlistId_fkey" FOREIGN KEY ("wishlistId") REFERENCES "Wishlist" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
|
@ -15,18 +15,18 @@ model Wishlist {
|
||||||
title String
|
title String
|
||||||
imageSrc String
|
imageSrc String
|
||||||
slugUrlText String @unique
|
slugUrlText String @unique
|
||||||
description String?
|
description String @default("")
|
||||||
items Item[]
|
items Item[]
|
||||||
}
|
}
|
||||||
|
|
||||||
model Item {
|
model Item {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
title String
|
title String
|
||||||
url String?
|
url String @default("")
|
||||||
imageSrc String?
|
imageSrc String @default("")
|
||||||
description String
|
description String
|
||||||
comment String?
|
comment String @default("")
|
||||||
bought Boolean @default(false)
|
bought Boolean @default(false)
|
||||||
wishlist Wishlist @relation(fields: [wishlistId], references: [id])
|
wishlist Wishlist @relation(fields: [wishlistId], references: [id])
|
||||||
wishlistId String
|
wishlistId String
|
||||||
}
|
}
|
||||||
|
|
60
prisma/seed.ts
Normal file
60
prisma/seed.ts
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import { PrismaClient, Prisma } from '@prisma/client'
|
||||||
|
|
||||||
|
const prisma = new PrismaClient()
|
||||||
|
|
||||||
|
const wishlistData: Prisma.WishlistCreateInput[] = [
|
||||||
|
{
|
||||||
|
title: 'Junior',
|
||||||
|
imageSrc:
|
||||||
|
'https://unsplash.com/photos/JZ51o_-UOY8/download?force=true&w=200',
|
||||||
|
description: '',
|
||||||
|
slugUrlText: 'junior',
|
||||||
|
items: {
|
||||||
|
create: [
|
||||||
|
{
|
||||||
|
title: 'Goldfish 40442 | BrickHeadz',
|
||||||
|
url: 'https://www.lego.com/en-de/product/goldfish-40442',
|
||||||
|
imageSrc:
|
||||||
|
'https://www.lego.com/cdn/cs/set/assets/blt1fc37afef51cfa9f/40442.jpg?fit=bounds&format=jpg&quality=80&width=1500&height=1500&dpr=1',
|
||||||
|
description:
|
||||||
|
'Cute goldfish and fry, build-and-display BrickHeadz™ model',
|
||||||
|
comment: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Wedding',
|
||||||
|
imageSrc:
|
||||||
|
'https://unsplash.com/photos/8vaQKYnawHw/download?ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjQ0MDQ4MTIy&force=true&w=200',
|
||||||
|
description: 'We are getting married',
|
||||||
|
slugUrlText: 'wedding',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '40th birthday',
|
||||||
|
imageSrc:
|
||||||
|
'https://unsplash.com/photos/poH6OvcEeXE/download?ixid=MnwxMjA3fDB8MXxzZWFyY2h8NHx8YmlydGhkYXl8fDB8fHx8MTY0NDA1NDEzNA&force=true&w=200',
|
||||||
|
description: 'We are getting married',
|
||||||
|
slugUrlText: '40th-birthday',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log(`Start seeding ...`)
|
||||||
|
for (const u of wishlistData) {
|
||||||
|
const wishlist = await prisma.wishlist.create({
|
||||||
|
data: u,
|
||||||
|
})
|
||||||
|
console.log(`Created wishlist with id: ${wishlist.id}`)
|
||||||
|
}
|
||||||
|
console.log(`Seeding finished.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e)
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||
|
.finally(async () => {
|
||||||
|
await prisma.$disconnect()
|
||||||
|
})
|
56
src/api/config/errors/index.ts
Normal file
56
src/api/config/errors/index.ts
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import { FastifyRequest, FastifyReply, FastifyError } from 'fastify'
|
||||||
|
import { Prisma } from '@prisma/client'
|
||||||
|
|
||||||
|
const errorIs = (e: unknown, c: string) =>
|
||||||
|
e instanceof Prisma.PrismaClientKnownRequestError && e.code === c
|
||||||
|
|
||||||
|
export const defaultErrorHandler = (
|
||||||
|
error: FastifyError,
|
||||||
|
request: FastifyRequest,
|
||||||
|
reply: FastifyReply
|
||||||
|
) => {
|
||||||
|
if (error.validation) {
|
||||||
|
error.code = '400'
|
||||||
|
reply.send(error)
|
||||||
|
} else if (errorIs(error, 'P2002')) {
|
||||||
|
reply.send(
|
||||||
|
uniqueKeyError(
|
||||||
|
// @ts-expect-error: Object is possibly 'undefined'
|
||||||
|
`${error.meta.target[0] || 'One of the fields'} has to be unique`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else if (errorIs(error, 'P2025')) {
|
||||||
|
reply.callNotFound()
|
||||||
|
} else {
|
||||||
|
request.log.error(error)
|
||||||
|
const e = new httpError('unexpected error', 500, '500')
|
||||||
|
reply.send(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const notFoundHandler = (
|
||||||
|
request: FastifyRequest,
|
||||||
|
reply: FastifyReply
|
||||||
|
) => {
|
||||||
|
reply.send(notFoundError())
|
||||||
|
}
|
||||||
|
|
||||||
|
class httpError extends Error {
|
||||||
|
code: string
|
||||||
|
statusCode: number
|
||||||
|
constructor(message: string, statusCode: number, code: string) {
|
||||||
|
super(message)
|
||||||
|
this.name = this.constructor.name
|
||||||
|
Error.captureStackTrace(this, this.constructor)
|
||||||
|
this.statusCode = statusCode
|
||||||
|
this.code = code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const notFoundError = () => {
|
||||||
|
return new httpError('Not Found', 404, '404')
|
||||||
|
}
|
||||||
|
|
||||||
|
const uniqueKeyError = (msg: string, code = '4001') => {
|
||||||
|
return new httpError(msg, 422, code)
|
||||||
|
}
|
1
src/api/config/schemas/index.ts
Normal file
1
src/api/config/schemas/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export * from './wishlist'
|
53
src/api/config/schemas/wishlist.ts
Normal file
53
src/api/config/schemas/wishlist.ts
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
export const wishlistItemRequestSchema = {
|
||||||
|
type: 'object',
|
||||||
|
additionalProperties: false,
|
||||||
|
required: ['title', 'description'],
|
||||||
|
properties: {
|
||||||
|
title: { type: 'string' },
|
||||||
|
url: { type: 'string' },
|
||||||
|
imageSrc: { type: 'string' },
|
||||||
|
description: { type: 'string' },
|
||||||
|
comment: { type: 'string' },
|
||||||
|
bought: { type: 'boolean' },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const wishlistItemResponseSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: { type: 'number' },
|
||||||
|
title: { type: 'string' },
|
||||||
|
url: { type: 'string' },
|
||||||
|
imageSrc: { type: 'string' },
|
||||||
|
description: { type: 'string' },
|
||||||
|
comment: { type: 'string' },
|
||||||
|
bought: { type: 'boolean' },
|
||||||
|
wishlistId: { type: 'string' },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const wishlistRequestSchema = {
|
||||||
|
type: 'object',
|
||||||
|
additionalProperties: false,
|
||||||
|
required: ['title', 'imageSrc', 'slugUrlText'],
|
||||||
|
properties: {
|
||||||
|
title: { type: 'string' },
|
||||||
|
imageSrc: { type: 'string' },
|
||||||
|
description: { type: 'string' },
|
||||||
|
slugUrlText: { type: 'string' },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
export const wishlistResponseSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: { type: 'string' },
|
||||||
|
title: { type: 'string' },
|
||||||
|
imageSrc: { type: 'string' },
|
||||||
|
description: { type: 'string' },
|
||||||
|
slugUrlText: { type: 'string' },
|
||||||
|
items: {
|
||||||
|
type: 'array',
|
||||||
|
items: wishlistItemResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
|
@ -1,15 +1,13 @@
|
||||||
import { prisma } from '../../services'
|
import { prisma } from '../../services'
|
||||||
|
import { Wishlist, WishlistItem } from '@/types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getAll: async (): Promise<any> => {
|
getAll: async (): Promise<Wishlist[]> => {
|
||||||
return await prisma.client.wishlist.findMany({
|
return (await prisma.client.wishlist.findMany({
|
||||||
include: { items: false },
|
include: { items: false },
|
||||||
})
|
})) as Wishlist[]
|
||||||
},
|
},
|
||||||
getBySlugUrlText: async (
|
getBySlugUrlText: async (value: string, includeItems = false) => {
|
||||||
value: string,
|
|
||||||
includeItems = false
|
|
||||||
): Promise<any> => {
|
|
||||||
return await prisma.client.wishlist.findUnique({
|
return await prisma.client.wishlist.findUnique({
|
||||||
where: {
|
where: {
|
||||||
slugUrlText: value,
|
slugUrlText: value,
|
||||||
|
@ -17,6 +15,44 @@ export default {
|
||||||
include: { items: includeItems },
|
include: { items: includeItems },
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
create: async (payload: Wishlist) => {
|
||||||
|
return await prisma.client.wishlist.create({
|
||||||
|
data: payload,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
update: async (id: string, payload: Wishlist) => {
|
||||||
|
return await prisma.client.wishlist.update({
|
||||||
|
where: {
|
||||||
|
id: id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
...payload,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
delete: async (id: string) => {
|
||||||
|
return await prisma.client.wishlist.delete({
|
||||||
|
where: {
|
||||||
|
id: id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
createItem: async (wishlistId: string, payload: WishlistItem) => {
|
||||||
|
const wishlist = await prisma.client.wishlist.update({
|
||||||
|
where: {
|
||||||
|
id: wishlistId,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
items: {
|
||||||
|
create: {
|
||||||
|
...payload,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
include: { items: true },
|
||||||
|
})
|
||||||
|
return wishlist.items.pop()
|
||||||
|
},
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
updateItem: async (itemId: number, payload: any) => {
|
updateItem: async (itemId: number, payload: any) => {
|
||||||
return await prisma.client.item.update({
|
return await prisma.client.item.update({
|
||||||
|
@ -28,4 +64,11 @@ export default {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
deleteItem: async (itemId: number) => {
|
||||||
|
return await prisma.client.item.delete({
|
||||||
|
where: {
|
||||||
|
id: itemId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import { FastifyInstance } from 'fastify'
|
import { FastifyInstance } from 'fastify'
|
||||||
import { default as wishlistRoute } from './wishlist/'
|
import { default as wishlistRoute } from './wishlist/'
|
||||||
|
import { defaultErrorHandler, notFoundHandler } from '../config/errors'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
register: (app: FastifyInstance) => {
|
register: (app: FastifyInstance) => {
|
||||||
return app.register(
|
return app.register(
|
||||||
async (app) => {
|
async (app) => {
|
||||||
|
await app.setNotFoundHandler(notFoundHandler)
|
||||||
|
await app.setErrorHandler(defaultErrorHandler)
|
||||||
await app.register(wishlistRoute, { prefix: '/wishlist' })
|
await app.register(wishlistRoute, { prefix: '/wishlist' })
|
||||||
},
|
},
|
||||||
{ prefix: '/api' }
|
{ prefix: '/api' }
|
||||||
|
|
56
src/api/routes/wishlist/create.ts
Normal file
56
src/api/routes/wishlist/create.ts
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import { Wishlist, WishlistItem } from '@/types'
|
||||||
|
import { FastifyRequest, FastifyReply, RouteOptions } from 'fastify'
|
||||||
|
import { wishlist } from '../../models'
|
||||||
|
import {
|
||||||
|
wishlistItemRequestSchema,
|
||||||
|
wishlistItemResponseSchema,
|
||||||
|
wishlistRequestSchema,
|
||||||
|
wishlistResponseSchema,
|
||||||
|
} from '../../config/schemas'
|
||||||
|
|
||||||
|
interface createItemRequest extends FastifyRequest {
|
||||||
|
params: {
|
||||||
|
wishlistId: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createList = <RouteOptions>{
|
||||||
|
method: 'POST',
|
||||||
|
url: '/',
|
||||||
|
schema: {
|
||||||
|
body: wishlistRequestSchema,
|
||||||
|
response: {
|
||||||
|
201: wishlistResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
handler: async (request: FastifyRequest, reply: FastifyReply) => {
|
||||||
|
request.log.debug(request.body)
|
||||||
|
const item = await wishlist.create(request.body as Wishlist)
|
||||||
|
reply.code(201).send(item)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createItem = <RouteOptions>{
|
||||||
|
method: 'POST',
|
||||||
|
url: '/:wishlistId/item',
|
||||||
|
schema: {
|
||||||
|
body: wishlistItemRequestSchema,
|
||||||
|
params: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
wishlistId: { type: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
response: {
|
||||||
|
201: wishlistItemResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
handler: async (request: createItemRequest, reply: FastifyReply) => {
|
||||||
|
request.log.debug(request.body)
|
||||||
|
const item = await wishlist.createItem(
|
||||||
|
request.params.wishlistId,
|
||||||
|
request.body as WishlistItem
|
||||||
|
)
|
||||||
|
reply.code(201).send(item)
|
||||||
|
},
|
||||||
|
}
|
50
src/api/routes/wishlist/delete.ts
Normal file
50
src/api/routes/wishlist/delete.ts
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import { FastifyRequest, FastifyReply, RouteOptions } from 'fastify'
|
||||||
|
import { wishlist } from '../../models'
|
||||||
|
|
||||||
|
interface deleteRequest extends FastifyRequest {
|
||||||
|
params: {
|
||||||
|
wishlistId: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface deleteItemRequest extends FastifyRequest {
|
||||||
|
params: {
|
||||||
|
wishlistId: string
|
||||||
|
itemId: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteList = <RouteOptions>{
|
||||||
|
method: 'DELETE',
|
||||||
|
url: '/:wishlistId',
|
||||||
|
schema: {
|
||||||
|
params: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
wishlistId: { type: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
handler: async (request: deleteRequest, reply: FastifyReply) => {
|
||||||
|
await wishlist.delete(request.params.wishlistId)
|
||||||
|
reply.code(204).send()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteItem = <RouteOptions>{
|
||||||
|
method: 'DELETE',
|
||||||
|
url: '/:wishlistId/item/:itemId',
|
||||||
|
schema: {
|
||||||
|
params: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
wishlistId: { type: 'string' },
|
||||||
|
itemId: { type: 'number' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
handler: async (request: deleteItemRequest, reply: FastifyReply) => {
|
||||||
|
await wishlist.deleteItem(request.params.itemId)
|
||||||
|
reply.code(204).send()
|
||||||
|
},
|
||||||
|
}
|
|
@ -1,9 +1,16 @@
|
||||||
import { FastifyInstance } from 'fastify'
|
import { FastifyInstance } from 'fastify'
|
||||||
import { getAll, getBySlugUrl } from './read'
|
import { getAll, getBySlugUrl } from './read'
|
||||||
import { updateItem } from './update'
|
import { updateList, updateItem } from './update'
|
||||||
|
import { createList, createItem } from './create'
|
||||||
|
import { deleteList, deleteItem } from './delete'
|
||||||
|
|
||||||
export default async (app: FastifyInstance) => {
|
export default async (app: FastifyInstance) => {
|
||||||
await app.route(getAll)
|
await app.route(getAll)
|
||||||
await app.route(getBySlugUrl)
|
await app.route(getBySlugUrl)
|
||||||
|
await app.route(createList)
|
||||||
|
await app.route(createItem)
|
||||||
|
await app.route(updateList)
|
||||||
await app.route(updateItem)
|
await app.route(updateItem)
|
||||||
|
await app.route(deleteList)
|
||||||
|
await app.route(deleteItem)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,15 @@
|
||||||
import { FastifyRequest, FastifyReply, RouteOptions } from 'fastify'
|
import { FastifyRequest, FastifyReply, RouteOptions } from 'fastify'
|
||||||
import { wishlist } from '../../models'
|
import { wishlist } from '../../models'
|
||||||
|
import { wishlistResponseSchema } from '../../config/schemas'
|
||||||
|
|
||||||
export const getAll = <any>{
|
export const getAll = <RouteOptions>{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: '/',
|
url: '/',
|
||||||
schema: {
|
schema: {
|
||||||
response: {
|
response: {
|
||||||
200: {
|
200: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
items: {
|
items: wishlistResponseSchema,
|
||||||
properties: {
|
|
||||||
id: { type: 'string' },
|
|
||||||
title: { type: 'string' },
|
|
||||||
imageSrc: { type: 'string' },
|
|
||||||
description: { type: 'string' },
|
|
||||||
slugUrlText: { type: 'string' },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -36,31 +29,7 @@ export const getBySlugUrl = <RouteOptions>{
|
||||||
url: '/:slugText',
|
url: '/:slugText',
|
||||||
schema: {
|
schema: {
|
||||||
response: {
|
response: {
|
||||||
200: {
|
200: wishlistResponseSchema,
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
id: { type: 'string' },
|
|
||||||
title: { type: 'string' },
|
|
||||||
imageSrc: { type: 'string' },
|
|
||||||
description: { type: 'string' },
|
|
||||||
slugUrlText: { type: 'string' },
|
|
||||||
items: {
|
|
||||||
type: 'array',
|
|
||||||
items: {
|
|
||||||
properties: {
|
|
||||||
id: { type: 'number' },
|
|
||||||
title: { type: 'string' },
|
|
||||||
url: { type: 'string' },
|
|
||||||
imageSrc: { type: 'string' },
|
|
||||||
description: { type: 'string' },
|
|
||||||
comment: { type: 'string' },
|
|
||||||
bought: { type: 'boolean' },
|
|
||||||
wishlistId: { type: 'string' },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
handler: async (request: GetBySlugUrlTextRequest, reply: FastifyReply) => {
|
handler: async (request: GetBySlugUrlTextRequest, reply: FastifyReply) => {
|
||||||
|
@ -68,10 +37,7 @@ export const getBySlugUrl = <RouteOptions>{
|
||||||
if (list) {
|
if (list) {
|
||||||
return list
|
return list
|
||||||
} else {
|
} else {
|
||||||
return reply.code(404).send({
|
return reply.callNotFound()
|
||||||
error: 'notFound',
|
|
||||||
http: 404,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,58 +1,69 @@
|
||||||
|
import { Wishlist } from '@/types'
|
||||||
import { FastifyRequest, FastifyReply, RouteOptions } from 'fastify'
|
import { FastifyRequest, FastifyReply, RouteOptions } from 'fastify'
|
||||||
import { wishlist } from '../../models'
|
import { wishlist } from '../../models'
|
||||||
|
import {
|
||||||
|
wishlistRequestSchema,
|
||||||
|
wishlistResponseSchema,
|
||||||
|
wishlistItemRequestSchema,
|
||||||
|
wishlistItemResponseSchema,
|
||||||
|
} from '../../config/schemas'
|
||||||
|
|
||||||
interface GetBySlugUrlTextRequest extends FastifyRequest {
|
interface updateRequest extends FastifyRequest {
|
||||||
|
params: {
|
||||||
|
wishlistId: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface updateItemRequest extends FastifyRequest {
|
||||||
params: {
|
params: {
|
||||||
wishlistId: string
|
wishlistId: string
|
||||||
itemId: number
|
itemId: number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const updateList = <RouteOptions>{
|
||||||
|
method: 'PUT',
|
||||||
|
url: '/:wishlistId',
|
||||||
|
schema: {
|
||||||
|
body: wishlistRequestSchema,
|
||||||
|
params: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
wishlistId: { type: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
response: {
|
||||||
|
200: wishlistResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
handler: async (request: updateRequest, reply: FastifyReply) => {
|
||||||
|
request.log.debug(request.body)
|
||||||
|
const item = await wishlist.update(
|
||||||
|
request.params.wishlistId,
|
||||||
|
request.body as Wishlist
|
||||||
|
)
|
||||||
|
reply.code(201).send(item)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
export const updateItem = <RouteOptions>{
|
export const updateItem = <RouteOptions>{
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
url: '/:wishlistId/item/:itemId',
|
url: '/:wishlistId/item/:itemId',
|
||||||
schema: {
|
schema: {
|
||||||
body: {
|
body: wishlistItemRequestSchema,
|
||||||
|
params: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
additionalProperties: false,
|
|
||||||
properties: {
|
properties: {
|
||||||
title: { type: 'string' },
|
wishlistId: { type: 'string' },
|
||||||
url: { type: 'string' },
|
itemId: { type: 'number' },
|
||||||
image: { type: 'string' },
|
|
||||||
description: { type: 'string' },
|
|
||||||
comment: { type: 'string' },
|
|
||||||
bought: { type: 'boolean' },
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
response: {
|
response: {
|
||||||
204: {
|
200: wishlistItemResponseSchema,
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
id: { type: 'number' },
|
|
||||||
title: { type: 'string' },
|
|
||||||
url: { type: 'string' },
|
|
||||||
image: { type: 'string' },
|
|
||||||
description: { type: 'string' },
|
|
||||||
comment: { type: 'string' },
|
|
||||||
bought: { type: 'boolean' },
|
|
||||||
wishlistId: { type: 'string' },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
handler: async (request: GetBySlugUrlTextRequest, reply: FastifyReply) => {
|
handler: async (request: updateItemRequest, reply: FastifyReply) => {
|
||||||
request.log.debug(request.body)
|
request.log.debug(request.body)
|
||||||
const item = await wishlist.updateItem(
|
reply.send(await wishlist.updateItem(request.params.itemId, request.body))
|
||||||
Number(request.params.itemId),
|
|
||||||
request.body
|
|
||||||
)
|
|
||||||
if (item) {
|
|
||||||
return item
|
|
||||||
} else {
|
|
||||||
return reply.code(404).send({
|
|
||||||
error: 'notFound',
|
|
||||||
http: 404,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,12 @@ export interface WishlistItem {
|
||||||
wishlistId: boolean
|
wishlistId: boolean
|
||||||
}
|
}
|
||||||
export interface Wishlist {
|
export interface Wishlist {
|
||||||
id: string
|
id?: string
|
||||||
title: string
|
title: string
|
||||||
description: string
|
description: string
|
||||||
imageSrc: string
|
imageSrc: string
|
||||||
slugUrlText: string
|
slugUrlText: string
|
||||||
items: WishlistItem[]
|
items?: WishlistItem[]
|
||||||
}
|
}
|
||||||
export interface TileProp {
|
export interface TileProp {
|
||||||
title: string
|
title: string
|
||||||
|
|
|
@ -16,7 +16,7 @@ const { list, fetch, updateItem } = useWishlistStore()
|
||||||
await fetch(route.params.slug as string)
|
await fetch(route.params.slug as string)
|
||||||
|
|
||||||
const notBoughtItems = computed(() => {
|
const notBoughtItems = computed(() => {
|
||||||
return list.value?.items.filter(
|
return list.value?.items?.filter(
|
||||||
(item: WishlistItemType) => item.bought === false
|
(item: WishlistItemType) => item.bought === false
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
Loading…
Add table
Reference in a new issue