diff --git a/server/api/controllers/auth.controller.js b/server/api/controllers/auth.controller.js index 913a6e7..17eee30 100644 --- a/server/api/controllers/auth.controller.js +++ b/server/api/controllers/auth.controller.js @@ -187,7 +187,7 @@ exports.create_new_user = async (req, res) => { data: userFiltered, }); } catch { - res.status(400).json({ + res.status(500).json({ success: false, message: "General Error Creating new account", data: null, diff --git a/server/docs/auth/create-user.js b/server/docs/auth/create-user.js new file mode 100644 index 0000000..04af59c --- /dev/null +++ b/server/docs/auth/create-user.js @@ -0,0 +1,34 @@ +module.exports = { + post: { + tags: ["Auth CRUD Operations"], + description: "Creates a new user.", + operationId: "Create new user", + parameters: [], + requestBody: { + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/createUser", + }, + }, + }, + }, + }, + responses: { + 400: { + description: "Please provide all required fields", + description: "The entered passwords do not match!", + description: + "Your password must be at least 6 characters long and contain a lowercase letter, an uppercase letter, a numeric digit and a special character.", + description: "You need to accept the terms of use.", + description: "Email address has invalid format", + description: "Error creating user", + }, + 201: { + description: "User created", + }, + 500: { + description: "General Error Creating new account", + }, + }, +}; diff --git a/server/docs/auth/index.js b/server/docs/auth/index.js new file mode 100644 index 0000000..d9e7fe8 --- /dev/null +++ b/server/docs/auth/index.js @@ -0,0 +1,13 @@ +const login = require("./login-user"); +const createUser = require("./create-user"); + +module.exports = { + paths: { + "/login": { + ...login, + }, + "/create-new-user": { + ...createUser, + }, + }, +}; diff --git a/server/docs/auth/login-user.js b/server/docs/auth/login-user.js new file mode 100644 index 0000000..d8e6763 --- /dev/null +++ b/server/docs/auth/login-user.js @@ -0,0 +1,29 @@ +module.exports = { + post: { + tags: ["Auth CRUD Operations"], + description: "Login to app and create session.", + operationId: "login", + parameters: [], + requestBody: { + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/loginInput", + }, + }, + }, + }, + }, + responses: { + 400: { + description: "The provided email is not registered.", + description: "Email and password do not match.", + }, + 200: { + description: "Successfully logged in", + }, + 500: { + description: "Something went wrong.", + }, + }, +}; diff --git a/server/docs/basicInfo.js b/server/docs/basicInfo.js new file mode 100644 index 0000000..68affb4 --- /dev/null +++ b/server/docs/basicInfo.js @@ -0,0 +1,11 @@ +module.exports = { + openapi: "3.0.3", + info: { + title: "APC Nodejs API", + description: "Services for APC", + version: "1.0.0", + contact: { + name: "Ben Akehurst", + }, + }, +}; diff --git a/server/docs/components.js b/server/docs/components.js new file mode 100644 index 0000000..9ffb503 --- /dev/null +++ b/server/docs/components.js @@ -0,0 +1,183 @@ +module.exports = { + components: { + schemas: { + // User Model + user: { + type: "object", + properties: { + firstName: { + type: "string", + description: "User's first name.", + example: "John", + }, + lastName: { + type: "string", + description: "User's last name.", + example: "Doe", + }, + email: { + type: "string", + description: "User's email address.", + example: "john@doe.com", + }, + password: { + type: "string", + description: + "Password for user account. password must be at least 6 characters long and contain a lowercase letter, an uppercase letter, a numeric digit and a special character.", + example: "Abc123!@", + }, + acceptedTerms: { + type: "boolean", + description: "Confirms the user accepts t's & c's on sign up.", + example: "true", + }, + createdOnDate: { + type: "boolean", + description: + "A human readable format of the date the user was created.", + example: "12/11/2021", + }, + userUID: { + type: "string", + description: "A UUID for the user object.", + example: "8eac14c0-83b4-46f7-a9ef-bef5ded8997f", + }, + userAcquisitionLocation: { + type: "string", + description: + "Defines where the user opened their account, via the sign up form or using goolge credentials.", + example: "Manual Registration Form", + }, + trackedItems: { + type: "array", + description: "Array of object ids of items the user is tracking.", + example: "[]", + }, + isAdmin: { + type: "boolean", + description: "Defines if the user is an admin.", + example: "false", + }, + userActive: { + type: "boolean", + description: "Defines if the user confirmed their email address.", + example: "false", + }, + }, + }, + // Item Model + singleItem: { + type: "object", + properties: { + name: { + type: "string", + description: "Item name.", + example: "Samsung-LC32R500FHRXXU-Curved-FullHD-Monitor", + }, + link: { + type: "string", + description: "Valid Amazon url of item.", + example: + "https://www.amazon.co.uk/Samsung-LC32R500FHRXXU-Curved-FullHD-Monitor/dp/B08WXCZT4Y", + }, + imgUrl: { + type: "string", + description: "Scraped url for main image url.", + example: + "https://m.media-amazon.com/images/I/81kfNKhZp+L._AC_SX300_SY300_.jpg", + }, + currentPrice: { + type: "number", + description: + "Current item price rounded to the nearest whole number.", + example: "179", + }, + targetPrice: { + type: "number", + description: + "A user defined number the user would like to pay for the item.", + example: "175", + }, + asin: { + type: "string", + description: "Amazon SKU.", + example: "B08WXCZT4Y", + }, + rating: { + type: "string", + description: "Current rating.", + example: "4.5 out of 5 stars", + }, + following: { + type: "boolean", + description: + "Tracks if the user is tracking the price and the item price is being updated.", + example: "true", + }, + pastPrices: { + type: "array", + description: + "Holds a record of past prices. This array is updated each time the item price is updated.", + example: "[{time: 1636729931796, pastPrice: 179}]", + }, + }, + }, + loginInput: { + type: "object", + properties: { + email: { + type: "string", + description: "user email", + example: "john@doe.com", + }, + password: { + type: "string", + description: "user password", + example: "Abc123!@", + }, + rememberMe: { + type: "boolean", + description: "Defines if user is to remain logged in", + example: "true", + }, + }, + }, + createUser: { + type: "object", + properties: { + firstName: { + type: "string", + description: "User's first name.", + example: "John", + }, + lastName: { + type: "string", + description: "User's last name.", + example: "Doe", + }, + email: { + type: "string", + description: "User's email address.", + example: "john@doe.com", + }, + password: { + type: "string", + description: + "Password for user account. password must be at least 6 characters long and contain a lowercase letter, an uppercase letter, a numeric digit and a special character.", + example: "Abc123!@", + }, + password2: { + type: "string", + description: "A repeat of the first password", + example: "Abc123!@", + }, + acceptedTerms: { + type: "boolean", + description: "Confirms the user accepts t's & c's on sign up.", + example: "true", + }, + }, + }, + }, + }, +}; diff --git a/server/docs/index.js b/server/docs/index.js new file mode 100644 index 0000000..9bea68d --- /dev/null +++ b/server/docs/index.js @@ -0,0 +1,13 @@ +const basicInfo = require("./basicInfo"); +const servers = require("./servers"); +const tags = require("./tags"); +const components = require("./components"); +const auth = require("./auth"); + +module.exports = { + ...basicInfo, + ...servers, + ...tags, + ...components, + ...auth, +}; diff --git a/server/docs/servers.js b/server/docs/servers.js new file mode 100644 index 0000000..a88fcd7 --- /dev/null +++ b/server/docs/servers.js @@ -0,0 +1,3 @@ +module.exports = { + servers: [{ url: "http://localhost:5000", description: "Dev Server" }], +}; diff --git a/server/docs/tags.js b/server/docs/tags.js new file mode 100644 index 0000000..eb601f1 --- /dev/null +++ b/server/docs/tags.js @@ -0,0 +1,3 @@ +module.exports = { + tags: [{ name: "Auth CRUD Operations" }, { name: "Scraper CRUD Operations" }], +}; diff --git a/server/package.json b/server/package.json index 460d70b..cbaa919 100644 --- a/server/package.json +++ b/server/package.json @@ -45,6 +45,7 @@ "morgan": "^1.10.0", "node-cron": "^2.0.3", "nodemailer": "^6.6.5", + "swagger-ui-express": "^4.1.6", "uuid": "^8.3.2", "winston": "^3.3.3" } diff --git a/server/server.js b/server/server.js index 42a8da7..4d478ac 100755 --- a/server/server.js +++ b/server/server.js @@ -6,6 +6,8 @@ const cors = require('cors'); const morgan = require('morgan'); const winston = require('./config/winston'); const helmet = require('helmet'); +const swaggerUI = require('swagger-ui-express'); +const docs = require('./docs'); // Models Imports const User = require('./api/models/user.model'); @@ -59,6 +61,9 @@ app.use((req, res, next) => { }); app.use(cors()); +// SwaggerUI Setup +app.use('/api-docs', swaggerUI.serve, swaggerUI.setup(docs)); + // Routes Definitions const authRoutes = require('./api/routes/auth.routes'); const scraperRoutes = require('./api/routes/scraper.routes'); @@ -75,5 +80,7 @@ app.use((req, res) => { // Server Port Controls server.listen(process.env.PORT, () => - console.log(`API running on localhost:${process.env.PORT}`) + console.log( + `API running on localhost:${process.env.PORT} \nAPI docs - http://localhost:${process.env.PORT}/api-docs` + ) ); diff --git a/server/services/scraperService.js b/server/services/scraperService.js index ad753df..1205806 100644 --- a/server/services/scraperService.js +++ b/server/services/scraperService.js @@ -30,7 +30,7 @@ const fetchItemInfo = async (url) => { .replace(/\r?\n|\r/g, "") .replace(/£/g, "") ).toFixed(2); - item.image = $(el).find("#imgTagWrapperId").children("img").attr("src"); + item.imgUrl = $(el).find("#imgTagWrapperId").children("img").attr("src"); item.rating = $(el).find(".a-icon-alt").text(); }); return item;