From db84498a9e1238b37729ba3d17aea5e6b50617fb Mon Sep 17 00:00:00 2001 From: HungLV46 <44282860+HungLV46@users.noreply.github.com> Date: Sat, 17 Aug 2024 17:09:49 +0700 Subject: [PATCH] feat: users APIs (#7) --- prisma/schema.prisma | 4 +- src/apis/routes/index.ts | 3 ++ src/apis/routes/products/create.ts | 7 ++- src/apis/routes/products/get.ts | 2 +- src/apis/routes/products/update.ts | 49 ++++++++++------- src/apis/routes/users/create.ts | 70 ++++++++++++++++++++++++ src/apis/routes/users/update.ts | 86 ++++++++++++++++++++++++++++++ 7 files changed, 196 insertions(+), 25 deletions(-) create mode 100644 src/apis/routes/users/create.ts create mode 100644 src/apis/routes/users/update.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index c1b3a46..5805576 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -25,8 +25,8 @@ model User { banner_img String additional_info Json? - products Product[] - user_attributes UserAttribute[] + products Product[] + attributes UserAttribute[] @@index(name) @@index(wallet_address) diff --git a/src/apis/routes/index.ts b/src/apis/routes/index.ts index 8e99636..28bd185 100644 --- a/src/apis/routes/index.ts +++ b/src/apis/routes/index.ts @@ -2,6 +2,9 @@ export * from '#apis/routes/products/update'; export * from '#apis/routes/products/create'; export * from '#apis/routes/products/get'; +export * from '#apis/routes/users/create'; +export * from '#apis/routes/users/update'; + // Search API export * from '#apis/routes/elastic-search/search'; diff --git a/src/apis/routes/products/create.ts b/src/apis/routes/products/create.ts index d23e5eb..778a7d9 100644 --- a/src/apis/routes/products/create.ts +++ b/src/apis/routes/products/create.ts @@ -3,7 +3,7 @@ import { prisma } from '#common/db'; import Joi from 'joi'; import { Prisma } from '@prisma/client'; -export const createProductionRoute: Hapi.ServerRoute = { +export const createProductRoute: Hapi.ServerRoute = { method: 'POST', path: '/products', options: { @@ -31,6 +31,7 @@ export const createProductionRoute: Hapi.ServerRoute = { value: Joi.string().required().example('attribute value'), }), ) + .optional() .default([]), metadata: Joi.object({ previews: Joi.array() @@ -41,7 +42,9 @@ export const createProductionRoute: Hapi.ServerRoute = { 'https://loremflickr.com/640/480?lock=1572275828555776', ]), cta_url: Joi.string().default('').example('https://www.google.com'), - }).required(), + }) + .optional() + .default({}), collections: Joi.array() .items( Joi.object({ diff --git a/src/apis/routes/products/get.ts b/src/apis/routes/products/get.ts index c6d5102..1bfb8eb 100644 --- a/src/apis/routes/products/get.ts +++ b/src/apis/routes/products/get.ts @@ -2,7 +2,7 @@ import Hapi from '@hapi/hapi'; import { prisma } from '#common/db'; import Joi from 'joi'; -export const getProductionRoute: Hapi.ServerRoute = { +export const getProductRoute: Hapi.ServerRoute = { method: 'GET', path: '/products/{id}', options: { diff --git a/src/apis/routes/products/update.ts b/src/apis/routes/products/update.ts index 40b2878..ff92082 100644 --- a/src/apis/routes/products/update.ts +++ b/src/apis/routes/products/update.ts @@ -3,7 +3,7 @@ import { prisma } from '#common/db'; import { Prisma } from '@prisma/client'; import Joi from 'joi'; -export const updateProductionRoute: Hapi.ServerRoute = { +export const updateProductRoute: Hapi.ServerRoute = { method: 'PUT', path: '/products/{id}', options: { @@ -17,35 +17,43 @@ export const updateProductionRoute: Hapi.ServerRoute = { }), payload: Joi.object({ // TODO make optional - name: Joi.string().required().example('Product name'), - owner_id: Joi.number().required().example(1), + name: Joi.string().optional().example('Product name'), + owner_id: Joi.number().optional().example(1), avatar_img: Joi.string() - .required() + .optional() .example('https://loremflickr.com/640/480?lock=1572275828555776'), banner_img: Joi.string() - .required() + .optional() .example('https://loremflickr.com/640/480?lock=1572275828555776'), - category: Joi.string().required().example('game'), - description: Joi.string().required().example('description of product'), - featured: Joi.boolean().default(false), + category: Joi.string().optional().example('game'), + description: Joi.string().optional().example('description of product'), + featured: Joi.boolean().optional().default(false).example('false'), attributes: Joi.array() .items( Joi.object({ - name: Joi.string().required().example('attribute name'), - value: Joi.string().required().example('attribute value'), + name: Joi.string().required(), + value: Joi.string().required(), }), ) - .default([]), + .optional() + .default([]) + .example([ + { name: 'attribute name', value: 'attribute value' }, + { name: 'attribute name 2', value: 'attribute value 2' }, + ]), metadata: Joi.object({ - previews: Joi.array() - .items(Joi.string()) - .default([]) - .example([ + previews: Joi.array().items(Joi.string()).optional().default([]), + cta_url: Joi.string().optional().default(''), + }) + .optional() + .default({}) + .example({ + previews: [ 'https://loremflickr.com/640/480?lock=1572275828555776', 'https://loremflickr.com/640/480?lock=1572275828555776', - ]), - cta_url: Joi.string().default('').example('https://www.google.com'), - }).required(), + ], + cta_url: 'https://www.google.com', + }), collections: Joi.array() .items( Joi.object({ @@ -53,6 +61,7 @@ export const updateProductionRoute: Hapi.ServerRoute = { contract_address: Joi.string().required(), }), ) + .optional() .default([]) .example([ { chain_id: '1', contract_address: '0x1234x' }, @@ -76,7 +85,7 @@ export const updateProductionRoute: Hapi.ServerRoute = { } as Prisma.ProductUpdateInput; const id = request.params.id; - // create new collections + // create new collections TODO refactor const existingCollections = await prisma.collection.findMany({ where: { OR: payload.collections as any /** TODO use where in */ }, }); @@ -97,7 +106,7 @@ export const updateProductionRoute: Hapi.ServerRoute = { }); } - // update product & create new product - collection relation + // update product & create new product - collection relation TODO refactor await Promise.all([ prisma.product.update({ where: { id: Number(request.params.id) }, diff --git a/src/apis/routes/users/create.ts b/src/apis/routes/users/create.ts new file mode 100644 index 0000000..db1aea5 --- /dev/null +++ b/src/apis/routes/users/create.ts @@ -0,0 +1,70 @@ +import Hapi from '@hapi/hapi'; +import { prisma } from '#common/db'; +import Joi from 'joi'; +import { Prisma } from '@prisma/client'; + +export const createUserionRoute: Hapi.ServerRoute = { + method: 'POST', + path: '/users', + options: { + description: 'Create a new user', + notes: 'Create user and its attributes', + tags: ['api', 'user', 'create'], + plugins: { 'hapi-swagger': {} }, + validate: { + payload: Joi.object({ + name: Joi.string().required().example('User name'), + bio: Joi.string().required().example('Bio'), + email: Joi.string().required().email().example('Hqk7S@example.com'), + wallet_address: Joi.string().required().example('0x1234567890'), + avatar_img: Joi.string() + .required() + .example('https://loremflickr.com/640/480?lock=1572275828555776'), + banner_img: Joi.string() + .required() + .example('https://loremflickr.com/640/480?lock=1572275828555776'), + attributes: Joi.array() + .items( + Joi.object({ + name: Joi.string().required().example('attribute name'), + value: Joi.string().required().example('attribute value'), + }), + ) + .default([]), + additional_info: Joi.object({ + headline: Joi.string().optional().example('headline'), + location: Joi.string().optional().example('location'), + socials: Joi.array() + .items( + Joi.object({ + name: Joi.string().required().example('twitter'), + url: Joi.string().required().example('https://twitter.com'), + }), + ) + .default([]), + }) + .optional() + .default({}), + }), + }, + // TODO validate response: { schema: Joi.object({}) } + }, + handler: async (request: Hapi.Request) => { + const payload = request.payload as any; + + // create new user + const userData = { + name: payload.name, + bio: payload.bio, + email: payload.email, + wallet_address: payload.wallet_address, + avatar_img: payload.avatar_img, + banner_img: payload.banner_img, + additional_info: payload.additional_info, + attributes: { create: payload.attributes }, + } as Prisma.UserCreateInput; + const user = await prisma.user.create({ data: userData }); + + return { data: { id: user.id } }; + }, +}; diff --git a/src/apis/routes/users/update.ts b/src/apis/routes/users/update.ts new file mode 100644 index 0000000..8e969de --- /dev/null +++ b/src/apis/routes/users/update.ts @@ -0,0 +1,86 @@ +import Hapi from '@hapi/hapi'; +import { prisma } from '#common/db'; +import { Prisma } from '@prisma/client'; +import Joi from 'joi'; + +export const updateUserRoute: Hapi.ServerRoute = { + method: 'PUT', + path: '/users/{id}', + options: { + description: 'Update user by its ID', + notes: 'Update user, its attributes and collections', + tags: ['api', 'user', 'update'], + plugins: { 'hapi-swagger': {} }, + validate: { + params: Joi.object({ + id: Joi.number().required().example(1), + }), + payload: Joi.object({ + name: Joi.string().optional().example('User name'), + bio: Joi.string().optional().example('Bio'), + email: Joi.string().optional().email().example('Hqk7S@example.com'), + wallet_address: Joi.string().optional().example('0x1234567890'), + avatar_img: Joi.string() + .optional() + .example('https://loremflickr.com/640/480?lock=1572275828555776'), + banner_img: Joi.string() + .optional() + .example('https://loremflickr.com/640/480?lock=1572275828555776'), + attributes: Joi.array() + .items( + Joi.object({ + name: Joi.string().required().example('attribute name'), + value: Joi.string().required().example('attribute value'), + }), + ) + .default([]), + additional_info: Joi.object({ + headline: Joi.string().optional().example('headline'), + location: Joi.string().optional().example('location'), + socials: Joi.array() + .optional() + .items( + Joi.object({ + name: Joi.string().required().example('twitter'), + url: Joi.string().required().example('https://twitter.com'), + }), + ) + .default([]) + .optional(), + }).optional(), + }), + }, + // response: { schema: Joi.object({}) } + }, + handler: async (request: Hapi.Request) => { + const id = Number(request.params.id); + const payload = request.payload as any; + + // create new user + const user = { + name: payload.name, + bio: payload.bio, + avatar_img: payload.avatar_img, + banner_img: payload.banner_img, + additional_info: payload.additional_info, + } as Prisma.UserCreateInput; + + await Promise.all([ + prisma.user.update({ + where: { id: Number(request.params.id) }, + data: user, + }), + prisma.$transaction([ + prisma.userAttribute.deleteMany({ where: { user_id: id } }), + prisma.userAttribute.createMany({ + data: payload.attributes.map((att: any) => ({ + ...att, + user_id: id, + })) as Prisma.UserAttributeCreateManyInput[], + }), + ]), + ]); + + return { data: { id } }; + }, +};