diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2febc00 --- /dev/null +++ b/.gitignore @@ -0,0 +1,118 @@ +# User-specific stuff +.idea/ + +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +/y.js +/package-lock.json diff --git a/README.md b/README.md index e69de29..45f538b 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,3 @@ +# Simple ReST API + +See code for details... \ No newline at end of file diff --git a/helpers/http.js b/helpers/http.js new file mode 100644 index 0000000..cf2947f --- /dev/null +++ b/helpers/http.js @@ -0,0 +1,34 @@ +/** + * + * @param type + * @param message + * @returns {{code: string, type: null, message: null, status: string}} + */ +export const setResp400 = (type = null, message = null) => ({ + code: '400 Bad Request', + status: 'Rejected', + type: type, + message: message, +}) + +/** + * + * @param message + * @returns {{code: string, message: null, status: string}} + */ +export const setResp403 = (message = null) => ({ + code: '403 Forbidden', + status: 'Forbidden', + message: message, +}) + +/** + * + * @returns {{code: string, type: string, message: string, status: string}} + */ +export const setResp500 = () => ({ + code: '500 Internal Server Error', + status: 'Error', + type: 'Critical error', + message: 'Try reload the page in a while', +}) \ No newline at end of file diff --git a/helpers/utils.js b/helpers/utils.js new file mode 100644 index 0000000..30df232 --- /dev/null +++ b/helpers/utils.js @@ -0,0 +1,7 @@ +/** + * + * @param reduced + * @param input + * @returns {{}} + */ +export const extractProps = (reduced, input) => Object.keys(reduced).reduce((a, b) => (a[b] = input[b], a), {}) \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..2e8e869 --- /dev/null +++ b/index.js @@ -0,0 +1,25 @@ +import express from 'express' +import router from './routes/router.js' + +const PORT = 1976 +const app = express() + +app.disable('etag') +app.use((req, res, next) => { + res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate') + res.header('Expires', '-1') + res.header('Pragma', 'no-cache') + next() +}) +app.use(express.json()) +app.use('/', router) + +const startServer = async () => { + try { + app.listen(PORT, () => console.log('Server is running on port: ' + PORT)) + } catch (e) { + console.error(e) + } +} + +await startServer() \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..e35f11f --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "restfulapi", + "version": "0.0.1", + "description": "NodeJS application having a few ReSTFul endpoints", + "main": "index.js", + "type": "module", + "scripts": { + "start": "node index.js", + "dev": "nodemon index.js" + }, + "repository": { + "type": "git", + "url": "ssh://git@git.amok.space:8822/yevhen/restful-api-task.git" + }, + "keywords": [ + "ReST API", + "HTTP", + "CRUD", + "Todolist", + "NodeJS" + ], + "author": "Yevhen theAmok", + "license": "ISC", + "dependencies": { + "express": "^4.17.1", + "yup": "^0.32.11" + }, + "devDependencies": { + "nodemon": "^2.0.13" + } +} diff --git a/repositories/data.json b/repositories/data.json new file mode 100644 index 0000000..a69f2c7 --- /dev/null +++ b/repositories/data.json @@ -0,0 +1,290 @@ +[ + { + "id": 1, + "title": "Western carpet python", + "category": "quote", + "archive": false, + "content": "I'll parse the online AI firewall, that should bandwidth the USB alarm!", + "created_at": "2021-08-29T01:56:20.756Z", + "updated_at": "2021-10-03T17:42:27.242Z" + }, + { + "id": 2, + "title": "Bornean pitviper", + "category": "random_thought", + "archive": false, + "content": "If we program the microchip, we can get to the RSS bandwidth through the 1080p SMS matrix!", + "created_at": "2021-07-15T09:37:47.284Z", + "updated_at": "2021-08-06T01:33:55.383Z" + }, + { + "id": 3, + "title": "Cat snake", + "category": "idea", + "archive": false, + "content": "I'll parse the haptic SMTP pixel, that should panel the AGP microchip!", + "created_at": "2021-09-04T10:25:06.090Z", + "updated_at": "2021-08-06T17:54:52.519Z" + }, + { + "id": 4, + "title": "Bluntnose viper", + "category": "idea", + "archive": true, + "content": "The RSS port is down, synthesize the auxiliary interface so we can generate the GB array!", + "created_at": "2021-08-31T02:48:43.764Z", + "updated_at": null + }, + { + "id": 5, + "title": "Scarlet kingsnake", + "category": "idea", + "archive": true, + "content": "overriding the array won't do anything, we need to navigate the haptic SSL feed!", + "created_at": "2021-09-17T10:21:26.174Z", + "updated_at": "2021-07-07T18:14:54.247Z" + }, + { + "id": 6, + "title": "Green mamba", + "category": "task", + "archive": true, + "content": "The HDD driver is down, generate the back-end panel so we can calculate the THX program!", + "created_at": "2021-08-24T14:52:07.315Z", + "updated_at": null + }, + { + "id": 7, + "title": "White-lipped tree viper", + "category": "idea", + "archive": true, + "content": "Use the solid state SQL panel, then you can back up the redundant transmitter!", + "created_at": "2021-07-22T16:08:28.099Z", + "updated_at": null + }, + { + "id": 8, + "title": "Mangrove snake", + "category": "idea", + "archive": false, + "content": "Try to synthesize the AGP feed, maybe it will copy the open-source hard drive!", + "created_at": "2021-08-11T06:04:47.752Z", + "updated_at": null + }, + { + "id": 9, + "title": "Sea snake", + "category": "random_thought", + "archive": true, + "content": "Try to parse the THX protocol, maybe it will compress the bluetooth hard drive!", + "created_at": "2021-08-20T22:52:54.944Z", + "updated_at": null + }, + { + "id": 10, + "title": "Cyclades blunt-nosed viper", + "category": "random_thought", + "archive": false, + "content": "The SQL hard drive is down, navigate the redundant program so we can bypass the JSON matrix!", + "created_at": "2021-09-15T10:43:02.014Z", + "updated_at": "2021-08-07T07:06:51.050Z" + }, + { + "id": 11, + "title": "Western carpet python", + "category": "quote", + "archive": false, + "content": "I'll parse the online AI firewall, that should bandwidth the USB alarm!", + "created_at": "2021-09-10T15:30:21.732Z", + "updated_at": "2021-10-14T22:37:50.128Z" + }, + { + "id": 12, + "title": "Indian python", + "category": "task", + "archive": false, + "content": "Try to compress the SAS firewall, maybe it will override the solid state capacitor!", + "created_at": "2021-07-11T07:26:31.840Z", + "updated_at": "2021-09-04T16:11:18.316Z" + }, + { + "id": 13, + "title": "Dwarf pipe snake", + "category": "random_thought", + "archive": false, + "content": "I'll compress the 1080p HDD bandwidth, that should hard drive the AI system!", + "created_at": "2021-09-16T21:55:09.955Z", + "updated_at": "2021-08-16T18:39:41.482Z" + }, + { + "id": 14, + "title": "Australian scrub python", + "category": "task", + "archive": true, + "content": "Try to compress the TCP sensor, maybe it will input the bluetooth feed!", + "created_at": "2021-09-20T04:20:47.013Z", + "updated_at": "2021-07-02T21:43:14.712Z" + }, + { + "id": 15, + "title": "Baja California lyresnake", + "category": "quote", + "archive": true, + "content": "I'll navigate the solid state SMTP monitor, that should application the SCSI driver!", + "created_at": "2021-08-24T23:03:13.707Z", + "updated_at": null + }, + { + "id": 16, + "title": "Yellow anaconda", + "category": "quote", + "archive": false, + "content": "The ADP port is down, copy the online protocol so we can reboot the SMTP microchip!", + "created_at": "2021-07-18T08:59:58.608Z", + "updated_at": "2021-09-02T01:06:46.953Z" + }, + { + "id": 17, + "title": "Brown water python", + "category": "quote", + "archive": true, + "content": "Try to connect the JSON interface, maybe it will input the haptic application!", + "created_at": "2021-09-22T02:27:11.017Z", + "updated_at": "2021-07-21T17:23:13.134Z" + }, + { + "id": 18, + "title": "Red-bellied black snake", + "category": "random_thought", + "archive": true, + "content": "I'll override the optical SAS program, that should feed the XML panel!", + "created_at": "2021-07-06T02:49:11.318Z", + "updated_at": null + }, + { + "id": 19, + "title": "Green snake", + "category": "quote", + "archive": false, + "content": "You can't generate the alarm without programming the online RSS pixel!", + "created_at": "2021-07-19T06:42:56.112Z", + "updated_at": "2021-09-25T11:48:31.274Z" + }, + { + "id": 20, + "title": "King rat snake", + "category": "idea", + "archive": false, + "content": "The FTP pixel is down, generate the virtual capacitor so we can copy the JBOD application!", + "created_at": "2021-09-26T09:32:25.827Z", + "updated_at": null + }, + { + "id": 21, + "title": "Northern white-lipped python", + "category": "quote", + "archive": false, + "content": "Try to transmit the ADP matrix, maybe it will parse the bluetooth port!", + "created_at": "2021-08-04T18:57:45.351Z", + "updated_at": null + }, + { + "id": 22, + "title": "King brown", + "category": "task", + "archive": true, + "content": "I'll hack the haptic USB monitor, that should bus the JBOD transmitter!", + "created_at": "2021-08-27T21:45:28.945Z", + "updated_at": null + }, + { + "id": 23, + "title": "Grass snake", + "category": "random_thought", + "archive": false, + "content": "The GB array is down, generate the solid state port so we can copy the GB sensor!", + "created_at": "2021-09-07T02:55:30.896Z", + "updated_at": "2021-09-26T21:01:21.378Z" + }, + { + "id": 24, + "title": "Red-tailed bamboo pitviper", + "category": "quote", + "archive": false, + "content": "I'll program the online SSL panel, that should protocol the RSS capacitor!", + "created_at": "2021-07-23T10:13:48.120Z", + "updated_at": null + }, + { + "id": 25, + "title": "Indian tree viper", + "category": "random_thought", + "archive": false, + "content": "You can't copy the pixel without bypassing the 1080p SMTP card!", + "created_at": "2021-08-07T19:47:49.200Z", + "updated_at": null + }, + { + "id": 26, + "title": "Corn snake", + "category": "random_thought", + "archive": true, + "content": "If we copy the capacitor, we can get to the USB sensor through the online RAM card!", + "created_at": "2021-07-24T09:30:24.222Z", + "updated_at": "2021-09-16T19:03:48.529Z" + }, + { + "id": 27, + "title": "Blanding's tree snake", + "category": "random_thought", + "archive": false, + "content": "copying the transmitter won't do anything, we need to program the digital TCP driver!", + "created_at": "2021-08-09T11:17:56.670Z", + "updated_at": null + }, + { + "id": 28, + "title": "Rattlesnake", + "category": "quote", + "archive": true, + "content": "The RAM array is down, calculate the 1080p sensor so we can hack the JBOD bus!", + "created_at": "2021-09-01T22:55:33.156Z", + "updated_at": "2021-07-12T11:28:04.735Z" + }, + { + "id": 29, + "title": "Khasi Hills keelback", + "category": "idea", + "archive": true, + "content": "If we parse the driver, we can get to the HDD port through the solid state CSS port!", + "created_at": "2021-07-28T15:50:20.241Z", + "updated_at": null + }, + { + "id": 30, + "title": "Red spitting cobra", + "category": "task", + "archive": true, + "content": "The AI circuit is down, synthesize the auxiliary port so we can generate the JBOD panel!", + "created_at": "2021-07-17T00:35:58.760Z", + "updated_at": "2021-10-06T11:31:10.150Z" + }, + { + "id": 31, + "title": "Southern Indonesian spitting cobra", + "category": "random_thought", + "archive": false, + "content": "If we hack the bus, we can get to the TCP capacitor through the 1080p CSS transmitter!", + "created_at": "2021-08-17T12:04:24.277Z", + "updated_at": null + }, + { + "id": 32, + "title": "Cat snake", + "category": "task", + "archive": false, + "content": "bypassing the system won't do anything, we need to quantify the open-source RAM matrix!", + "created_at": "2021-10-14T13:03:11.921Z", + "updated_at": null + } +] \ No newline at end of file diff --git a/repositories/schema.js b/repositories/schema.js new file mode 100644 index 0000000..9df6cd6 --- /dev/null +++ b/repositories/schema.js @@ -0,0 +1,24 @@ +import { object, boolean, date, number, string } from 'yup' + +export const webFields = { title: null, category: null, archive: null, content: null } +export const idOnlySchema = number().integer().positive().min(1).max(1024).required() + +const initialSchema = { + updated_at: string().nullable().default(null), + created_at: date().default(() => new Date()), + content: string().ensure().max(1024), + archive: boolean().nullable().default(false), + category: string().matches(/^(random_thought|idea|task|quote)$/).required(), + title: string().trim().min(1).max(127).required(), + id: string().nullable().default(null), +} + +/** + * Combine exportable schemas using previously created chunks along with adding a new ones dynamically + */ + +export const newNoteSchema = object(initialSchema) + +initialSchema.id = idOnlySchema +initialSchema.updated_at = date().default(() => new Date()) +export const updateNoteSchema = object(initialSchema) diff --git a/routes/methods/delete.js b/routes/methods/delete.js new file mode 100644 index 0000000..b3709e6 --- /dev/null +++ b/routes/methods/delete.js @@ -0,0 +1,17 @@ +import { setResp500 } from '../../helpers/http.js' +import NotesService from '../../services/NotesService.js' + +/** + * + * @type {NotesService} + */ +const db = new NotesService() + +export const deleteNote = async (req, res) => { + try{ + const data = await db.deleteNote(req.validatedData) + res.status(data.code || 200).json(data) + }catch (e) { + res.status(500).json(setResp500()) + } +} \ No newline at end of file diff --git a/routes/methods/get.js b/routes/methods/get.js new file mode 100644 index 0000000..0ef9a99 --- /dev/null +++ b/routes/methods/get.js @@ -0,0 +1,65 @@ +import { setResp403, setResp500 } from '../../helpers/http.js' +import NotesService from '../../services/NotesService.js' + +/** + * + * @type {NotesService} + */ +const db = new NotesService() + +/** + * + * @param req + * @param res + * @returns {Promise} + */ +export const setForbidden = async (req, res) => { + const ip = req.headers['x-real-ip'] || req.connection.remoteAddress + res.status(403).json(setResp403(`You are not allowed to access the resource. Your IP is ${ip}`)) +} + +/** + * + * @param req + * @param res + * @returns {Promise} + */ +export const getAllNotes = async (req, res) => { + try{ + const data = await db.getNotes + res.status(data.code || 200).json(data) + }catch (e) { + res.status(500).json(setResp500()) + } +} + +/** + * + * @param req + * @param res + * @returns {Promise} + */ +export const getSingleNote = async (req, res) => { + + try{ + const data = await db.getSingle(req.validatedData) + res.status(data.code || 200).json(data) + }catch (e) { + res.status(500).json(setResp500()) + } +} + +/** + * + * @param req + * @param res + * @returns {Promise} + */ +export const getStats = (req, res) => { + try{ + const data = db.getStats + res.status(data.code || 200).json( data ) + }catch (e) { + res.status(500).json(setResp500()) + } +} \ No newline at end of file diff --git a/routes/methods/patch.js b/routes/methods/patch.js new file mode 100644 index 0000000..1455bfb --- /dev/null +++ b/routes/methods/patch.js @@ -0,0 +1,18 @@ +import { setResp500 } from '../../helpers/http.js' +import NotesService from '../../services/NotesService.js' + +/** + * + * @type {NotesService} + */ +const db = new NotesService() + +export const editNote = async (req, res) => { + + try{ + const data = await db.updateNote(req.validatedData) + res.status(data.code || 200).json(data) + }catch (e) { + res.status(500).json(setResp500()) + } +} \ No newline at end of file diff --git a/routes/methods/post.js b/routes/methods/post.js new file mode 100644 index 0000000..6c826cb --- /dev/null +++ b/routes/methods/post.js @@ -0,0 +1,18 @@ +import { setResp500 } from '../../helpers/http.js' +import NotesService from '../../services/NotesService.js' + +/** + * + * @type {NotesService} + */ +const db = new NotesService() + +export const addNote = async (req, res) => { + + try{ + const data = await db.insertNote(req.validatedData) + res.status(data.code || 200).json(data) + }catch (e) { + res.status(500).json(setResp500()) + } +} \ No newline at end of file diff --git a/routes/router.js b/routes/router.js new file mode 100644 index 0000000..bf5886a --- /dev/null +++ b/routes/router.js @@ -0,0 +1,20 @@ +import { Router } from 'express' +import { addNote } from './methods/post.js' +import { getAllNotes, getSingleNote, getStats, setForbidden } from './methods/get.js' +import { editNote } from './methods/patch.js' +import { deleteNote } from './methods/delete.js' +import { id as validate } from '../services/ValidationService.js' +import { idOnlySchema, newNoteSchema, updateNoteSchema } from '../repositories/schema.js' + +const router = new Router() + +router.get('/notes(\/|)$/', getAllNotes) +router.get('/notes\/stats(\/|)$/', getStats) +router.get('/notes/:id([0-9]{1,4})$/', validate( idOnlySchema ), getSingleNote) // e.g. also notes with `0x1a` => id = 26 will be affected +router.get('*', setForbidden) + +router.post('/notes(\/|)$/', validate( newNoteSchema ), addNote) +router.patch('/notes/:id([0-9]{1,4})$/', validate( updateNoteSchema ), editNote) +router.delete('/notes/:id([0-9]{1,4})$/', validate( idOnlySchema ), deleteNote) + +export default router \ No newline at end of file diff --git a/services/NotesService.js b/services/NotesService.js new file mode 100644 index 0000000..39665bb --- /dev/null +++ b/services/NotesService.js @@ -0,0 +1,182 @@ +//const data = require('../repositories/data.json') +import * as path from 'path' +import * as fs from 'fs' + +class NotesService { + dbPath + db + limit + + constructor () { + + this.dbPath = path.resolve('./repositories/data.json') + + try { + const data = fs.readFileSync(this.dbPath, 'utf8') + this.db = JSON.parse(data || '[]') + } catch (e) { + this.db = [] + } + + this.limit = 7 + + } + + /** + * USE AT FINAL STAGE BEFORE RETURN FROM THE METHOD TO AVOID HELTER SKELTER IN THE RESULT + * + * @param result + * @param query + * @returns {*|{result: null, data: *[], query: null, message: string, type: string, status: string}} + * @private + */ + _composeResponse (result, query) { + + const response = { + code: 200, + status: 'Accepted', + message: `The database contains ${this.db.length} notes`, + result: `Matched ${result.length} note(s)`, + query: query, + type: 'Completed', + data: [], + } + + if (result.length === 1) { + response.data = result[0] + } else if (result.length > 1) { + response.data = result + } else { + response.code = 404 + response.type = 'Warning' + } + + return response + } + + /** + * + * @param id + * @returns {*|{result: null, data: *[], query: null, message: string, type: string, status: string}} + */ + getSingle (id) { + //, returned LIMIT ${this.limit} ORDER BY id DESC + const note = this.db.filter(el => el.id === +id) + return this._composeResponse(note, `id = ${id} LIMIT 1`) + } + + /** + * + * @returns {*|{result: null, data: *[], query: null, message: string, type: string, status: string}} + */ + get getNotes () { + const notes = this.db.sort((a, b) => new Date(a.created_at) > new Date(b.created_at) ? -1 : 1) + return this._composeResponse(notes.slice(0, this.limit), `ORDER BY created_at DESC LIMIT ` + this.limit) + } + + /** + * + * @param id + * @returns {Promise<*|{result: null, data: *[], query: null, message: string, type: string, status: string}>} + */ + async deleteNote (id) { + const deletedNote = this.db.filter(note => note.id === +id) + + if (deletedNote.length === 1) { + this.db = this.db.filter(note => note.id !== +id) + await this.saveDatabase() + } + + return this._composeResponse(deletedNote, `DELETED FROM db WHERE id = ${id}`) + } + + /** + * + * @param data + * @returns {Promise<*|{result: null, data: *[], query: null, message: string, type: string, status: string}>} + */ + async insertNote( data ){ + + const notes = this.db.sort((a, b) => new Date(a.id) > new Date(b.id) ? -1 : 1) + data.id = ( notes.length >= 1 ) ? notes[0].id + 1 : 1 + this.db.push(data) + await this.saveDatabase() + + return this._composeResponse([data], `INSERT INTO database SET id = ${data.id} (AI)`) + } + + /** + * + * @param data + * @returns {Promise<*|{result: null, data: *[], query: null, message: string, type: string, status: string}>} + */ + async updateNote( data ){ + + const noteExists = this.db.find(note => note.id === +data.id) + + if( noteExists ){ + noteExists.title = data.title + noteExists.content = data.content + noteExists.category = data.category + noteExists.archive = data.archive + noteExists.updated_at = data.updated_at + + await this.saveDatabase() + return this._composeResponse([noteExists], `UPDATED WHERE id = ${data.id}`) + } + + return this._composeResponse([], `UPDATED WHERE id = ${data.id}`) + + + } + + /** + * + * @returns {Promise} + */ + async saveDatabase () { + try { + await fs.promises.writeFile(this.dbPath, JSON.stringify(this.db, null, 2), { encoding: 'utf-8' }) + } catch (e) { + console.error(e) + } + } + + /** + * + * @returns {{result: string, data: {}, query: string, message: string, status: string}} + */ + get getStats () { + + const stats = {} + const cats = [...new Set(this.db.map(item => item.category))] + + if (typeof cats[0] !== 'undefined') { + const isArchive = this.db.map(item => { + const container = {} + container[item.category] = item.archive + return container + }) + + cats.forEach((cat, i) => { + stats[cat] = { + total: this.db.filter(el => el.category === cat).length, + archived: isArchive.filter(el => el[cat] === true).length, + active: isArchive.filter(el => el[cat] === false).length, + } + }) + } + + return { + code: 200, + status: 'Accepted', + message: `The database contains ${this.db.length} notes`, + result: `Aggregated data statistics`, + query: `GROUP BY categories, (active|archived)`, + type: 'Completed', + data: stats, + } + } +} + +export default NotesService \ No newline at end of file diff --git a/services/ValidationService.js b/services/ValidationService.js new file mode 100644 index 0000000..6dec0d9 --- /dev/null +++ b/services/ValidationService.js @@ -0,0 +1,32 @@ +import { setResp400 } from '../helpers/http.js' +import { extractProps } from '../helpers/utils.js' +import {webFields} from '../repositories/schema.js' + +/** + * + * @param schema + * @returns {(function(*, *, *): Promise<*|undefined>)|*} + */ +export const id = (schema) => async (req, res, next) => { + + let objToValidate + + if (req.method === 'POST' || req.method === 'PATCH') { + + if (req.method === 'PATCH'){ + webFields.id = null + } + + objToValidate = extractProps(webFields, req.body) + }else{ + const { id } = req.params + objToValidate = id + } + + try { + req.validatedData = await schema.validate(objToValidate) + next() + } catch (error) { + return res.status(400).json(setResp400(error.name || null, error.message || null )) + } +}