diff --git a/package.json b/package.json index 02df37b..476b5fe 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,8 @@ "@fastify/jwt": "^8.0.0", "@fastify/multipart": "^8.1.0", "@fastify/static": "^6.12.0", + "@fastify/swagger": "^8.14.0", + "@fastify/swagger-ui": "^2.1.0", "@prisma/client": "5.8.1", "@types/bcryptjs": "^2.4.6", "@types/express": "^4.17.21", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1e52066..339798f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,12 @@ dependencies: '@fastify/static': specifier: ^6.12.0 version: 6.12.0 + '@fastify/swagger': + specifier: ^8.14.0 + version: 8.14.0 + '@fastify/swagger-ui': + specifier: ^2.1.0 + version: 2.1.0 '@prisma/client': specifier: 5.8.1 version: 5.8.1(prisma@5.8.1) @@ -1076,6 +1082,28 @@ packages: p-limit: 3.1.0 dev: false + /@fastify/swagger-ui@2.1.0: + resolution: {integrity: sha512-mu0C28kMEQDa3miE8f3LmI/OQSmqaKS3dYhZVFO5y4JdgBIPbzZj6COCoRU/P/9nu7UogzzcCJtg89wwLwKtWg==} + dependencies: + '@fastify/static': 6.12.0 + fastify-plugin: 4.5.1 + openapi-types: 12.1.3 + rfdc: 1.3.1 + yaml: 2.3.4 + dev: false + + /@fastify/swagger@8.14.0: + resolution: {integrity: sha512-sGiznEb3rl6pKGGUZ+JmfI7ct5cwbTQGo+IjewaTvtzfrshnryu4dZwEsjw0YHABpBA+kCz3kpRaHB7qpa67jg==} + dependencies: + fastify-plugin: 4.5.1 + json-schema-resolver: 2.0.0 + openapi-types: 12.1.3 + rfdc: 1.3.1 + yaml: 2.3.4 + transitivePeerDependencies: + - supports-color + dev: false + /@humanwhocodes/config-array@0.11.14: resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} @@ -4060,6 +4088,17 @@ packages: fast-deep-equal: 3.1.3 dev: false + /json-schema-resolver@2.0.0: + resolution: {integrity: sha512-pJ4XLQP4Q9HTxl6RVDLJ8Cyh1uitSs0CzDBAz1uoJ4sRD/Bk7cFSXL1FUXDW3zJ7YnfliJx6eu8Jn283bpZ4Yg==} + engines: {node: '>=10'} + dependencies: + debug: 4.3.4 + rfdc: 1.3.1 + uri-js: 4.4.1 + transitivePeerDependencies: + - supports-color + dev: false + /json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} dev: true @@ -4511,6 +4550,10 @@ packages: mimic-fn: 4.0.0 dev: true + /openapi-types@12.1.3: + resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + dev: false + /optionator@0.9.3: resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} diff --git a/src/app.ts b/src/app.ts index 3d8590a..c5cc54e 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,4 +1,3 @@ -import fastify from 'fastify' import { userRoutes } from './controller/user/routes' import { env } from './env' import { ZodError } from 'zod' @@ -8,8 +7,14 @@ import cors from '@fastify/cors' import { logMiddleware } from './controller/middlewares/logMiddleware' import { projectRoutes } from './controller/project/routes' +import fastify from 'fastify' + +import { setupSwagger } from './swagger' export const app = fastify() + +setupSwagger(app) + app.register(cors, { origin: [env.FRONTEND_URL], }) diff --git a/src/controller/project/routes.ts b/src/controller/project/routes.ts index 90f10ce..56d051b 100644 --- a/src/controller/project/routes.ts +++ b/src/controller/project/routes.ts @@ -9,6 +9,9 @@ import fastifyStatic from '@fastify/static' import { getProjectsByTags } from './getProjectsByTags' import { editProject } from './editProjectById' import { deleteProjectById } from './deleteProjectById' +import getProjectByUserIdSchema from './swagger/getProjectsByUserIdSwagger.json' +import getProjectByIdSchema from './swagger/getProjectByIDSwagger.json' +import createProjectSwagger from './swagger/createProjectSwagger.json' export async function projectRoutes(app: FastifyInstance) { app.register(FastifyMultipart, { @@ -24,11 +27,11 @@ export async function projectRoutes(app: FastifyInstance) { }) app.post('/projects/tags', getProjectsByTags) - app.get('/projects/:userId', getProjectsByUserId) - app.get('/project/:projectId', getProjectsById) + app.get('/projects/:userId', getProjectByUserIdSchema, getProjectsByUserId) + app.get('/project/:projectId', getProjectByIdSchema, getProjectsById) app.post('/project/:projectId/photo', addImageProject) - app.post('/user/:userId/project', createProject) + app.post('/user/:userId/project', createProjectSwagger, createProject) app.put('/project/:projectId/edit', editProject) app.delete('/project/:projectId', deleteProjectById) diff --git a/src/controller/project/swagger/createProjectSwagger.json b/src/controller/project/swagger/createProjectSwagger.json new file mode 100644 index 0000000..3b1ba85 --- /dev/null +++ b/src/controller/project/swagger/createProjectSwagger.json @@ -0,0 +1,70 @@ +{ + "schema": { + "tags": [ + "Project" + ], + "summary": "Create Project", + "description": "Create a project based on the user ID.", + "operationId": "createProject", + "body": { + "title": "object", + "properties": { + "title": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "link": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "title", + "tags", + "link", + "description" + ], + "type": "object" + }, + "params": { + "type": "object", + "properties": { + "userId": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "userId" + ], + "type": "object" + }, + "response": { + "201": { + "description": "Project created successfully", + "type": "object", + "properties": { + "project": { + "type": "object" + } + } + }, + "409": { + "description": "User was not Found !", + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/src/controller/project/swagger/getProjectByIDSwagger.json b/src/controller/project/swagger/getProjectByIDSwagger.json new file mode 100644 index 0000000..302a7fc --- /dev/null +++ b/src/controller/project/swagger/getProjectByIDSwagger.json @@ -0,0 +1,47 @@ +{ + "schema": { + "tags": [ + "Project" + ], + "summary": "Get project by ID", + "description": "Retrieves project information based on the provided project ID.", + "operationId": "getProjectById", + "parameters": [ + { + "name": "projectId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "The ID of the project to retrieve." + } + ], + "responses": { + "200": { + "description": "Project found and returned successfully.", + "content": { + "application/json": { + "example": { + "project": { + "id": "example_uuid", + "name": "Example Project" + } + } + } + } + }, + "404": { + "description": "Project not found.", + "content": { + "application/json": { + "example": { + "error": "Project not found." + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/controller/project/swagger/getProjectsByUserIdSwagger.json b/src/controller/project/swagger/getProjectsByUserIdSwagger.json new file mode 100644 index 0000000..6c4536f --- /dev/null +++ b/src/controller/project/swagger/getProjectsByUserIdSwagger.json @@ -0,0 +1,51 @@ +{ + "schema": { + "tags": [ + "Project" + ], + "summary": "Get projects by user ID", + "operationId": "getProjectsByUserId", + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "The ID of the user to retrieve projects for." + } + ], + "responses": { + "200": { + "description": "Projects found and returned successfully.", + "content": { + "application/json": { + "example": { + "projects": [ + { + "id": "example_uuid_1", + "name": "Project 1" + }, + { + "id": "example_uuid_2", + "name": "Project 2" + } + ] + } + } + } + }, + "404": { + "description": "User not Found !", + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/src/controller/user/Swagger/getUserByIdSchema.json b/src/controller/user/Swagger/getUserByIdSchema.json new file mode 100644 index 0000000..1ecd1a8 --- /dev/null +++ b/src/controller/user/Swagger/getUserByIdSchema.json @@ -0,0 +1,42 @@ +{ + "schema": { + "tags": [ + "User" + ], + "summary": "Get user by ID", + "description": "Retrieves user information based on the provided user ID.", + "operationId": "getUserById", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + }, + "description": "The ID of the user to retrieve." + } + ], + "responses": { + "200": { + "description": "User found and returned successfully.", + "type": "object", + "properties": { + "message": { + "description": "User found and returned successfully" + } + } + }, + "404": { + "description": "User not found.", + "type": "object", + "properties": { + "message": { + "description": "User not found." + } + } + } + } + } +} \ No newline at end of file diff --git a/src/controller/user/Swagger/registerUserSchema.json b/src/controller/user/Swagger/registerUserSchema.json new file mode 100644 index 0000000..0b711e1 --- /dev/null +++ b/src/controller/user/Swagger/registerUserSchema.json @@ -0,0 +1,55 @@ +{ + "schema": { + "tags": [ + "User" + ], + "summary": "Register a new user", + "description": "Registers a new user with the provided information.", + "operationId": "registerUser", + "body": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "surname": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + }, + "password": { + "type": "string", + "minLength": 6 + } + }, + "required": [ + "name", + "surname", + "email", + "password" + ] + }, + "response": { + "201": { + "description": "User successfully registered", + "type": "object", + "properties": { + "message": { + "description": "No body returned for response" + } + } + }, + "409": { + "description": "E-mail already exists.", + "type": "object", + "properties": { + "message": { + "description": "E-mail already exists." + } + } + } + } + } +} \ No newline at end of file diff --git a/src/controller/user/routes.ts b/src/controller/user/routes.ts index c5387ce..8a2d1ad 100644 --- a/src/controller/user/routes.ts +++ b/src/controller/user/routes.ts @@ -5,6 +5,8 @@ import { registerUser } from './registerUser' import { editUserById } from './editUserById' import { addImageUser } from './addImageToUser' import FastifyMultipart from '@fastify/multipart' +import getUserByIdSchema from './Swagger/getUserByIdSchema.json' +import registerUserSchema from './Swagger/registerUserSchema.json' export async function userRoutes(app: FastifyInstance) { app.register(FastifyMultipart, { @@ -13,8 +15,8 @@ export async function userRoutes(app: FastifyInstance) { fileSize: 1000000, // the max file size in bytes }, }) - app.post('/user', registerUser) - app.get('/user/:id', getUserById) + app.post('/user', registerUserSchema, registerUser) + app.get('/user/:id', getUserByIdSchema, getUserById) app.get('/user', getUserByEmail) app.put('/user/:userId/edit', editUserById) app.post('/user/:userId/photo', addImageUser) diff --git a/src/swagger.ts b/src/swagger.ts new file mode 100644 index 0000000..5c0e399 --- /dev/null +++ b/src/swagger.ts @@ -0,0 +1,28 @@ +import fastifySwagger from '@fastify/swagger' +import fastifySwaggerUi from '@fastify/swagger-ui' + +export const setupSwagger = (app) => { + app.register(fastifySwagger, { + exposeRoute: true, + routePrefix: '/documentation', + swagger: { + info: { + title: 'Test swagger - Orange Portfolio', + description: 'Testing the Fastify swagger API', + version: '1.0.0', + }, + }, + }) + + app.register(fastifySwaggerUi, { + routePrefix: '/documentation', + swagger: { + info: { + title: 'Test swagger - Orange Portfolio', + description: 'Testing the Fastify swagger API', + version: '1.0.0', + }, + }, + exposeRoute: true, + }) +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 1b7fc4d..20953c3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -36,7 +36,7 @@ // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ + "resolveJsonModule": true, /* Enable importing .json files. */ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ /* JavaScript Support */