diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 2e2c715..d2cd278 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -43,7 +43,15 @@ paths: type: integer minimum: 1 maximum: 100 + default: 20 description: Maximum number of items to return (default 20) + - name: offset + in: query + schema: + type: integer + minimum: 0 + default: 0 + description: Number of items to skip before starting to collect the result set (default 0) - name: currency in: query schema: @@ -67,6 +75,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' post: tags: @@ -90,6 +100,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' /sneakers/{id}: get: @@ -114,6 +126,9 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' + put: tags: - sneakers @@ -147,6 +162,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' delete: tags: @@ -166,7 +183,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } - + '500': + $ref: '#/components/responses/InternalServerError' # REVIEWS @@ -195,6 +213,9 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' + post: tags: - reviews @@ -233,6 +254,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' /reviews/{reviewId}: get: @@ -257,6 +280,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' put: tags: @@ -286,6 +311,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' delete: tags: @@ -305,7 +332,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } - + '500': + $ref: '#/components/responses/InternalServerError' # USERS @@ -338,6 +366,8 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + '500': + $ref: '#/components/responses/InternalServerError' /users/{id}: get: @@ -362,6 +392,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' put: tags: @@ -396,6 +428,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' delete: tags: @@ -415,6 +449,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' /users/{id}/favorites: get: @@ -441,6 +477,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' post: tags: @@ -473,6 +511,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' /users/{id}/favorites/{sneakerId}: delete: @@ -498,6 +538,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' # PROVIDERS @@ -512,12 +554,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Provider' + $ref: '#/components/schemas/ProvidersResponse' '404': description: No providers found content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' post: tags: @@ -541,6 +585,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' /providers/{id}: get: @@ -565,6 +611,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' put: tags: @@ -599,6 +647,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' delete: tags: @@ -618,6 +668,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' # STORES @@ -640,6 +692,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' post: tags: @@ -663,6 +717,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' /stores/{id}: get: @@ -687,6 +743,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' put: tags: @@ -721,6 +779,8 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' delete: tags: @@ -740,8 +800,13 @@ paths: content: application/json: schema: { $ref: '#/components/schemas/Error' } + '500': + $ref: '#/components/responses/InternalServerError' +## COMPONENTS components: + + # Schemas schemas: SneakersResponse: @@ -753,6 +818,16 @@ components: $ref: '#/components/schemas/Sneaker' count: type: integer + description: Number of sneakers returned in this response + total: + type: integer + description: Total number of sneakers matching the query (for all pages) + offset: + type: integer + description: Offset used in the query + limit: + type: integer + description: Limit used in the query message: type: string status: @@ -775,6 +850,8 @@ components: type: string modelKey: type: string + size: + type: number retail_price: type: number format: float @@ -919,4 +996,15 @@ components: status: type: string enum: [failure] - required: [message, status] \ No newline at end of file + required: [message, status] + + + # Responses + responses: + + InternalServerError: + description: Internal Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' diff --git a/docs/roadmap.md b/docs/roadmap.md index 3b6c85b..ac4bf51 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -10,7 +10,7 @@ - [x] There are at least 3 resources and they are related to each other (sneakers, users, reviews). - [x] One of the collections has at least **1000 documents** - [x] There is a **dataset** to seed this collection in the repository ( -> this dataset is in **JSON format**) - - [ ] At least one route allows pagination + - [x] At least one route allows pagination - [x] At least one route allows filtering data to search inside this collection - [x] Uses an external API diff --git a/docs/routes.md b/docs/routes.md index 7ff8ed6..ab50666 100644 --- a/docs/routes.md +++ b/docs/routes.md @@ -14,6 +14,10 @@ Query Parameters: - Minimum: 1 - Maximum: 100 - Default: 20 +- `offset`: Number of items to skip before starting to collect the result set (useful for **pagination**) + - Type: integer + - Minimum: 0 + - Default: 0 - `currency`: Currency in which prices should be returned. See [currencies.md](/docs/currencies.md) for the list of **valid** currencies. - Type: string @@ -27,6 +31,9 @@ Examples: # Get 2 sneakers released after October 22, 2021, priced in EUR sneakers?currency=eur&limit=2&release_date_after=2021-10-22 + + # Get 2 sneakers released after October 22, 2021, priced in EUR, skipping the first 5 +/sneakers?currency=eur&limit=2&offset=5&release_date_after=2021-10-22 ``` **GET** /sneakers/{id} : Get a specific sneaker information diff --git a/src/controllers/provider.ts b/src/controllers/provider.ts index bbd3283..068755d 100644 --- a/src/controllers/provider.ts +++ b/src/controllers/provider.ts @@ -2,7 +2,7 @@ import { Provider } from "../schemas/provider"; export const getProviders = async (req, res) => { try { - const providers = await Provider.find(); + const providers = await Provider.find().select('-__v'); if (!providers || providers.length === 0) { return res.status(404).json({ @@ -30,7 +30,9 @@ export const getProviderById = async (req, res) => { const { id } = req.params; try { - const provider = await Provider.findById(id); + const provider = await Provider + .findById(id) + .select('-__v'); if (!provider) { return res.status(404).json({ @@ -85,7 +87,11 @@ export const updateProviderById = async (req, res) => { const updateData = req.body; try { - const provider = await Provider.findByIdAndUpdate(id, updateData, { new: true }); + const provider = await Provider.findByIdAndUpdate( + id, + updateData, + { new: true, select: '-__v'} + ); if (!provider) { return res.status(404).json({ diff --git a/src/controllers/reviews.ts b/src/controllers/reviews.ts index 708eeeb..c7e8fdb 100644 --- a/src/controllers/reviews.ts +++ b/src/controllers/reviews.ts @@ -3,7 +3,10 @@ import { Review } from "../schemas/review"; export const getReviewById = async (req, res) => { try { const { reviewId } = req.params; - const review = await Review.findById(reviewId); + + const review = await Review + .findById(reviewId) + .select('-__v'); if (!review) { return res.status(404).json({ @@ -31,7 +34,7 @@ export const updateReviewById = async (req, res) => { const updatedReview = await Review.findByIdAndUpdate( reviewId, req.body, - { new: true } + { new: true, select: '-__v' } ); if (!updatedReview) { diff --git a/src/controllers/sneakers.ts b/src/controllers/sneakers.ts index 211926d..caf98b4 100644 --- a/src/controllers/sneakers.ts +++ b/src/controllers/sneakers.ts @@ -4,7 +4,7 @@ import { getCurrencyRate } from "./utils/currency"; export const getSneakers = async (req, res) => { try { - const { release_date_after, limit = 20, currency } = req.query; + const { release_date_after, limit = 20, offset = 0, currency } = req.query; let query = {}; @@ -22,13 +22,23 @@ export const getSneakers = async (req, res) => { } // Limit validation - if (limit && (limit < 1 || limit > 100)) { + const parsedLimit = Number(limit); + if (parsedLimit < 1 || parsedLimit > 100) { return res.status(400).json({ message: 'Limit must be between 1 and 100', status: 'failure' }); } + // Offset validation + const parsedOffset = Number(offset); + if (parsedOffset < 0) { + return res.status(400).json({ + message: 'Offset must be 0 or greater', + status: 'failure' + }); + } + let currencyRate = 1; // Currency conversion handling @@ -43,7 +53,12 @@ export const getSneakers = async (req, res) => { } } - const sneakers = await Sneaker.find(query).limit(Number(limit)); + const total = await Sneaker.countDocuments(query); + + const sneakers = await Sneaker.find(query) + .skip(parsedOffset) + .limit(parsedLimit) + .select('-__v'); if (!sneakers || sneakers.length === 0) { return res.status(404).json({ @@ -61,6 +76,9 @@ export const getSneakers = async (req, res) => { return res.status(200).json({ items: convertedSneakers, count: convertedSneakers.length, + total, + offset: parsedOffset, + limit: parsedLimit, message: 'Sneakers data fetched successfully', status: 'success' }); @@ -78,7 +96,9 @@ export const getSneakerById = async (req, res) => { try { const { sneakerId } = req.params; - const sneaker = await Sneaker.findOne({ _id: sneakerId }); + const sneaker = await Sneaker + .findOne({ _id: sneakerId }) + .select('-__v'); if (!sneaker) { return res @@ -138,7 +158,7 @@ export const updateSneakerById = async (req, res) => { const sneaker = await Sneaker.findByIdAndUpdate( sneakerId, updateData, - { new: true } + { new: true, select: '-__v' } ); if (!sneaker) { diff --git a/src/controllers/stores.ts b/src/controllers/stores.ts index 1732bfe..4b11e9e 100644 --- a/src/controllers/stores.ts +++ b/src/controllers/stores.ts @@ -97,7 +97,7 @@ export const updateStoreById = async (req, res) => { const updatedStore = await Store.findOneAndUpdate( { _id: storeId }, updateData, - { new: true } + { new: true, select: '-__v' } ); if (!updatedStore) { @@ -134,7 +134,7 @@ export const deleteStoreById = async (req, res) => { }); } - return res.status(204); + return res.status(204).send(); } catch (error) { return res.status(500).json({ diff --git a/src/controllers/users.ts b/src/controllers/users.ts index aa7a696..96c69a2 100644 --- a/src/controllers/users.ts +++ b/src/controllers/users.ts @@ -6,7 +6,7 @@ export const getUserById = async (req, res) => { try { const { id } = req.params; - const user = await User.findById(id); + const user = await User.findById(id).select('-__v');; if (!user) { return res.status(404).json({ @@ -78,7 +78,11 @@ export const updateUser = async (req, res) => { }); } - const updatedUser = await User.findByIdAndUpdate(id, updateData, { new: true }); + const updatedUser = await User.findByIdAndUpdate( + id, + updateData, + { new: true, select: '-__v' } + ); if (!updatedUser) { return res.status(404).json({